From 50e8d72e98d12ad641ff925f058691ebaec46cf5 Mon Sep 17 00:00:00 2001 From: xufulong <839789740@qq.com> Date: Thu, 12 Dec 2019 00:44:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=9B=BE=E7=89=87=E5=90=88?= =?UTF-8?q?=E6=88=90=E8=A7=86=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 处理图片合成视频 --- .../ffmpeg/activity/VideoHandleActivity.java | 31 +++- .../com/frank/ffmpeg/util/FFmpegUtil.java | 157 ++++++++++-------- 2 files changed, 109 insertions(+), 79 deletions(-) diff --git a/app/src/main/java/com/frank/ffmpeg/activity/VideoHandleActivity.java b/app/src/main/java/com/frank/ffmpeg/activity/VideoHandleActivity.java index 902b565..d301a73 100644 --- a/app/src/main/java/com/frank/ffmpeg/activity/VideoHandleActivity.java +++ b/app/src/main/java/com/frank/ffmpeg/activity/VideoHandleActivity.java @@ -91,6 +91,10 @@ public class VideoHandleActivity extends BaseActivity { @Override public void onViewClick(View view) { viewId = view.getId(); + if (viewId == R.id.btn_combine_video) { + handlePhoto(); + return; + } selectFile(); } @@ -182,15 +186,6 @@ public class VideoHandleActivity extends BaseActivity { // int recordTime = 10; // commandLine = FFmpegUtil.screenRecord(screenSize, recordTime, screenRecord); break; - case R.id.btn_combine_video://图片合成视频 - //图片所在路径,图片命名格式img+number.jpg - String picturePath = PATH + File.separator + "img/"; - if (!FileUtil.checkFileExist(picturePath)) { - return; - } - String combineVideo = PATH + File.separator + "combineVideo.mp4"; - commandLine = FFmpegUtil.pictureToVideo(picturePath, combineVideo); - break; case R.id.btn_multi_video://视频画面拼接:分辨率、时长、封装格式不一致时,先把视频源转为一致 String input1 = PATH + File.separator + "input1.mp4"; String input2 = PATH + File.separator + "input2.mp4"; @@ -263,6 +258,24 @@ public class VideoHandleActivity extends BaseActivity { } } + /** + * 图片合成视频 + */ + private void handlePhoto() { + // 图片所在路径,图片命名格式img+number.jpg + // 这里指定目录为根目录下img文件夹 + String picturePath = PATH + "/frame/"; + if (!FileUtil.checkFileExist(picturePath)) { + return; + } + String combineVideo = PATH + File.separator + "combineVideo.mp4"; + int frameRate = 2;// 合成视频帧率建议:1-10 普通视频帧率一般为25 + String[] commandLine = FFmpegUtil.pictureToVideo(picturePath, frameRate, combineVideo); + if (ffmpegHandler != null) { + ffmpegHandler.executeFFmpegCmd(commandLine); + } + } + @Override protected void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/com/frank/ffmpeg/util/FFmpegUtil.java b/app/src/main/java/com/frank/ffmpeg/util/FFmpegUtil.java index 17dc8b8..135b3c3 100644 --- a/app/src/main/java/com/frank/ffmpeg/util/FFmpegUtil.java +++ b/app/src/main/java/com/frank/ffmpeg/util/FFmpegUtil.java @@ -16,11 +16,12 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行音频转码 - * @param srcFile 源文件 + * + * @param srcFile 源文件 * @param targetFile 目标文件(后缀指定转码格式) * @return 转码后的文件 */ - public static String[] transformAudio(String srcFile, String targetFile){ + public static String[] transformAudio(String srcFile, String targetFile) { String transformAudioCmd = "ffmpeg -i %s %s"; transformAudioCmd = String.format(transformAudioCmd, srcFile, targetFile); return transformAudioCmd.split(" ");//以空格分割为字符串数组 @@ -28,14 +29,15 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行音频剪切 - * @param srcFile 源文件 - * @param startTime 剪切的开始时间(单位为秒) - * @param duration 剪切时长(单位为秒) + * + * @param srcFile 源文件 + * @param startTime 剪切的开始时间(单位为秒) + * @param duration 剪切时长(单位为秒) * @param targetFile 目标文件 * @return 剪切后的文件 */ @SuppressLint("DefaultLocale") - public static String[] cutAudio(String srcFile, int startTime, int duration, String targetFile){ + public static String[] cutAudio(String srcFile, int startTime, int duration, String targetFile) { String cutAudioCmd = "ffmpeg -i %s -acodec copy -ss %d -t %d %s"; cutAudioCmd = String.format(cutAudioCmd, srcFile, startTime, duration, targetFile); return cutAudioCmd.split(" ");//以空格分割为字符串数组 @@ -43,12 +45,13 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行音频合并 - * @param srcFile 源文件 + * + * @param srcFile 源文件 * @param appendFile 待追加的文件 * @param targetFile 目标文件 * @return 合并后的文件 */ - public static String[] concatAudio(String srcFile, String appendFile, String targetFile){ + public static String[] concatAudio(String srcFile, String appendFile, String targetFile) { String concatAudioCmd = "ffmpeg -i concat:%s|%s -acodec copy %s"; concatAudioCmd = String.format(concatAudioCmd, srcFile, appendFile, targetFile); return concatAudioCmd.split(" ");//以空格分割为字符串数组 @@ -56,12 +59,13 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行音频混合 - * @param srcFile 源文件 - * @param mixFile 待混合文件 + * + * @param srcFile 源文件 + * @param mixFile 待混合文件 * @param targetFile 目标文件 * @return 混合后的文件 */ - public static String[] mixAudio(String srcFile, String mixFile, String targetFile){ + public static String[] mixAudio(String srcFile, String mixFile, String targetFile) { String mixAudioCmd = "ffmpeg -i %s -i %s -filter_complex amix=inputs=2:duration=first -strict -2 %s"; mixAudioCmd = String.format(mixAudioCmd, srcFile, mixFile, targetFile); return mixAudioCmd.split(" ");//以空格分割为字符串数组 @@ -71,14 +75,15 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行音视频合成 + * * @param videoFile 视频文件 * @param audioFile 音频文件 - * @param duration 视频时长 - * @param muxFile 目标文件 + * @param duration 视频时长 + * @param muxFile 目标文件 * @return 合成后的文件 */ @SuppressLint("DefaultLocale") - public static String[] mediaMux(String videoFile, String audioFile, int duration, String muxFile){ + public static String[] mediaMux(String videoFile, String audioFile, int duration, String muxFile) { //-t:时长 如果忽略音视频时长,则把"-t %d"去掉 String mixAudioCmd = "ffmpeg -i %s -i %s -t %d %s"; mixAudioCmd = String.format(mixAudioCmd, videoFile, audioFile, duration, muxFile); @@ -87,11 +92,12 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行抽取音频 - * @param srcFile 原文件 + * + * @param srcFile 原文件 * @param targetFile 目标文件 * @return 抽取后的音频文件 */ - public static String[] extractAudio(String srcFile, String targetFile){ + public static String[] extractAudio(String srcFile, String targetFile) { //-vn:video not String mixAudioCmd = "ffmpeg -i %s -acodec copy -vn %s"; mixAudioCmd = String.format(mixAudioCmd, srcFile, targetFile); @@ -100,11 +106,12 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行抽取视频 - * @param srcFile 原文件 + * + * @param srcFile 原文件 * @param targetFile 目标文件 * @return 抽取后的视频文件 */ - public static String[] extractVideo(String srcFile, String targetFile){ + public static String[] extractVideo(String srcFile, String targetFile) { //-an audio not String mixAudioCmd = "ffmpeg -i %s -vcodec copy -an %s"; mixAudioCmd = String.format(mixAudioCmd, srcFile, targetFile); @@ -114,11 +121,12 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行视频转码 - * @param srcFile 源文件 + * + * @param srcFile 源文件 * @param targetFile 目标文件(后缀指定转码格式) * @return 转码后的文件 */ - public static String[] transformVideo(String srcFile, String targetFile){ + public static String[] transformVideo(String srcFile, String targetFile) { //指定目标视频的帧率、码率、分辨率 // String transformVideoCmd = "ffmpeg -i %s -r 25 -b 200 -s 1080x720 %s"; String transformVideoCmd = "ffmpeg -i %s -vcodec copy -acodec copy %s"; @@ -128,14 +136,15 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行视频剪切 - * @param srcFile 源文件 - * @param startTime 剪切的开始时间(单位为秒) - * @param duration 剪切时长(单位为秒) + * + * @param srcFile 源文件 + * @param startTime 剪切的开始时间(单位为秒) + * @param duration 剪切时长(单位为秒) * @param targetFile 目标文件 * @return 剪切后的文件 */ @SuppressLint("DefaultLocale") - public static String[] cutVideo(String srcFile, int startTime, int duration, String targetFile){ + public static String[] cutVideo(String srcFile, int startTime, int duration, String targetFile) { String cutVideoCmd = "ffmpeg -i %s -ss %d -t %d -acodec copy -vcodec copy %s"; cutVideoCmd = String.format(cutVideoCmd, srcFile, startTime, duration, targetFile); return cutVideoCmd.split(" ");//以空格分割为字符串数组 @@ -143,13 +152,14 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行视频截图 - * @param srcFile 源文件 - * @param time 截图开始时间 - * @param size 图片尺寸大小 + * + * @param srcFile 源文件 + * @param time 截图开始时间 + * @param size 图片尺寸大小 * @param targetFile 目标文件 * @return 截图后的文件 */ - public static String[] screenShot(String srcFile, int time, String size, String targetFile){ + public static String[] screenShot(String srcFile, int time, String size, String targetFile) { String screenShotCmd = "ffmpeg -i %s -f image2 -ss %d -s %s %s"; screenShotCmd = String.format(Locale.getDefault(), screenShotCmd, srcFile, time, size, targetFile); return screenShotCmd.split(" ");//以空格分割为字符串数组 @@ -157,12 +167,13 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行给视频添加水印 - * @param srcFile 源文件 - * @param waterMark 水印文件路径 + * + * @param srcFile 源文件 + * @param waterMark 水印文件路径 * @param targetFile 目标文件 * @return 添加水印后的文件 */ - public static String[] addWaterMark(String srcFile, String waterMark, String resolution, int bitRate, String targetFile){ + public static String[] addWaterMark(String srcFile, String waterMark, String resolution, int bitRate, String targetFile) { String mBitRate = String.valueOf(bitRate) + "k"; String waterMarkCmd = "ffmpeg -i %s -i %s -s %s -b:v %s -filter_complex overlay=0:0 %s"; waterMarkCmd = String.format(waterMarkCmd, srcFile, waterMark, resolution, mBitRate, targetFile); @@ -171,17 +182,18 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行视频转成Gif动图 - * @param srcFile 源文件 - * @param startTime 开始时间 - * @param duration 截取时长 + * + * @param srcFile 源文件 + * @param startTime 开始时间 + * @param duration 截取时长 * @param targetFile 目标文件 * @param resolution 分辨率 - * @param frameRate 帧率 + * @param frameRate 帧率 * @return Gif文件 */ @SuppressLint("DefaultLocale") - public static String[] generateGif(String srcFile, int startTime, int duration, - String resolution, int frameRate, String targetFile){ + public static String[] generateGif(String srcFile, int startTime, int duration, + String resolution, int frameRate, String targetFile) { String generateGifCmd = "ffmpeg -i %s -ss %d -t %d -s %s -r %d -f gif %s"; generateGifCmd = String.format(generateGifCmd, srcFile, startTime, duration, resolution, frameRate, targetFile); @@ -190,13 +202,14 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行屏幕录制 - * @param size 视频尺寸大小 + * + * @param size 视频尺寸大小 * @param recordTime 录屏时间 * @param targetFile 目标文件 * @return 屏幕录制文件 */ @SuppressLint("DefaultLocale") - public static String[] screenRecord(String size, int recordTime, String targetFile){ + public static String[] screenRecord(String size, int recordTime, String targetFile) { //-vd x11:0,0 指录制所使用的偏移为 x=0 和 y=0 //String screenRecordCmd = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s %s"; String screenRecordCmd = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s -t %d %s"; @@ -207,15 +220,17 @@ public class FFmpegUtil { /** * 使用ffmpeg命令行进行图片合成视频 - * @param srcFile 源文件 + * + * @param srcFile 源文件 + * @param frameRate 合成视频帧率 * @param targetFile 目标文件(mpg格式) * @return 合成的视频文件 */ @SuppressLint("DefaultLocale") - public static String[] pictureToVideo(String srcFile, String targetFile){ + public static String[] pictureToVideo(String srcFile, int frameRate, String targetFile) { //-f image2:代表使用image2格式,需要放在输入文件前面 - String combineVideo = "ffmpeg -f image2 -r 1 -i %simg#d.jpg -vcodec mpeg4 %s"; - combineVideo = String.format(combineVideo, srcFile, targetFile); + String combineVideo = "ffmpeg -f image2 -r %d -i %simg#d.jpg -vcodec mpeg4 %s"; + combineVideo = String.format(combineVideo, frameRate, srcFile, targetFile); combineVideo = combineVideo.replace("#", "%"); Log.i("VideoHandleActivity", "combineVideo=" + combineVideo); return combineVideo.split(" ");//以空格分割为字符串数组 @@ -223,14 +238,15 @@ public class FFmpegUtil { /** * 音频编码 - * @param srcFile 源文件pcm裸流 + * + * @param srcFile 源文件pcm裸流 * @param targetFile 编码后目标文件 * @param sampleRate 采样率 - * @param channel 声道:单声道为1/立体声道为2 + * @param channel 声道:单声道为1/立体声道为2 * @return 音频编码的命令行 */ @SuppressLint("DefaultLocale") - public static String[] encodeAudio(String srcFile, String targetFile, int sampleRate, int channel){ + public static String[] encodeAudio(String srcFile, String targetFile, int sampleRate, int channel) { String combineVideo = "ffmpeg -f s16le -ar %d -ac %d -i %s %s"; combineVideo = String.format(combineVideo, sampleRate, channel, srcFile, targetFile); return combineVideo.split(" "); @@ -238,18 +254,18 @@ public class FFmpegUtil { /** * 多画面拼接视频 - * @param input1 输入文件1 - * @param input2 输入文件2 - * @param videoLayout 视频布局 - * @param targetFile 画面拼接文件 * + * @param input1 输入文件1 + * @param input2 输入文件2 + * @param videoLayout 视频布局 + * @param targetFile 画面拼接文件 * @return 画面拼接的命令行 */ - public static String[] multiVideo(String input1, String input2, String targetFile, int videoLayout){ + public static String[] multiVideo(String input1, String input2, String targetFile, int videoLayout) { // String multiVideo = "ffmpeg -i %s -i %s -i %s -i %s -filter_complex " + // "\"[0:v]pad=iw*2:ih*2[a];[a][1:v]overlay=w[b];[b][2:v]overlay=0:h[c];[c][3:v]overlay=w:h\" %s"; String multiVideo = "ffmpeg -i %s -i %s -filter_complex hstack %s";//hstack:水平拼接,默认 - if(videoLayout == VideoLayout.LAYOUT_VERTICAL){//vstack:垂直拼接 + if (videoLayout == VideoLayout.LAYOUT_VERTICAL) {//vstack:垂直拼接 multiVideo = multiVideo.replace("hstack", "vstack"); } multiVideo = String.format(multiVideo, input1, input2, targetFile); @@ -258,12 +274,12 @@ public class FFmpegUtil { /** * 视频反序倒播 - * @param inputFile 输入文件 - * @param targetFile 反序文件 * + * @param inputFile 输入文件 + * @param targetFile 反序文件 * @return 视频反序的命令行 */ - public static String[] reverseVideo(String inputFile, String targetFile){ + public static String[] reverseVideo(String inputFile, String targetFile) { //FIXME 音频也反序 // String reverseVideo = "ffmpeg -i %s -filter_complex [0:v]reverse[v];[0:a]areverse[a] -map [v] -map [a] %s"; String reverseVideo = "ffmpeg -i %s -filter_complex [0:v]reverse[v] -map [v] %s";//单纯视频反序 @@ -273,12 +289,12 @@ public class FFmpegUtil { /** * 视频降噪 - * @param inputFile 输入文件 - * @param targetFile 输出文件 * + * @param inputFile 输入文件 + * @param targetFile 输出文件 * @return 视频降噪的命令行 */ - public static String[] denoiseVideo(String inputFile, String targetFile){ + public static String[] denoiseVideo(String inputFile, String targetFile) { String reverseVideo = "ffmpeg -i %s -nr 500 %s"; reverseVideo = String.format(reverseVideo, inputFile, targetFile); return reverseVideo.split(" "); @@ -286,15 +302,15 @@ public class FFmpegUtil { /** * 视频抽帧转成图片 - * @param inputFile 输入文件 - * @param startTime 开始时间 - * @param duration 持续时间 - * @param frameRate 帧率 - * @param targetFile 输出文件 * + * @param inputFile 输入文件 + * @param startTime 开始时间 + * @param duration 持续时间 + * @param frameRate 帧率 + * @param targetFile 输出文件 * @return 视频抽帧的命令行 */ - public static String[] videoToImage(String inputFile, int startTime, int duration, int frameRate, String targetFile){ + public static String[] videoToImage(String inputFile, int startTime, int duration, int frameRate, String targetFile) { //-ss:开始时间,单位为秒 //-t:持续时间,单位为秒 //-r:帧率,每秒抽多少帧 @@ -306,16 +322,16 @@ public class FFmpegUtil { /** * 视频叠加成画中画 + * * @param inputFile1 输入文件 * @param inputFile2 输入文件 * @param targetFile 输出文件 - * @param x 小视频起点x坐标 - * @param y 小视频起点y坐标 - * + * @param x 小视频起点x坐标 + * @param y 小视频起点y坐标 * @return 视频画中画的命令行 */ @SuppressLint("DefaultLocale") - public static String[] picInPicVideo(String inputFile1, String inputFile2, int x, int y, String targetFile){ + public static String[] picInPicVideo(String inputFile1, String inputFile2, int x, int y, String targetFile) { String reverseVideo = "ffmpeg -i %s -i %s -filter_complex overlay=%d:%d %s"; reverseVideo = String.format(reverseVideo, inputFile1, inputFile2, x, y, targetFile); return reverseVideo.split(" "); @@ -323,11 +339,12 @@ public class FFmpegUtil { /** * mp4视频的moov往mdat前面移动 - * @param inputFile inputFile + * + * @param inputFile inputFile * @param outputFile outputFile * @return 移动moov命令行 */ - public static String[] moveMoovAhead(String inputFile, String outputFile){ + public static String[] moveMoovAhead(String inputFile, String outputFile) { String moovCmd = "ffmpeg -i %s -movflags faststart -acodec copy -vcodec copy %s"; moovCmd = String.format(Locale.CHINESE, moovCmd, inputFile, outputFile); return moovCmd.split(" ");