Android端使用FFmpeg进行视频画面拼接

      今天看到抖音上,有人发布水平拼接的短视频。这创意不错,本身是两个不同的短视频,通过视频拼接技术把两短视频拼在一起。除了视频进行水平拼接,音频也混合在一起。前期还需要对那两视频进行预处理:包括时长、分辨率、像素格式等。既然抖音可以做出这样效果,那我们也应该可以做。下面是抖音的视频拼接截图:

      我第一时间想到的是FFmpeg,通过查阅FFmpeg文档,发现可以采用overlayhstack两种方式实现。文档上介绍hstack处理会overlay更快点,但要求所有输入的视频流像素格式和高度一致。我这里选择hstack来进行视频画面拼接,通过调用FFmpeg命令行实现。

      首先JNI层提供接口处理命令行:

JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_FFmpegCmd_handle
(JNIEnv *env, jclass obj, jobjectArray commands){
    int argc = (*env)->GetArrayLength(env, commands);
    char **argv = (char**)malloc(argc * sizeof(char*));
    int i;
    int result;
    for (i = 0; i < argc; i++) {
        jstring jstr = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
        char* temp = (char*) (*env)->GetStringUTFChars(env, jstr, 0);
        argv[i] = malloc(1024);
        strcpy(argv[i], temp);
        (*env)->ReleaseStringUTFChars(env, jstr, temp);
    }
    //执行ffmpeg命令
    result =  run(argc, argv);
    //释放内存
    for (i = 0; i < argc; i++) {
        free(argv[i]);
    }
    free(argv);
    return result;
}

      java层调用JNI接口,开启子线程执行:

    public static void execute(final String[] commands, final OnHandleListener onHandleListener){
        new Thread(new Runnable() {
            @Override
            public void run() {
                if(onHandleListener != null){
                    onHandleListener.onBegin();
                }
                //调用ffmpeg进行处理
                int result = handle(commands);
                if(onHandleListener != null){
                    onHandleListener.onEnd(result);
                }
            }
        }).start();
    }
    private native static int handle(String[] commands);

      视频画面拼接的FFmpeg命令行是这样的:

    /**
     * 多画面拼接视频
     * @param input1 输入文件1
     * @param input2 输入文件2
     * @param videoLayout 视频布局
     * @param targetFile 画面拼接文件
     *
     * @return 画面拼接的命令行
     */
    public static  String[] multiVideo(String input1, String input2, String targetFile, int videoLayout){
        String multiVideo = "ffmpeg -i %s -i %s -filter_complex hstack %s";//hstack:水平拼接,默认
        if(videoLayout == VideoLayout.LAYOUT_VERTICAL){//vstack:垂直拼接
            multiVideo = multiVideo.replace("hstack", "vstack");
        }
        multiVideo = String.format(multiVideo, input1, input2, targetFile);
        return multiVideo.split(" ");
    }

      调用上述方法,需要指定输入、输出视频文件路径,指定视频画面布局:水平拼接/垂直拼接,默认水平拼接:

String input1 = PATH + File.separator + "input1.mp4";
String input2 = PATH + File.separator + "input2.mp4";
String outputFile = PATH + File.separator + "multi.mp4";
commandLine = FFmpegUtil.multiVideo(input1, input2, outputFile, VideoLayout.LAYOUT_HORIZONTAL);

      最终执行FFmpeg命令行,带开始与结束回调接口:

    private void executeFFmpegCmd(final String[] commandLine){
        if(commandLine == null){
            return;
        }
        FFmpegCmd.execute(commandLine, new FFmpegCmd.OnHandleListener() {
            @Override
            public void onBegin() {
                Log.i(TAG, "handle video onBegin...");
                mHandler.obtainMessage(MSG_BEGIN).sendToTarget();
            }

            @Override
            public void onEnd(int result) {
                Log.i(TAG, "handle video onEnd...");

                mHandler.obtainMessage(MSG_FINISH).sendToTarget();
            }
        });
    }

      视频水平拼接出来的效果图,由于博客无法上传视频,我转成GIF图(有点模糊):

      除了水平拼接,还可以进行垂直拼接,改用vstack(大伙儿应该可以猜到v代表vertical):

      到这里,已经介绍完防抖音神器的视频拼接技术,也给出另外一种拼接方式。大家如有问题或建议,欢迎提出来互相探讨。具体源码请前往Github:https://github.com/xufuji456/FFmpegAndroid

发布了63 篇原创文章 · 获赞 179 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/u011686167/article/details/80726977