Android:WIN10+MinGW+NDK编译FFmpeg

来源:互联网 发布:环信 服务端开发 java 编辑:程序博客网 时间:2024/05/16 13:41

前言:

Android音视频开发的话,FFmpeg总是必须的,出于学习目的,花时间起了个开头。

FFmpeg源码是很多的c文件,首先要借助工具编译出so库,仅有这个Android是无法调用的,必须再用JNI写一层Android能用的接口。网上资料非常多,但是看了几天下来,编译平台和工具版本的差异总是存在,编译过程中也有各种各样的问题。这里给出自己的编译过程,希望给刚好需要的同学。

到写文章为止,所有源码和工具都是用的最新版。


一.MinGW编译出so库

http://ffmpeg.org/下载源码,解压到一个文件夹里



https://sourceforge.net/projects/mingw/files/下载MinGW,把相应的选项都勾上,环境变量配置好。


找到ffmpeg目录下的configure

打开并找到以下几行

SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'  LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'  SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'

替换为

SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'SLIB_INSTALL_LINKS='$(SLIBNAME)'

在ffmpeg下新建一个build_android.sh,将以下代码复制进去

1)前三行按实际情况替换

2)这里我用notepad++编辑将文件格式改为Unix格式,否则会有一些换行符等Windows与Linux平台差异问题编译出问题

#!/bin/shNDK=D:/Program/android-ndk-r14bSYSROOT=$NDK/platforms/Android-21/arch-armTOOLCHAIN=$NDK/toolchains/arm-Linux-androideabi-4.9/prebuilt/windows-x86_64function build_one{./configure \--prefix=$PREFIX \--enable-shared \--enable-gpl \--disable-static \--disable-doc \--disable-ffmpeg \--disable-ffplay \--disable-ffprobe \--disable-ffserver \--disable-doc \--disable-symver \--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \--target-os=linux \--arch=arm \--enable-cross-compile \--sysroot=$SYSROOT \--extra-cflags="-Os -fpic $ADDI_CFLAGS" \--extra-ldflags="$ADDI_LDFLAGS" \$ADDITIONAL_CONFIGURE_FLAGmake cleanmakemake install}CPU=armPREFIX=$(pwd)/libs/$CPUADDI_CFLAGS="-marm"build_one

之后打开下载好的MinGW的  MinGW\msys\1.0\msys.bat,执行以下命令



耐心等待后,ffmpeg下多出一个libs文件夹,里面就是我们想要的东西了



二.NDK编译JNI

还是在ffmpeg目录下,新建一个jni目录,里面是这些东西



其中Android.mk

APP_ALLOW_MISSING_DEPS=trueLOCAL_PATH := $(call my-dir)# FFmpeg libraryinclude $(CLEAR_VARS)LOCAL_MODULE := avcodecLOCAL_SRC_FILES := libavcodec-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avfilterLOCAL_SRC_FILES := libavfilter-6.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avformatLOCAL_SRC_FILES := libavformat-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avutilLOCAL_SRC_FILES := libavutil-55.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := swscaleLOCAL_SRC_FILES := libswscale-4.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := swresampleLOCAL_SRC_FILES := libswresample-2.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avdeviceLOCAL_SRC_FILES := libavdevice-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := postprocLOCAL_SRC_FILES := libpostproc-54.soinclude $(PREBUILT_SHARED_LIBRARY)# Programinclude $(CLEAR_VARS)LOCAL_MODULE := jniloaderLOCAL_SRC_FILES := jniloader.cLOCAL_C_INCLUDES += $(LOCAL_PATH)/includeLOCAL_LDLIBS := -llog -lzLOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil postproc swresample swscaleinclude $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI :=armeabi

jniloader.c

#include <stdio.h>#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libavfilter/avfilter.h"  #ifdef ANDROID#include <jni.h>#include <android/log.h>#endifstruct URLProtocol;  JNIEXPORT jstring Java_com_example_ffmpeg_JNILoader_urlprotocolinfo(JNIEnv *env, jobject obj){            char info[40000]={0};      av_register_all();        struct URLProtocol *pup = NULL;      //Input      struct URLProtocol **p_temp = &pup;      avio_enum_protocols((void **)p_temp, 0);      while ((*p_temp) != NULL){          sprintf(info, "%s[In ][%10s]\n", info, avio_enum_protocols((void **)p_temp, 0));      }      pup = NULL;      //Output      avio_enum_protocols((void **)p_temp, 1);      while ((*p_temp) != NULL){          sprintf(info, "%s[Out][%10s]\n", info, avio_enum_protocols((void **)p_temp, 1));      }        //LOGE("%s", info);      return (*env)->NewStringUTF(env, info);  }JNIEXPORT jstring Java_com_example_ffmpeg_JNILoader_avformatinfo(JNIEnv *env, jobject obj){        char info[40000] = { 0 };        av_register_all();        AVInputFormat *if_temp = av_iformat_next(NULL);      AVOutputFormat *of_temp = av_oformat_next(NULL);      //Input      while(if_temp!=NULL){          sprintf(info, "%s[In ][%10s]\n", info, if_temp->name);          if_temp=if_temp->next;      }      //Output      while (of_temp != NULL){          sprintf(info, "%s[Out][%10s]\n", info, of_temp->name);          of_temp = of_temp->next;      }      //LOGE("%s", info);      return (*env)->NewStringUTF(env, info);  } JNIEXPORT jstring Java_com_example_ffmpeg_JNILoader_avcodecinfo(JNIEnv *env, jobject obj)  {      char info[40000] = { 0 };        av_register_all();        AVCodec *c_temp = av_codec_next(NULL);        while(c_temp!=NULL){          if (c_temp->decode!=NULL){              sprintf(info, "%s[Dec]", info);          }          else{              sprintf(info, "%s[Enc]", info);          }          switch (c_temp->type){          case AVMEDIA_TYPE_VIDEO:              sprintf(info, "%s[Video]", info);              break;          case AVMEDIA_TYPE_AUDIO:              sprintf(info, "%s[Audio]", info);              break;          default:              sprintf(info, "%s[Other]", info);              break;          }          sprintf(info, "%s[%10s]\n", info, c_temp->name);                      c_temp=c_temp->next;      }      //LOGE("%s", info);        return (*env)->NewStringUTF(env, info);  }     JNIEXPORT jstring Java_com_example_ffmpeg_JNILoader_avfilterinfo(JNIEnv *env, jobject obj)  {      char info[40000] = { 0 };      avfilter_register_all();      AVFilter *f_temp = (AVFilter *)avfilter_next(NULL);      while (f_temp != NULL){          sprintf(info, "%s[%10s]\n", info, f_temp->name);  f_temp = f_temp->next;     }      //LOGE("%s", info);        return (*env)->NewStringUTF(env, info);  } JNIEXPORT jstring Java_com_example_ffmpeg_JNILoader_configurationinfo(JNIEnv *env, jobject obj)  {      char info[10000] = { 0 };      av_register_all();        sprintf(info, "%s\n", avcodec_configuration());        //LOGE("%s", info);      return (*env)->NewStringUTF(env, info);  }   

然后就用NDK编译即可,这就是我们最终要的了。



三.Java层调用接口

把JNI编译出来的SO库放在jniLibs下



声明相关的c层函数并加载so库



最后调用即可,这些都只是简单的用ffmpeg提供的函数输出相关的配置信息,还有很多强大的音视频相关的功能要慢慢探索了。