1. 程式人生 > >android全平臺編譯ffmpeg支援命令列實踐

android全平臺編譯ffmpeg支援命令列實踐

本例基於 android全平臺編譯ffmpeg合併為單個庫實踐 進行的

目錄

環境準備

作業系統 ubuntu 16.05

編譯準備好libffmpeg庫和需要include標頭檔案,我們還需要抽取ffmpeg-3.3.8中的部分標頭檔案和原始檔

筆者整理了一個copy_ffmpeg_files.sh指令碼


FFMPEG_DIR=ffmpeg-3.3.8

mkdir include
mkdir include/compat
mkdir include/libavcodec
mkdir include/
libavformat mkdir include/libavutil mkdir include/libavdevice mkdir include/libpostproc cp $FFMPEG_DIR/compat/va_copy.h include/compat/va_copy.h cp $FFMPEG_DIR/libavcodec/mathops.h include/libavcodec/mathops.h cp $FFMPEG_DIR/libavdevice/avdevice.h include/libavdevice/avdevice.h cp $FFMPEG_DIR/libavdevice/version.
h include/libavdevice/version.h cp $FFMPEG_DIR/libavformat/ffm.h include/libavformat/ffm.h cp $FFMPEG_DIR/libavformat/network.h include/libavformat/network.h cp $FFMPEG_DIR/libavformat/os_support.h include/libavformat/os_support.h cp $FFMPEG_DIR/libavformat/url.h include/libavformat/url.h cp $FFMPEG_DIR/libavutil/libm.
h include/libavutil/libm.h cp $FFMPEG_DIR/libavutil/internal.h include/libavutil/internal.h cp $FFMPEG_DIR/libavutil/reverse.h include/libavutil/reverse.h cp $FFMPEG_DIR/libavutil/timer.h include/libavutil/timer.h cp $FFMPEG_DIR/libpostproc/postprocess.h include/libpostproc/postprocess.h cp $FFMPEG_DIR/libpostproc/version.h include/libpostproc/version.h #cpp mkdir cpp cp $FFMPEG_DIR/cmdutils.c cpp/cmdutils.c cp $FFMPEG_DIR/cmdutils.h cpp/cmdutils.h cp $FFMPEG_DIR/cmdutils_common_opts.h cpp/cmdutils_common_opts.h # cp $FFMPEG_DIR/config.h cpp/config.h cp $FFMPEG_DIR/ffmpeg.h cpp/ffmpeg.h cp $FFMPEG_DIR/ffmpeg.c cpp/ffmpeg_mod.c cp $FFMPEG_DIR/ffmpeg_filter.c cpp/ffmpeg_filter.c cp $FFMPEG_DIR/ffmpeg_opt.c cpp/ffmpeg_opt.c
  • 檔案目錄結構如下:

將生成之後的include資料夾覆蓋到專案中的include目錄
本例中即覆蓋到ffmpeg-single元件下的libs/armeabi-v7ainclude目錄

新建ffmpeg-single-cmd工程

將生成的cpp目錄複製到src/main目錄下

  • 新建FFmpegCmd類
public class FFmpegCmd {

    static {
        System.loadLibrary("ffmpeg");
        System.loadLibrary("ffmpeg-cmd");
    }

    public native int exec(int argc, String[] argv);

}
  • 新建ffmpeg_cmd.c檔案

#include <jni.h>
#include <stdio.h>
#include <time.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavutil/log.h"

#ifdef ANDROID

#include <android/log.h>

#define LOG_TAG    "HelloFFmpeg"
#define LOGE(format, ...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, format, ##__VA_ARGS__)
#define LOGI(format, ...)  __android_log_print(ANDROID_LOG_INFO,  LOG_TAG, format, ##__VA_ARGS__)
#else
#define LOGE(format, ...)  printf(LOG_TAG format "\n", ##__VA_ARGS__)
#define LOGI(format, ...)  printf(LOG_TAG format "\n", ##__VA_ARGS__)
#endif

int exec(int argc, char **argv);

void custom_log(void *ptr, int level, const char *fmt, va_list vl) {
    FILE *fp=fopen("/storage/emulated/0/Android/data/com.onzhou.ffmpeg.cmd/files/av_log.txt""a+");
    if(fp){
        vfprintf(fp,fmt,vl);
        fflush(fp);
        fclose(fp);
    }
}

