NDK开发——FFmpeg在Linux下编译、移植命令行、合并音视频

实现内容

  • 编译FFmpeg成命令行形式
  • 测试FFmpeg命令行合并音视频

编译FFmpeg成命令行形式

一、编写本地方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class FFmpegRun {
static {
System.loadLibrary("ffmpeg");
System.loadLibrary("ffmpeginvoke");
}
/**
* 合并音视频的命令
* @param commands
* @return
*/
public native static int run(String[] commands);
/**
* @param pathAudio
* @param pathVideo
* @param pathOutputVideo
* @return
*/
public static int mergeVideo(String pathAudio, String pathVideo, String pathOutputVideo) {
String[] commands = new String[10];
commands[0] = "ffmpeg";
commands[1] = "-i";
commands[2] = pathAudio;
commands[3] = "-i";
commands[4] = pathVideo;
commands[5] = "-vcodec";
commands[6] = "copy";
commands[7] = "-acodec";
commands[8] = "copy";
commands[9] = pathOutputVideo;
return FFmpegRun.run(commands);
}
}

二、javah生成文件

在Terminal中javah我们写好的本地方法

1
C:\Users\handsome\Desktop\FFmpegDouYin\app\src\main\java>javah com.handsome.ffmpegdouyin.FFmpeg.FFmpegRun

将生成的.h文件放置jni目录下

三、移植FFmpeg

  1. 将编译FFmpeg成一个SO库生成的 libffmpeg.so 文件拷贝至 jni 目录
  2. 在 jni 目录新建文件 Android.mk Application.mk com_ihubin_ffmpegstudy_FFmpegKit.c
  3. 复制FFmpeg源码文件ffmpeg.h, ffmpeg.c, ffmpeg_opt.c, ffmpeg_filter.c,cmdutils.c, cmdutils.h, cmdutils_common_opts.h 到jni目录下

这里写图片描述

四、修改FFmpeg源码

1、ffmpeg.c

1
2
3
4
//找到mian方法
int main(int argc, char ** argv)
//改名为run方法
int run(int argc, char **argv)

2、ffmpeg.h

1
2
//在文件末尾添加函数申明
int run(int argc, char **argv);

3、cmdutils.c

1
2
3
4
5
6
7
8
9
10
11
12
13
//找到
void exit_program(int ret)
{
if (program_exit)
program_exit(ret);
exit(ret);
}
//改成
int exit_program(int ret)
{
return ret;
}

4、cmdutils.h

1
2
3
4
//找到
void exit_program(int ret) av_noreturn;
//改为
int exit_program(int ret);

五、编写c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "com_handsome_ffmpegdouyin_FFmpeg_FFmpegRun.h"
#include "ffmpeg.h"
#include <string.h>
JNIEXPORT jint JNICALL Java_com_handsome_ffmpegdouyin_FFmpeg_FFmpegRun_run(JNIEnv *env,
jclass obj, jobjectArray commands) {
int argc = (*env)->GetArrayLength(env, commands);
char *argv[argc];
int i;
for (i = 0; i < argc; i++) {
jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
argv[i] = (char*) (*env)->GetStringUTFChars(env, js, 0);
}
return run(argc, argv);
}

六、编写Android.mk和Application.mk

Android.mk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeginvoke
LOCAL_SRC_FILES := com_handsome_ffmpegdouyin_FFmpeg_FFmpegRun.c ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.c
# 这里的地址改成自己的 FFmpeg 源码目录
LOCAL_C_INCLUDES := D:\360Downloads\ffmpeg-3.0.10
LOCAL_LDLIBS := -llog -lz -ldl
LOCAL_SHARED_LIBRARIES := ffmpeg
include $(BUILD_SHARED_LIBRARY)

Application.mk

1
2
3
APP_ABI := armeabi armeabi-v7a
APP_BUILD_SCRIPT := Android.mk
APP_PLATFORM := android-14

七、ndk-build

在命令行执行ndk-build,编译会报找不到h文件,这个时候就要从服务器中编译过后的FFmpeg源码中找到对应的h文件添加到自己的本地源码对应的位置即可,具体在三个左右

八、提取so库

当你编译成功时,会产生obj的文件夹,我们只需要剪切产生的两个so库放置到libs目录下

这里写图片描述

九、配置gradle

在app的gradle下配置以下信息

1
2
3
4
5
6
7
8
9
defaultConfig {
ndk{
abiFilters "armeabi-v7a"
}
}
sourceSets.main {
jniLibs.srcDirs = ['libs']
jni.srcDirs = []
}

十、配置gradle.properties

1
android.useDeprecatedNdk=true

十一、增加权限

1
2
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

测试FFmpeg命令行合并音视频

一、编写代码,执行FFmpeg命令函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainActivity extends AppCompatActivity {
public String SD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar;
public String VIDEO_PATH = SD_PATH + "kugoumusic/input.mp4";
public String AUDIO_PATH = SD_PATH + "kugoumusic/input.mp3";
public String OUTPUT_VIDEO_PATH = SD_PATH + "kugoumusic/output.mp4";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void mergeVideo(View view) {
int result = FFmpegRun.mergeVideo(AUDIO_PATH, VIDEO_PATH, OUTPUT_VIDEO_PATH);
if (result == 0) {
Toast.makeText(this, "finish", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "error", Toast.LENGTH_SHORT).show();
}
}
}

二、运行代码

点击按钮合并音视频,这样就大功告成了

源码下载

坚持原创技术分享,您的支持将鼓励我继续创作!