android采用FFmpeg实现音视频合成与分离
上一篇文章谈到音频剪切、混音、拼接与转码,也详细介绍cMake配置与涉及FFmpeg文件的导入: android端采用FFmpeg进行音频混合与拼接剪切 。现在接着探讨音视频的合成与分离。
1、音频提取
从多媒体文件中提取音频,关键命令为“-acodec copy -vn”,其中“-acodec copy”是采用音频编码器拷贝音频流,“-vn”是去掉video视频流:
/** * 使用ffmpeg命令行进行抽取音频 * @param srcFile 原文件 * @param targetFile 目标文件 * @return 抽取后的音频文件 */ 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); return mixAudioCmd.split(" ");//以空格分割为字符串数组 }
2、视频提取
从多媒体文件中提取视频,关键命令为“-vcodec copy -an”,其中“-vcodec copy”是采用视频编码器拷贝视频流,“-an”是去掉audio音频流:
/** * 使用ffmpeg命令行进行抽取视频 * @param srcFile 原文件 * @param targetFile 目标文件 * @return 抽取后的视频文件 */ 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); return mixAudioCmd.split(" ");//以空格分割为字符串数组 }
3、音视频合成
把音频和视频文件合成多媒体文件,关键命令是“-i %s -i %s -t”,分别代表输入音频、视频和文件时长。需要注意的是,如果原视频文件包含有音频,先把单独视频流抽取出来,然后再使用独立音频和视频进行合成:
/** * 使用ffmpeg命令行进行音视频合成 * @param videoFile 视频文件 * @param audioFile 音频文件 * @param duration 视频时长 * @param muxFile 目标文件 * @return 合成后的文件 */ @SuppressLint("DefaultLocale") 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); return mixAudioCmd.split(" ");//以空格分割为字符串数组 }
单独的视频提取出来后,进行音视频合成:
public void handleMessage(Message msg) { super.handleMessage(msg); if(msg.what == 100){ String audioFile = PATH + File.separator + "tiger.mp3";//tiger.mp3 String muxFile = PATH + File.separator + "media-mux.mp4"; try { //使用MediaPlayer获取视频时长 MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(videoFile); mediaPlayer.prepare(); //单位为ms int videoDuration = mediaPlayer.getDuration()/1000; Log.i(TAG, "videoDuration=" + videoDuration); mediaPlayer.release(); //使用MediaMetadataRetriever获取音频时长 MediaMetadataRetriever mediaRetriever = new MediaMetadataRetriever(); mediaRetriever.setDataSource(audioFile); //单位为ms String duration = mediaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); int audioDuration = (int)(Long.parseLong(duration)/1000); Log.i(TAG, "audioDuration=" + audioDuration); mediaRetriever.release(); //如果视频时长比音频长,采用音频时长,否则用视频时长 int mDuration = Math.min(audioDuration, videoDuration); //使用纯视频与音频进行合成 String[] commandLine = FFmpegUtil.mediaMux(temp, audioFile, mDuration, muxFile); executeFFmpegCmd(commandLine); isMux = false; } catch (Exception e) { e.printStackTrace(); } } }
拼接好FFmpeg命令后,调用native方法去执行:
/** * 调用ffmpeg处理音视频 * @param handleType handleType */ private void doHandleMedia(int handleType){ String[] commandLine = null; switch (handleType){ case 0://音视频合成 try { //视频文件有音频,先把纯视频文件抽取出来 commandLine = FFmpegUtil.extractVideo(videoFile, temp); isMux = true; } catch (Exception e) { e.printStackTrace(); } break; case 1://提取音频 String extractAudio = PATH + File.separator + "extractAudio.aac"; commandLine = FFmpegUtil.extractAudio(srcFile, extractAudio); break; case 2://提取视频 String extractVideo = PATH + File.separator + "extractVideo.mp4"; commandLine = FFmpegUtil.extractVideo(srcFile, extractVideo); 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 media onBegin..."); } @Override public void onEnd(int result) { Log.i(TAG, "handle media onEnd..."); if(isMux){ mHandler.obtainMessage(100).sendToTarget(); }else { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MediaHandleActivity.this, "handle media finish...", Toast.LENGTH_SHORT).show(); } }); } } }); }
好了,使用FFmpeg进行音视频合成与分离介绍完毕。如果各位有什么问题或者建议,欢迎交流。
源码:链接地址。如果对您有帮助,麻烦fork和star。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
Flutter如何通过一行命令解决多个pubspec.yaml文件的依赖项问题
这篇文章主要介绍了Flutter如何通过一行命令解决多个pubspec.yaml文件的依赖项问题,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2023-06-06Android开发使用RecyclerView添加点击事件实例详解
这篇文章主要为大家介绍了Android开发使用RecyclerView添加点击事件实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2022-08-08
最新评论