今天看到抖音上,有人发布水平拼接的短视频。这创意不错,本身是两个不同的短视频,通过视频拼接技术把两短视频拼在一起。除了视频进行水平拼接,音频也混合在一起。前期还需要对那两视频进行预处理:包括时长、分辨率、像素格式等。既然抖音可以做出这样效果,那我们也应该可以做。下面是抖音的视频拼接截图:
我第一时间想到的是FFmpeg,通过查阅FFmpeg文档,发现可以采用overlay和hstack两种方式实现。文档上介绍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