jni下使用ffmpeg

系统,ubuntu14.04LTS,32bit(x86),android-ndk-r10d,ffmpeg-2.6.1.tar.bz2源码包

1.首先去官网上下载上述ffmpeg源码,http://ffmpeg.org/download.html

2.切换到ffmpeg解压后的目录下,执行如下配置检查命令:

在我的电脑上没有什么问题,如果有问题请自行google或是stackoverflow,要不问问我也行,[email protected]

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

如果还有其他问题,请私聊:

[email protected]

时间: 2024-07-28 20:04:36

jni下使用ffmpeg的相关文章

Linux下编译ffmpeg

Linux下编译ffmpeg太简单了. 1.下载yasm 1-1. 下载地址:http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz 1-2. 解压缩: tar zxvf yasm-1.3.0.tar.gz 1-3. cd yasm-1.3.0进入 1-4. 命令行执行./configure 检测环境并生成Makefile文件 1-5. 执行make编译,sudo make install安装 2. 下载ffmpeg 2-1

在Windows下编译ffmpeg完全手册

本文的内容几乎全部来自于FFmpeg on Windows,但是由于国内的网络封锁,很难访问这个域名下的内容,因此我一方面按照我自己的理解和实践做了翻译,另一方面也是为了能提供一个方便的参考方法. 注: 1.       对于compile和build这两个词,本文统一的使用编译这个词,根据上下文读者应能区分语义上的差别. 本文的目的是提供一切与FFmpeg在Windows下的相关帮助.它最初是一个针对用MSys+MinGW系统编译的手把手的教程,现在也增加了如何在你的项目中链接FFmpeg所产

在Windows下编译FFmpeg详细说明

MinGW:一个可自由使用和自由发布的Windows特定头文件和使用GNC工具集导入库的集合,允许你生成本地的Windows程序而不需要第三方C运行时 MinGW,即 Minimalist GNU For Windows.它是一些头文件和端口库的集合,该集合允许人们在没有第三方动态链接库的情况下使用 GCC产生 Windows32 程序. 开发 MinGW 是为了那些不喜欢工作在 Linux(FreeBSD) 操作系统而留在 Windows 的人提供一套符合 GNU 的 GNU 工作环境. 所以

【转】Linux下编译ffmpeg

1.下载ffmpeg.下载网址:http://www.ffmpeg.org/download.html 2.解压缩tar -zxvf ffmpeg-2.0.1.tar.gz 3.配置,生成Makefile./configure --enable-shared --disable-yasm --prefix=/usr/local/ffmpeg如果执行结果不对,可以根据提示信息,并查看帮助,解决问题./configure --help 4.编译安装makemake install 5.安装之后在/u

windows环境下搭建ffmpeg开发环境

ffmpeg是一个开源.跨平台的程序库,可以使用在windows.linux等平台下,本文将简单讲解windows环境下ffmpeg开发环境搭建过程,本人使用的操作系统为windows 7,集成开发环境为Visual Studio 2005,ffmpeg版本为2.2.有人可能会说都什么年代了,还VS 2005,现在VS 2010/2012/2013都出了.本人电脑也安装了VS2010,每次打开,伴随着硬盘指示灯的闪烁,以及硬盘的吱吱响声,过了许久才弹出闪屏页面,此时你的思绪可能已经飘到了南极,启

win7_32下编译FFmpeg

运行环境:  VC2010软件:                 [附:本文所用软件安装包:http://download.csdn.NET/detail/sinat_36666600/9705438] 1.下载mingw-get-setup.exe [网站:https://sourceforge.net/projects/mingw/] 安装步骤很简单,一直next就行.然后把basic setup中的 “1”点右键选择“make for installation”全部选择就行.最后点击“2”

Ubuntu下安装ffmpeg

Ubuntu下安装ffmpeg可以通过以下命令安装: >sudo apt-get install libav-tools

ios 下使用ffmpeg随记

ffmpeg是一个多平台多媒体处理工具,处理视频和音频的功能非常强大.目前在网上搜到的iOS上使用FFMPEG的资料都比较陈旧,而FFMPEG更新迭代比较快: 且网上的讲解不够详细,对于初次接触FFMPEG的新手(例如我)来说确实不太好使用.为了防止忘记,这里对iOS下使用FFMPEG做一个总结. 1. FFMPEG层次结构的简单理解 要使用FFMPEG,首先需要理解FFMPEG的代码结构.根据志哥的提示,ffmpeg的代码是包括两部分的,一部分是library,一部分是 tool.api都是在

win下编译ffmpeg库,Compile and build ffmpeg library and dll on Windows x64( 正版)

转载请注明:来自EricKing,thanks 从没想到编一个library这么坑爹,再次提醒各位百度的东西只能参考,想节约时间还是要到官网上去查看docum.不废话了,开始详细过程: ——>1.搭建Win下的GCC编译环境(因为win下vs不支持ffmpeg的compile 和build,官网上也有说这一点) ——>2.下载latest ffmpeg source(后面附官网地址),想办法将编译后的文件做成dll,这是win下编程调试的核心 (这里就用到vs下的一个vc的bash文件叫vcv