FFmpeg 编译的so和include在android studio中的使用
程序员文章站
2022-03-21 08:04:32
...
FFmpeg 编译的so和include在android studio中的使用
上一篇博客讲了如何编译ffmpeg,产生适合在android上调用的so库文件和include头文件,下面讲讲如何在android studio中使用这些库文件来进行ndk开发
1、新建android 项目**,
注意勾选支持c/c++,然后将编译好的文件拷贝到libs目录下:
2、拷贝带版本号的库文件到libs/armeabi目录**(没有就新建)
3、 gradle将jni目录设置到libs****
// 如果将so文件放在libs文件夹下得加上下面几句
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
4.gradle设置 cpp文件目录 ,设置cpu编译库**
externalNativeBuild {
cmake {
cppFlags ""
}
ndk{
abiFilters "armeabi"
}
}
这里给出完整的gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.example.lammy.ffmpegdemo"
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
ndk{
abiFilters "armeabi"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// 如果将so文件放在libs文件夹下得加上下面几句
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
5、配置CMakeLists.txt文件**
- 1、将libs/include导入
- 2、将so各个库添加进来(add_library 和 set_target_properties)
这里直接贴出来:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
include_directories(libs/include)
#设置so的路径
set(ffmpeg_DIR ${CMAKE_SOURCE_DIR}/libs)
add_library( avcodec-57
SHARED
IMPORTED)
set_target_properties( avcodec-57
PROPERTIES IMPORTED_LOCATION
${ffmpeg_DIR}/armeabi/libavcodec-57.so)
add_library( avfilter-6
SHARED
IMPORTED)
set_target_properties( avfilter-6
PROPERTIES IMPORTED_LOCATION
${ffmpeg_DIR}/armeabi/libavfilter-6.so)
add_library( avformat-57
SHARED
IMPORTED)
set_target_properties( avformat-57
PROPERTIES IMPORTED_LOCATION
${ffmpeg_DIR}/armeabi/libavformat-57.so)
add_library( avutil-55
SHARED
IMPORTED)
set_target_properties( avutil-55
PROPERTIES IMPORTED_LOCATION
${ffmpeg_DIR}/armeabi/libavutil-55.so)
add_library( swresample-2
SHARED
IMPORTED)
set_target_properties( swresample-2
PROPERTIES IMPORTED_LOCATION
${ffmpeg_DIR}/armeabi/libswresample-2.so)
add_library( swscale-4
SHARED
IMPORTED)
set_target_properties( swscale-4
PROPERTIES IMPORTED_LOCATION
${ffmpeg_DIR}/armeabi/libswscale-4.so)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
avcodec-57
avfilter-6
avformat-57
avutil-55
swresample-2
swscale-4
# Links the target library to the log library
# included in the NDK.
${log-lib} )
6、编写jni文件和java文件
public class MainActivity extends AppCompatActivity {
private int permissionRequestCode = 1;
private String inputFilePath = new File(Environment.getExternalStorageDirectory()+
"/a-lammytest/","test.mp4").getAbsolutePath();
private String outputFilePath = new File(Environment.getExternalStorageDirectory()+
"/a-lammytest/","output_yuv420p.yuv").getAbsolutePath();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermissions();
}
private String permissions[] = new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
};
private void requestPermissions(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
for(int i =0; i < permissions.length ; i ++) {
if( checkSelfPermission(permissions[0]) == PackageManager.PERMISSION_DENIED){
requestPermissions(permissions ,permissionRequestCode );
return;
// Toast.makeText(this , "请打开"+permissions[0]+"权限",Toast.LENGTH_LONG).show();
}
}
decode(inputFilePath, outputFilePath);
}else{
decode(inputFilePath, outputFilePath);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if(requestCode ==permissionRequestCode ){
for(int result:grantResults){
if(result == PackageManager.PERMISSION_DENIED){
return;
}
}
decode(inputFilePath, outputFilePath);
}
}
}
public class FFmpegUtil {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
System.loadLibrary("avcodec-57");
System.loadLibrary("avfilter-6");
System.loadLibrary("avformat-57");
System.loadLibrary("avutil-55");
System.loadLibrary("swresample-2");
System.loadLibrary("swscale-4");
}
public native static void decode(String input,String output);
}
jni中解码demo参考:https://www.cnblogs.com/CoderTian/p/6651343.html
extern "C" {
//编码
#include "libavcodec/avcodec.h"
//封装格式处理
#include "libavformat/avformat.h"
//像素处理
#include "libswscale/swscale.h"
}
#define FFLOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"ffmpeg",FORMAT,##__VA_ARGS__);
#define FFLOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"ffmpeg",FORMAT,##__VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_lammy_ffmpegdemo_FFmpegUtil_decode(JNIEnv *env, jclass type, jstring input_,
jstring output_) {
const char *input = env->GetStringUTFChars(input_, 0);
const char *output = env->GetStringUTFChars(output_, 0);
// TODO
//1.注册所有组件
av_register_all();
//封装格式上下文,统领全局的结构体,保存了视频文件封装格式的相关信息
AVFormatContext *pFormatCtx = avformat_alloc_context();
//2.打开输入视频文件
if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0)
{
FFLOGE("%s","无法打开输入视频文件");
return;
}
//3.获取视频文件信息
if (avformat_find_stream_info(pFormatCtx,NULL) < 0)
{
FFLOGE("%s","无法获取视频文件信息");
return;
}
//获取视频流的索引位置
//遍历所有类型的流(音频流、视频流、字幕流),找到视频流
int v_stream_idx = -1;
int i = 0;
//number of streams
for (; i < pFormatCtx->nb_streams; i++)
{
//流的类型
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
v_stream_idx = i;
break;
}
}
if (v_stream_idx == -1)
{
FFLOGE("%s","找不到视频流\n");
return;
}
//只有知道视频的编码方式,才能够根据编码方式去找到解码器
//获取视频流中的编解码上下文
AVCodecContext *pCodecCtx = pFormatCtx->streams[v_stream_idx]->codec;
//4.根据编解码上下文中的编码id查找对应的解码
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL)
{
FFLOGE("%s","找不到解码器\n");
return;
}
//5.打开解码器
if (avcodec_open2(pCodecCtx,pCodec,NULL)<0)
{
FFLOGE("%s","解码器无法打开\n");
return;
}
//输出视频信息
FFLOGI("视频的文件格式:%s",pFormatCtx->iformat->name);
FFLOGI("视频时长:%d", (pFormatCtx->duration)/1000000);
FFLOGI("视频的宽高:%d,%d",pCodecCtx->width,pCodecCtx->height);
FFLOGI("解码器的名称:%s",pCodec->name);
//准备读取
//AVPacket用于存储一帧一帧的压缩数据(H264)
//缓冲区,开辟空间
AVPacket *packet = (AVPacket*)av_malloc(sizeof(AVPacket));
//AVFrame用于存储解码后的像素数据(YUV)
//内存分配
AVFrame *pFrame = av_frame_alloc();
//YUV420
AVFrame *pFrameYUV = av_frame_alloc();
//只有指定了AVFrame的像素格式、画面大小才能真正分配内存
//缓冲区分配内存
uint8_t *out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
pCodecCtx->height));
//初始化缓冲区
avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width,
pCodecCtx->height);
//用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等
struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P,
SWS_BICUBIC, NULL, NULL, NULL);
int got_picture, ret;
FILE *fp_yuv = fopen(output, "wb+");
int frame_count = 0;
//6.一帧一帧的读取压缩数据
while (av_read_frame(pFormatCtx, packet) >= 0)
{
//只要视频压缩数据(根据流的索引位置判断)
if (packet->stream_index == v_stream_idx)
{
//7.解码一帧视频压缩数据,得到视频像素数据
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if (ret < 0)
{
FFLOGE("%s","解码错误");
return;
}
//为0说明解码完成,非0正在解码
if (got_picture)
{
//AVFrame转为像素格式YUV420,宽高
//2 6输入、输出数据
//3 7输入、输出画面一行的数据的大小 AVFrame 转换是一行一行转换的
//4 输入数据第一列要转码的位置 从0开始
//5 输入画面的高度
sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
//输出到YUV文件
//AVFrame像素帧写入文件
//data解码后的图像像素数据(音频采样数据)
//Y 亮度 UV 色度(压缩了) 人对亮度更加敏感
//U V 个数是Y的1/4
int y_size = pCodecCtx->width * pCodecCtx->height;
fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);
fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);
fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);
frame_count++;
FFLOGI("解码第%d帧",frame_count);
}
}
//释放资源
av_free_packet(packet);
}
fclose(fp_yuv);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_free_context(pFormatCtx);
env->ReleaseStringUTFChars(input_, input);
env->ReleaseStringUTFChars(output_, output);
}
最后注意添加内存卡的访问权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
运行的结果是将mp4格式的视频 解码后编码为 YUV420格式的视频,yuv播发器打开,并且设置号视频参数
1、yuv播发器:YUV Player Deluxe打开:
2、设置参数为:
代码:这里给出完整代码,欢迎各位老铁去下载。
推荐阅读
-
在Android Studio中Parcelable插件的简单使用教程
-
ffmpeg.so在Android中的使用
-
Android FFmpeg源码编译及在Android studio的集成
-
Eclipse和Android Studio中的DDMS使用时什么不同?
-
10_Android中通过HttpUrlConnection访问网络,Handler和多线程使用,读取网络html代码并显示在界面上,ScrollView组件的使用_html/css_WEB-ITnose
-
在Android Studio中Parcelable插件的简单使用教程
-
FFmpeg 编译的so和include在android studio中的使用
-
Eclipse和Android Studio中的DDMS使用时什么不同?