JNIEXPORT jint JNICALL Java_com_onzhou_ffmpeg_cmd_FFmpegCmd_exec
        (JNIEnv *env, jobject obj, jint cmdnum, jobjectArray cmdline) {

    av_log_set_callback(custom_log);

    int argc = cmdnum;
    char **argv = (char **) malloc(sizeof(char *) * argc);

    int i = 0;
    for (i = 0; i < argc; i++) {
        jstring string = (*env)->GetObjectArrayElement(env, cmdline, i);
        const char *tmp = (*env)->GetStringUTFChars(env, string, 0);
        argv[i] = (char *) malloc(sizeof(char) * 1024);
        strcpy(argv[i], tmp);
    }

    exec(argc, argv);

    for (i = 0; i < argc; i++) {
        free(argv[i]);
    }
    free(argv);
    return 0;
}
  • 新建ffmpeg_cmd.h標頭檔案
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_onzhou_ffmpeg_cmd_FFmpegCmd */

#ifndef _Included_com_onzhou_ffmpeg_cmd_FFmpegCmd
#define _Included_com_onzhou_ffmpeg_cmd_FFmpegCmd
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_onzhou_ffmpeg_cmd_FFmpegCmd
 * Method:    exec
 * Signature: (I[Ljava/lang/String;)V
 */
JNIEXPORT jint JNICALL Java_com_onzhou_ffmpeg_cmd_FFmpegCmd_exec
        (JNIEnv *, jobject, jint, jobjectArray);

#ifdef __cplusplus
}
#endif
#endif

build.gradle中的配置不多說了,可以參考之前的實踐配置

  • 配置CMakeLists.txt檔案

cmake_minimum_required(VERSION 3.4.1)


add_library(ffmpeg-cmd
           SHARED
           src/main/cpp/ffmpeg_cmd.c
           src/main/cpp/cmdutils.c
           src/main/cpp/ffmpeg_filter.c
           src/main/cpp/ffmpeg_opt.c
           src/main/cpp/ffmpeg_mod.c)

find_library(log-lib
            log)

#獲取上級目錄
get_filename_component(PARENT_DIR ${CMAKE_SOURCE_DIR} PATH)
set(LIBRARY_DIR ${PARENT_DIR}/ffmpeg-single)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
set(CMAKE_VERBOSE_MAKEFILE on)

add_library(ffmpeg-single
           SHARED
           IMPORTED)

set_target_properties(ffmpeg-single
                    PROPERTIES IMPORTED_LOCATION
                    ${LIBRARY_DIR}/libs/${ANDROID_ABI}/libffmpeg.so
                    )

#標頭檔案
include_directories(${LIBRARY_DIR}/libs/${ANDROID_ABI}/include src/main/cpp)

target_link_libraries(ffmpeg-cmd ffmpeg-single ${log-lib})

修改原始檔

  • 修改cmdutils.c檔案
void exit_program(int ret)
{
    if (program_exit)
        program_exit(ret);

    exit(ret);
}

#改為

int exit_program(int ret)
{
    if (program_exit)
        program_exit(ret);
    //exit(ret);
    return ret;
}
  • 修改cmdutils.h標頭檔案
void exit_program(int ret) av_noreturn;

#改為

int exit_program(int ret) av_noreturn;
  • 修改ffmpeg_mod.c檔案
int main(int argc, char **argv)

#改為

int exec(int argc, char **argv)

return main_return_code;之前加上如下程式碼:

nb_filtergraphs = 0;
progress_avio = NULL;

input_streams = NULL;
nb_input_streams = 0;
input_files = NULL;
nb_input_files = 0;

output_streams = NULL;
nb_output_streams = 0;
output_files = NULL;
nb_output_files = 0;
  • 測試執行命令列進行轉碼
public void onCmdClick(View view) {
    if (fFmpegCmd == null) {
        fFmpegCmd = new FFmpegCmd();
    }
    btnTransfer.setEnabled(false);
    File outputFile = new File(getExternalFilesDir(null)"output.mkv");
    if (outputFile.exists()) {
        outputFile.delete();
    }
    Schedulers.newThread().scheduleDirect(new Runnable() {
        @Override
        public void run() {
            String filePath = getExternalFilesDir(null).getAbsolutePath();
            String cmdline = String.format("ffmpeg -i %s/input.mp4 %s/output.mkv", filePath, filePath);
            String[] argv = cmdline.split(" ");
            Integer argc = argv.length;
            fFmpegCmd.exec(argc, argv);
        }
    });
}

執行輸出

專案地址
https://github.com/byhook/ffmpeg4android

參考雷神:
https://blog.csdn.net/leixiaohua1020/article/details/47056365