标签:
系统,ubuntu14.04LTS,32bit(x86),android-ndk-r10d,ffmpeg-2.6.1.tar.bz2源码包
1.首先去官网上下载上述ffmpeg源码,http://ffmpeg.org/download.html
2.切换到ffmpeg解压后的目录下,执行如下配置检查命令:
在我的电脑上没有什么问题,如果有问题请自行google或是stackoverflow,要不问问我也行,mylhyz@sina.com
3.为了支持Android的JNI拓展,一般第三方包都是编译成动态链接库.so或是静态库.a然后在构建的时候被包含,为了复用性我们一般使用的是.a动态库,我这里使用的是编译的静态库,在当前源码目录下建立一个build_android.sh的可执行bash脚本,所有代码如下:
其中,NDK目录还有其他的目录一般都要根据你自己的实际情况配置,上述脚本我在查资料的时候都是大同小异,所以如果你有好的配置脚本请自行尝试(但是要提醒一句,编译过程比较漫长),要理解./configure的那些参数的意义,可以使用./configure --help命令查看所有支持的参数和默认值([NO]),
要支持Android平台只需要很少的东西,至少可执行的工具选项可以被disable,架构要选arm,我选用的是静态库,因此enable-static,其他的按照help逐个理解即可,其他额外编解码工具应该也可以自行设置,我暂时没有设置,也不会设置。
最后在build_one函数执行之前设置的PREFIX就是编译完成后生成的库文件所在的位置。
4.执行上述构建脚本,不要管他是不是有警告,只要最后成功编译完成即可(因为肯定有warning提示),最后成功之后生成的东西在这里找到
..屏幕不够大,只能截出这么多
共计 9 directories, 102 files。
5.将上述ffmpeg目录拷贝到一个用来配置NDK的目录下
,在ext下就是ffmpeg目录:
这时候我们来建立上述Android.mk文件,内容如下:
1 LOCAL_PATH:= $(call my-dir) 2 3 #static version of libavcodec 4 include $(CLEAR_VARS) 5 LOCAL_MODULE:= libavcodec_static 6 LOCAL_SRC_FILES:= lib/libavcodec.a 7 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 8 include $(PREBUILT_STATIC_LIBRARY) 9 10 #static version of libavformat 11 include $(CLEAR_VARS) 12 LOCAL_MODULE:= libavformat_static 13 LOCAL_SRC_FILES:= lib/libavformat.a 14 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 15 include $(PREBUILT_STATIC_LIBRARY) 16 17 #static version of libswscale 18 include $(CLEAR_VARS) 19 LOCAL_MODULE:= libswscale_static 20 LOCAL_SRC_FILES:= lib/libswscale.a 21 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 22 include $(PREBUILT_STATIC_LIBRARY) 23 24 #static version of libavutil 25 include $(CLEAR_VARS) 26 LOCAL_MODULE:= libavutil_static 27 LOCAL_SRC_FILES:= lib/libavutil.a 28 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 29 include $(PREBUILT_STATIC_LIBRARY) 30 31 #static version of libavfilter 32 include $(CLEAR_VARS) 33 LOCAL_MODULE:= libavfilter_static 34 LOCAL_SRC_FILES:= lib/libavfilter.a 35 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 36 include $(PREBUILT_STATIC_LIBRARY) 37 38 #static version of libavswresample 39 include $(CLEAR_VARS) 40 LOCAL_MODULE:= libswresample_static 41 LOCAL_SRC_FILES:= lib/libswresample.a 42 LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 43 include $(PREBUILT_STATIC_LIBRARY)
这个将来是要被预编译的链接库,不要单独编译这个makefile检来测是否有问题。
6.现在我们需要来到最难的问题,编辑JNI模块并集成到Android应用中。首先建立一个Android项目,主Activity的代码如下:
文件路径是一个绝对路径,文件是一个.mp4文件。编译出来的库模块我命名为ffmpeg。另外要在权限上声明:
1 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 2 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 3 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
然后通过javah按照上述Activity的java文件生成c头文件
然后将头文件转移到刚才包含ext/ffmpeg 的 NDK目录下的jni中:
,
可以先编写infomsg这个源文件作为将来生成模块的源文件:所有代码如下,包含了那个生成的头文件:
1 #include <jni.h> 2 #include <android/log.h> 3 #include <stdio.h> 4 5 #include <libavcodec/avcodec.h> 6 #include <libavformat/avformat.h> 7 #include <libavformat/avio.h> 8 #include <libavutil/fifo.h> 9 #include <libavutil/avutil.h> 10 #include <libavutil/mem.h> 11 #include <libswscale/swscale.h> 12 13 #include "com_example_jnidemo_MainActivity.h" 14 15 #define LOG_TAG "FFMPEG INFO" 16 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 17 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 18 19 20 void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { 21 FILE* pFile; 22 char szFileName[32]; 23 int y; 24 25 sprintf(szFileName,"/mnt/sdcard/frame%d.ppm",iFrame); 26 27 LOGI("filename : %s",szFileName); 28 29 pFile = fopen(szFileName,"w"); 30 if(pFile == NULL){ 31 LOGI("can not open file %s",szFileName); 32 return ; 33 } 34 35 fprintf(pFile,"P6\n%d %d\n255\n",width,height); 36 37 for(y=0;y<width;++y){ 38 LOGI("Write file AVFrame"); 39 fwrite(pFrame->data[0]+y*pFrame->linesize[0],1,width*3,pFile); 40 } 41 42 fclose(pFile); 43 LOGI("close file %s",szFileName); 44 } 45 46 JNIEXPORT void JNICALL Java_com_example_jnidemo_MainActivity_info(JNIEnv *env, jobject obj, jstring jpath){ 47 const jbyte* path = (*env)->GetStringUTFChars(env,jpath,NULL); 48 AVFormatContext *pFormatCtx = NULL; 49 int i, videoStream; 50 AVCodecContext *pCodecCtxOrig = NULL; 51 AVCodecContext *pCodecCtx = NULL; 52 AVCodec *pCodec = NULL; 53 AVFrame *pFrame = NULL; 54 AVFrame *pFrameRGB = NULL; 55 AVPacket packet; 56 int frameFinished; 57 int numBytes; 58 uint8_t *buffer = NULL; 59 struct SwsContext *sws_ctx = NULL; 60 61 av_register_all(); 62 63 if(avformat_open_input(&pFormatCtx, path, NULL, NULL)!=0) 64 { 65 LOGE("Could not open the file : %s",path); 66 return ; 67 } 68 69 if(avformat_find_stream_info(pFormatCtx, NULL)<0) 70 return ; 71 72 av_dump_format(pFormatCtx, 0, path, 0); 73 74 videoStream=-1; 75 for(i=0; i<pFormatCtx->nb_streams; i++) 76 if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) 77 { 78 videoStream=i; 79 break; 80 } 81 82 if(videoStream==-1) 83 return ; 84 85 pCodecCtxOrig=pFormatCtx->streams[videoStream]->codec; 86 87 pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id); 88 if(pCodec==NULL) 89 { 90 LOGE("Unsupported codec!\n"); 91 return ; 92 } 93 94 pCodecCtx = avcodec_alloc_context3(pCodec); 95 if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) 96 { 97 LOGE("Couldn‘t copy codec context"); 98 return ; 99 } 100 101 if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) 102 return ; 103 104 pFrame=av_frame_alloc(); 105 pFrameRGB=av_frame_alloc(); 106 if(pFrameRGB==NULL) 107 return ; 108 109 numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height); 110 buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); 111 112 avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height); 113 114 sws_ctx = sws_getContext(pCodecCtx->width, 115 pCodecCtx->height, 116 pCodecCtx->pix_fmt, 117 pCodecCtx->width, 118 pCodecCtx->height, 119 PIX_FMT_RGB24, 120 SWS_BILINEAR, 121 NULL, 122 NULL, 123 NULL 124 ); 125 126 i=0; 127 while(av_read_frame(pFormatCtx, &packet)>=0) { 128 if(packet.stream_index==videoStream) { 129 130 avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); 131 132 if(frameFinished) { 133 134 sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, 135 pFrame->linesize, 0, pCodecCtx->height, 136 pFrameRGB->data, pFrameRGB->linesize); 137 138 if(++i<=5) 139 SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i); 140 } 141 } 142 av_free_packet(&packet); 143 } 144 av_free(buffer); 145 av_frame_free(&pFrameRGB); 146 av_frame_free(&pFrame); 147 avcodec_close(pCodecCtx); 148 avcodec_close(pCodecCtxOrig); 149 avformat_close_input(&pFormatCtx); 150 (*env)->ReleaseStringUTFChars(env,jpath,path); 151 }
具体这个文件的原始来源是:http://dranger.com/ffmpeg/tutorial01.html,由于为了支持到Android原生代码,我做了一些简单修改。
编辑完成之后我们先编辑Application.mk文件来指定平台:
这是因为其他平台都不能使用我用在x86工具链生成的静态库,如果强制使用(就是指定了arm64或是mips/x86_64),会出现一些类似符号引用或是不支持的.a库格式(稍后我会添加我遇到的错误的解决方法),如果需要支持其他平台可能需要更改ffmpeg源代码编译脚本上的工具链等地方。
然后我们来编辑Android.mk文件生成链接库:
这样我们只需要在NDK目录下依次执行如下命令:
原因是NDK_MODULE_PATH是上述代码$(call import-module,ext/ffmpeg)中第二个参数的相对路径,相对的路径就是NDK_MODULE_PATH的目录。
到此为止我成功在Android下编译运行了上述应用,生成的framex.ppm文件如下(我copy到了电脑上):
上述小demo的github地址:https://github.com/wylhyz/JNIDemo-FFMPEG-config,包含了上述所有的文件,连带有编译好的ffmpeg下的动态库和include头文件。
由于Github空间有限,NDK部分已上传到百度网盘:http://pan.baidu.com/s/1gdKnePP
如果还有其他问题,请私聊:
mylhyz@sina.com
标签:
原文地址:http://www.cnblogs.com/lhyz/p/4395579.html