前兩篇文章介紹過FFmpeg進行音頻處理、音視頻處理:android端採用FFmpeg進行音頻混合與拼接剪切, android端採用FFmpeg進行音視頻合成與分離。關於FFmpeg涉及文件導入以及cmake配置,可查看第一篇文章。如今接着探討視頻相關處理:視頻轉碼、視頻剪切、視頻截圖、添加水印、視頻轉Gif動圖、圖片合成視頻。java
視頻轉碼包括格式、碼率、尺寸大小等轉換,-f表明強制轉換格式,-b表明碼率,-s表明尺寸,-r表明幀率:android
/** * 使用ffmpeg命令行進行視頻轉碼 * @param srcFile 源文件 * @param targetFile 目標文件(後綴指定轉碼格式) * @return 轉碼後的文件 */ public static String[] transformVideo(String srcFile, String targetFile){ String transformVideoCmd = "ffmpeg -i %s -r 25 -b 200 -s 1080x720 %s"; transformVideoCmd = String.format(transformVideoCmd, srcFile, targetFile); return transformVideoCmd.split(" ");//以空格分割爲字符串數組 }
視頻剪切是從視頻指定開始時間,剪切指定時長視頻,-ss表明開始時間,-t表明時長:git
/** * 使用ffmpeg命令行進行視頻剪切 * @param srcFile 源文件 * @param startTime 剪切的開始時間(單位爲秒) * @param duration 剪切時長(單位爲秒) * @param targetFile 目標文件 * @return 剪切後的文件 */ public static String[] cutVideo(String srcFile, int startTime, int duration, String targetFile){ String cutVideoCmd = "ffmpeg -i %s -ss %d -t %d %s"; cutVideoCmd = String.format(cutVideoCmd, srcFile, startTime, duration, targetFile); return cutVideoCmd.split(" ");//以空格分割爲字符串數組 }
視頻截圖是從視頻截取當前一幀畫面,保存爲指定格式的圖片,使用image2工具:github
/** * 使用ffmpeg命令行進行視頻截圖 * @param srcFile 源文件 * @param size 圖片尺寸大小 * @param targetFile 目標文件 * @return 截圖後的文件 */ public static String[] screenShot(String srcFile, String size, String targetFile){ String screenShotCmd = "ffmpeg -i %s -f image2 -t 0.001 -s %s %s"; screenShotCmd = String.format(screenShotCmd, srcFile, size, targetFile); return screenShotCmd.split(" ");//以空格分割爲字符串數組 }
給視頻添加水印包括:文字和圖片,使用到filter_complex濾波器,overlay指定圖片在視頻的位置:數組
/** * 使用ffmpeg命令行給視頻添加水印 * @param srcFile 源文件 * @param waterMark 水印文件路徑 * @param targetFile 目標文件 * @return 添加水印後的文件 */ public static String[] addWaterMark(String srcFile, String waterMark, String targetFile){ String waterMarkCmd = "ffmpeg -i %s -i %s -filter_complex overlay=0:0 %s"; waterMarkCmd = String.format(waterMarkCmd, srcFile, waterMark, targetFile); return waterMarkCmd.split(" ");//以空格分割爲字符串數組 }添加圖片水印效果:
/** * 文本轉成Bitmap * @param text 文本內容 * @param context 上下文 * @return 圖片的bitmap */ private static Bitmap textToBitmap(String text , Context context) { float scale = context.getResources().getDisplayMetrics().scaledDensity; TextView tv = new TextView(context); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); tv.setLayoutParams(layoutParams); tv.setText(text); tv.setTextSize(scale * TEXT_SIZE); tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setDrawingCacheEnabled(true); tv.setTextColor(TEXT_COLOR); tv.setBackgroundColor(Color.WHITE); tv.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight()); tv.buildDrawingCache(); Bitmap bitmap = tv.getDrawingCache(); int rate = bitmap.getHeight() / 20; return Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/rate, 20, false); } /** * 文字生成圖片 * @param filePath filePath * @param text text * @param context context * @return 生成圖片是否成功 */ public static boolean textToPicture(String filePath, String text , Context context){ Bitmap bitmap = textToBitmap(text , context); FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(filePath); bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); return false; }finally { try { if(outputStream != null){ outputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return true; }添加文字水印的效果:
/** * 使用ffmpeg命令行進行視頻轉成Gif動圖 * @param srcFile 源文件 * @param startTime 開始時間 * @param duration 截取時長 * @param targetFile 目標文件 * @return Gif文件 */ public static String[] generateGif(String srcFile, int startTime, int duration, String targetFile){ //String screenShotCmd = "ffmpeg -i %s -vframes %d -f gif %s"; String screenShotCmd = "ffmpeg -i %s -ss %d -t %d -s 320x240 -f gif %s"; screenShotCmd = String.format(screenShotCmd, srcFile, startTime, duration, targetFile); return screenShotCmd.split(" ");//以空格分割爲字符串數組 }截取出來的GIF動圖效果:
圖片合成視頻是把文件夾的有序命名圖片,使用-image2合成視頻,用-r指定幀率,這裏指定一秒播放一幀:app
/** * 使用ffmpeg命令行進行圖片合成視頻 * @param srcFile 源文件 * @param targetFile 目標文件(mpg格式) * @return 合成的視頻文件 */ public static String[] pictureToVideo(String srcFile, 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); combineVideo = combineVideo.replace("#", "%"); Log.i("VideoHandleActivity", "combineVideo=" + combineVideo); return combineVideo.split(" ");//以空格分割爲字符串數組 }因爲合成的視頻沒法上傳到這裏,我使用FFmpeg把視頻再轉成GIF動圖:
拼接好命令行後,調用FFmpeg的native方法處理:ide
/** * 調用ffmpeg處理視頻 * @param handleType handleType */ private void doHandleVideo(int handleType){ String[] commandLine = null; switch (handleType){ case 0://視頻轉碼 String transformVideo = PATH + File.separator + "transformVideo.wmv"; commandLine = FFmpegUtil.transformVideo(srcFile, transformVideo); break; case 1://視頻剪切 String cutVideo = PATH + File.separator + "cutVideo.mp4"; int startTime = 0; int duration = 20; commandLine = FFmpegUtil.cutVideo(srcFile, startTime, duration, cutVideo); break; case 2://視頻合併 break; case 3://視頻截圖 String screenShot = PATH + File.separator + "screenShot.jpg"; String size = "1080x720"; commandLine = FFmpegUtil.screenShot(srcFile, size, screenShot); break; case 4://視頻添加水印 //一、圖片 String photo = PATH + File.separator + "launcher.png"; String photoMark = PATH + File.separator + "photoMark.mp4"; commandLine = FFmpegUtil.addWaterMark(appendVideo, photo, photoMark); //二、文字 //String text = "Hello,FFmpeg"; //String textPath = PATH + File.separator + "text.jpg"; //boolean result = BitmapUtil.textToPicture(textPath, text, this); //Log.i(TAG, "text to pitcture result=" + result); //String textMark = PATH + File.separator + "textMark.mp4"; //commandLine = FFmpegUtil.addWaterMark(appendVideo, photo, textMark); break; case 5://視頻轉成gif String Video2Gif = PATH + File.separator + "Video2Gif.gif"; int gifStart = 30; int gifDuration = 5; commandLine = FFmpegUtil.generateGif(srcFile, gifStart, gifDuration, Video2Gif); break; case 6://屏幕錄製 break; case 7://圖片合成視頻 //圖片所在路徑,圖片命名格式img+number.jpg String picturePath = PATH + File.separator + "img/"; String combineVideo = PATH + File.separator + "combineVideo.mp4"; commandLine = FFmpegUtil.pictureToVideo(picturePath, combineVideo); break; default: break; } executeFFmpegCmd(commandLine); }FFmpeg處理的回調與更新界面:
/** * 執行ffmpeg命令行 * @param commandLine commandLine */ 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(); } }); }最終調用的FFmpeg的run方法:
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; }
好了,使用FFmpeg進行視頻剪切、轉碼、截圖、添加水印、截取GIF圖等介紹完畢。若是各位有什麼問題或者建議,歡迎交流。工具
源碼:https://github.com/xufuji456/FFmpegAndroid。若是對您有幫助,麻煩fork和star。ui