方法一(Fail)
利用MediaMux实现音视频的合成。
效果:可以实现音视频的合并,利用Android原生的VideoView和SurfaceView播放正常,大部分的播放器也播放正常,但是,但是,在上传Youtube就会出现问题:音频不连续,分析主要是上传Youtube时会被再次的压缩,可能在压缩的过程中出现音频的帧率出现问题。
方法二
利用mp4parser实现
mp4parser是一个视频处理的开源工具箱,由于mp4parser里的方法都依靠工具箱里的一些内容,所以需要将这些内容打包成jar包,放到自己的工程里,才能对mp4parser的方法进行调用。
方法三
利用FFmpeg大法
FFmpeg 由于其丰富的 codec 插件,详细的文档说明,并且与其调试复杂量大的编解码代码(是的,用 MediaCodec 实现起来十分啰嗦和繁琐)还是不如调试一行 ffmpeg 命令来的简单。
实现步骤:
1.添加依赖
implementation 'com.googlecode.mp4parser:isoparser:1.1.21'
2.视频裁剪
/**
* 裁剪视频
* @param srcPath 需要裁剪的原视频路径
* @param outPath 裁剪后的视频输出路径
* @param startTimeMs 裁剪的起始时间
* @param endTimeMs 裁剪的结束时间
*/
public static void clip(String srcPath, String outPath, double startTimeMs, double endTimeMs) throws IOException, IllegalArgumentException {
if (TextUtils.isEmpty(srcPath) || TextUtils.isEmpty(outPath)) {
throw new IllegalArgumentException("file path can't be null!!!!");
}
if (!(new File(srcPath).exists())) {
throw new IllegalArgumentException("the source file is not exist!!!!");
}
if (startTimeMs >= endTimeMs) {
throw new IllegalArgumentException("the startTimeMs is larger than endTimeMs!!!!");
}
Movie movie = MovieCreator.build(srcPath);
List<Track> tracks = movie.getTracks();
//移除旧的track
movie.setTracks(new LinkedList<Track>());
//处理的时间以秒为单位
double startTime = startTimeMs/1000;
double endTime = endTimeMs/1000;
YDLog.logDebeg(TAG, "--->>>>startTimeMs = " + startTimeMs + "\n endTimeMs = " + endTimeMs + "\n tracks.size = " + tracks.size());
//计算剪切时间,视频的采样间隔大,以视频为准
for (Track track : tracks) {
if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
startTime = correctTimeToSyncSample(track, startTime, false);
endTime = correctTimeToSyncSample(track, endTime, true);
if (track.getHandler().equals("vide")) {
break;
}
}
}
YDLog.logDebeg(TAG, "--->>>>startTime = " + startTime + "\n endTime = " + endTime);
long currentSample;
double currentTime;
double lastTime;
long startSample1;
long endSample1;
long delta;
for (Track track : tracks) {
currentSample = 0;
currentTime = 0;
lastTime = -1;
startSample1 = -1;
endSample1 = -1;
//根据起始时间和截止时间获取起始sample和截止sample的位置
for (int i = 0; i < track.getSampleDurations().length; i++) {
delta = track.getSampleDurations()[i];
if (currentTime > lastTime && currentTime <= startTime) {
startSample1 = currentSample;
}
if (currentTime > lastTime && currentTime <= endTime) {
endSample1 = currentSample;
}
lastTime = currentTime;
currentTime += (double)delta / (double)track.getTrackMetaData().getTimescale();
currentSample++;
}
Log.d(TAG, "track.getHandler() = " + track.getHandler() + "\n startSample1 = " + startSample1 + "\n endSample1 = " + endSample1);
if (startSample1 <= 0 && endSample1 <= 0) {
throw new RuntimeException("clip failed !!");
}
movie.addTrack(new CroppedTrack(track, startSample1, endSample1));// 添加截取的track
}
//合成视频mp4
Container out = new DefaultMp4Builder().build(movie);
FileOutputStream fos = new FileOutputStream(outPath);
FileChannel fco = fos.getChannel();
out.writeContainer(fco);
fco.close();
fos.close();
}
3.视频合成
/**
* 将 AAC 和 MP4 进行混合[替换了视频的音轨]
*
* @param aacPath .aac
* @param mp4Path .mp4
* @param outPath .mp4
*/
public static boolean muxAacMp4(String aacPath, String mp4Path, String outPath) {
try {
AACTrackImpl aacTrack = new AACTrackImpl(new FileDataSourceImpl(aacPath));
Movie videoMovie = MovieCreator.build(mp4Path);
Track videoTracks = null;// 获取视频的单纯视频部分
for (Track videoMovieTrack : videoMovie.getTracks()) {
if ("vide".equals(videoMovieTrack.getHandler())) {
videoTracks = videoMovieTrack;
}
}
Movie resultMovie = new Movie();
resultMovie.addTrack(videoTracks);// 视频部分
resultMovie.addTrack(aacTrack);// 音频部分
Container out = new DefaultMp4Builder().build(resultMovie);
FileOutputStream fos = new FileOutputStream(new File(outPath));
out.writeContainer(fos.getChannel());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
4.合成音视频长度不一样的问题
/***
* 把视频裁剪15s。然后和音频合并。然后再裁剪15s
* @param sdCardPath
*/
public static void getMergeVideo(String sdCardPath) {
String srcMergePath = sdCardPath + "/4.mp4";//录屏文件
String aacPath = sdCardPath + "/5.aac";
Log.d("peng", "onClick: aacPath" + aacPath);
String outMergePath = sdCardPath + "/6.mp4";//得到临时的合成的视频文件
Log.d("peng", "onClick: outMergePath" + outMergePath);
long startTime = 0;
long endTime = 15 * 1000;
boolean isMux = VideoClipUtils.muxAacMp4(aacPath, srcMergePath, outMergePath);
if (isMux) {
String outMergeClipPath = sdCardPath + "/7.mp4";
Log.d("peng", "onClick: outMergeClipPath" + outMergeClipPath);
try {
VideoClipUtils.clip(outMergePath, outMergeClipPath, startTime, endTime);
Log.d("peng", "OKKKKKKKK");
} catch (IOException e) {
e.printStackTrace();
}
}
}
遇到的问题:
1.音频和视频合成,他们的长度
要以视频为准的
2.合成音视频的格式,是否支持,2种方案
方案一:
先全部合并,然后再裁剪15s
方案二:(没有声音)
把视频裁剪15s。然后和音频合并。然后再裁剪15s
方案三:(导致有视频不一定有声音,或者有声音不一定有视频)
视频剪切,音频剪切,音频和视频合并
方法一:
MediaMuxer文件生成:
官方文档:
https://developer.android.google.cn/reference/android/media/MediaMuxer?hl=zh-cn
=========================================================
mp4方案:
http://blog.chinaunix.net/uid-20771867-id-4114253.html
使用map4parser作为视频处理包,android studio引入 compile 'com.googlecode.mp4parser:isoparser:1.1.21'//视频处理
2017年4月10日更新:
我发现该框架还有诸多问题和BUG,比如无法合并不同格式(帧率,分辨率)的视频,最近已改用MediaCodec,如果需要做一些比较复杂的处理,还是推荐使用MediaCodec和FFmpeg,后面有时间我会写一写相关的内容。
Android 使用 mp4parser 做视频裁剪
mp4parser (所有的用法-----可以)
https://www.jianshu.com/p/c87ada9b0f65
不错的库:
https://github.com/sannies/mp4parser
Android(java方法)上实现mp4的分割和拼接 (二)