因公司项目有需求,要实现视频剪切,视频拼接以及音视频合并的功能,自己通过在网上查找大量的资料终于把功能实现了,把实现的公共类提取出来,以便以后复习巩固。
使用map4parser作为视频处理包,android studio引入 compile 'com.googlecode.mp4parser:isoparser:1.1.21'//视频处理
工具类
一、视频处理工具类
/**
* mp4处理公共类
* Created by lxy on 17-4-21.
*/
public class Mp4ParseUtil {
/**
* 对Mp4文件集合进行追加合并(按照顺序一个一个拼接起来)
*
* @param mp4PathList [输入]Mp4文件路径的集合(支持m4a)(不支持wav)
* @param outPutPath [输出]结果文件全部名称包含后缀(比如.mp4)
* @throws IOException 格式不支持等情况抛出异常
*/
public static void appendMp4List(List mp4PathList, String outPutPath){
try {
List mp4MovieList = new ArrayList<>();// Movie对象集合[输入]
for (String mp4Path : mp4PathList) {// 将每个文件路径都构建成一个Movie对象
mp4MovieList.add(MovieCreator.build(mp4Path));
}
List
二、视频剪切工具类
/**
* 视频剪切
* Created by lxy on 17-4-21.
*/
public class VideoClip {
private static final String TAG = "VideoClip";
private String filePath;//视频路径
private String workingPath;//输出路径
private String outName;//输出文件名
private double startTime;//剪切起始时间
private double endTime;//剪切结束时间
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public void setWorkingPath(String workingPath) {
this.workingPath = workingPath;
}
public void setOutName(String outName) {
this.outName = outName;
}
public void setEndTime(double endTime) {
this.endTime = endTime / 1000;
}
public void setStartTime(double startTime) {
this.startTime = startTime / 1000;
}
public synchronized void clip() {
try {
//将要剪辑的视频文件
Movie movie = MovieCreator.build(filePath);
List tracks = movie.getTracks();
movie.setTracks(new LinkedList());
//时间是否修正
boolean timeCorrected = false;
//计算并换算剪切时间
for (Track track : tracks) {
if (track.getSyncSamples() != null
&& track.getSyncSamples().length > 0) {
if (timeCorrected) {
throw new RuntimeException(
"The startTime has already been corrected by another track with SyncSample. Not Supported.");
}
//true,false表示短截取;false,true表示长截取
startTime = VideoHelper.correctTimeToSyncSample(track, startTime, false);//修正后的开始时间
endTime = VideoHelper.correctTimeToSyncSample(track, endTime, true); //修正后的结束时间
timeCorrected = true;
}
}
//根据换算到的开始时间和结束时间来截取视频
for (Track track : tracks) {
long currentSample = 0; //视频截取到的当前的位置的时间
double currentTime = 0; //视频的时间长度
double lastTime = -1; //上次截取到的最后的时间
long startSample1 = -1; //截取开始的时间
long endSample1 = -1; //截取结束的时间
//设置开始剪辑的时间和结束剪辑的时间 避免超出视频总长
for (int i = 0; i < track.getSampleDurations().length; i++) {
long 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++; //当前位置+1
}
movie.addTrack(new CroppedTrack(track, startSample1, endSample1));// 创建一个新的视频文件
}
//合成视频mp4
Container out = new DefaultMp4Builder().build(movie);
File storagePath = new File(workingPath);
storagePath.mkdirs();
FileOutputStream fos = new FileOutputStream(new File(storagePath, outName));
FileChannel fco = fos.getChannel();
out.writeContainer(fco);
//关闭流
fco.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Created by lxy on 17-4-17.
*/
public class VideoHelper {
//换算剪切时间
public static double correctTimeToSyncSample(Track track, double cutHere,
boolean next) {
double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
long currentSample = 0;
double currentTime = 0;
for (int i = 0; i < track.getSampleDurations().length; i++) {
long delta = track.getSampleDurations()[i];
if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(),
currentSample + 1)] = currentTime;
}
currentTime += (double) delta
/ (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
double previous = 0;
for (double timeOfSyncSample : timeOfSyncSamples) {
if (timeOfSyncSample > cutHere) {
if (next) {
return timeOfSyncSample;
} else {
return previous;
}
}
previous = timeOfSyncSample;
}
return timeOfSyncSamples[timeOfSyncSamples.length - 1];
}
}
三、实现录音功能的类
/**
* 实现录音功能的类
*/
public class MediaRecorderUtil {
private MediaRecorder mediarecorder=null;//录音功能公共类
private static final String recordFilePath = FileUtil.getRecorderDir()+"/recorder.aac";
private RecorderThread recorderThread=null;
private static final String UPDATE_TAG="update_tag";
/**
* 开启录音功能
*/
public void recorderStart(){
//启动midiarecoder录音
recorderThread=new RecorderThread();
recorderThread.start();
}
/**
* 停止录音,并保存录音
*/
public void recorderSave(){
if(mediarecorder!=null){
mediarecorder.stop();
mediarecorder.release();
mediarecorder=null;
if(recorderThread!=null){
recorderThread=null;
}
Log.e(UPDATE_TAG,"Thread stop voice and save...");
}
}
//开启录音功能线程
class RecorderThread extends Thread{
@Override
public void run() {
super.run();
try {
//创建保存录音文件
File file=new File(recordFilePath);
if (mediarecorder==null) {
mediarecorder=new MediaRecorder();//实例化录音文件对象
}
mediarecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置获取录音文件来源
mediarecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);//设置录音文件输出格式
mediarecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//设置录音文件的编码格式
mediarecorder.setOutputFile(file.getAbsolutePath());//设置录音文件的输出路径
mediarecorder.prepare();//录音文件的准备工作
mediarecorder.start();//录音开始
Log.e(UPDATE_TAG,"Thread start voice...");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
我项目主要用到的功能实现方法
一、视频剪切主要实现方法
/**
* 视频剪切
* @param startTime 视频剪切的开始时间
* @param endTime 视频剪切的结束时间
* @param FilePath 被剪切视频的路径
* @param WorkingPath 剪切成功保存的视频路径
* @param fileName 剪切成功保存的文件名
*/
private synchronized void cutMp4(final long startTime, final long endTime, final String FilePath, final String WorkingPath, final String fileName){
new Thread(new Runnable() {
@Override
public void run() {
try{
//视频剪切
VideoClip videoClip= new VideoClip();//实例化VideoClip类
videoClip.setFilePath(FilePath);//设置被编辑视频的文件路径 FileUtil.getMediaDir()+"/test/laoma3.mp4"
videoClip.setWorkingPath(WorkingPath);//设置被编辑的视频输出路径 FileUtil.getMediaDir()
videoClip.setStartTime(startTime);//设置剪辑开始的时间
videoClip.setEndTime(endTime);//设置剪辑结束的时间
videoClip.setOutName(fileName);//设置输出的文件名称
videoClip.clip();//调用剪辑并保存视频文件方法(建议作为点击保存时的操作并加入等待对话框)
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
二、开启录音功能
MediaRecorderUtil mrUtil=new MediaRecorderUtil();
mrUtil.recorderStart();
三、音视频合成
//aacPath录音的文件路径、mp4Path原视频路径、outPath合成之后输出的视频文件路径
boolean flag=Mp4ParseUtil.muxAacMp4(aacPath,mp4Path,outPath);
四、视频拼接
//mMp4List视频拼接的路径集合、outPath拼接成功的视频输出路径
Mp4ParseUtil.appendMp4List(mMp4List,outPath);
大致就这样了
我不是个呆若木鸡的小小英