赞
踩
一、视频处理工具类
/** * 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<String> mp4PathList, String outPutPath){ try { List<Movie> mp4MovieList = new ArrayList<>();// Movie对象集合[输入] for (String mp4Path : mp4PathList) {// 将每个文件路径都构建成一个Movie对象 mp4MovieList.add(MovieCreator.build(mp4Path)); } List<Track> audioTracks = new LinkedList<>();// 音频通道集合 List<Track> videoTracks = new LinkedList<>();// 视频通道集合 for (Movie mp4Movie : mp4MovieList) {// 对Movie对象集合进行循环 for (Track inMovieTrack : mp4Movie.getTracks()) { if ("soun".equals(inMovieTrack.getHandler())) {// 从Movie对象中取出音频通道 audioTracks.add(inMovieTrack); } if ("vide".equals(inMovieTrack.getHandler())) {// 从Movie对象中取出视频通道 videoTracks.add(inMovieTrack); } } } Movie resultMovie = new Movie();// 结果Movie对象[输出] if (!audioTracks.isEmpty()) {// 将所有音频通道追加合并 resultMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()]))); } if (!videoTracks.isEmpty()) {// 将所有视频通道追加合并 resultMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()]))); } Container outContainer = new DefaultMp4Builder().build(resultMovie);// 将结果Movie对象封装进容器 FileChannel fileChannel = new RandomAccessFile(String.format(outPutPath), "rw").getChannel(); outContainer.writeContainer(fileChannel);// 将容器内容写入磁盘 fileChannel.close(); }catch(Exception e){ e.printStackTrace(); } } /** * 对AAC文件集合进行追加合并(按照顺序一个一个拼接起来) * * @param aacPathList [输入]AAC文件路径的集合(不支持wav) * @param outPutPath [输出]结果文件全部名称包含后缀(比如.aac) * @throws IOException 格式不支持等情况抛出异常 */ public static void appendAacList(List<String> aacPathList, String outPutPath){ try{ List<Track> audioTracks = new LinkedList<>();// 音频通道集合 for (int i = 0; i < aacPathList.size(); i++) {// 将每个文件路径都构建成一个AACTrackImpl对象 audioTracks.add(new AACTrackImpl(new FileDataSourceImpl(aacPathList.get(i)))); } Movie resultMovie = new Movie();// 结果Movie对象[输出] if (!audioTracks.isEmpty()) {// 将所有音频通道追加合并 resultMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()]))); } Container outContainer = new DefaultMp4Builder().build(resultMovie);// 将结果Movie对象封装进容器 FileChannel fileChannel = new RandomAccessFile(String.format(outPutPath), "rw").getChannel(); outContainer.writeContainer(fileChannel);// 将容器内容写入磁盘 fileChannel.close(); }catch (Exception e){ e.printStackTrace(); } } private static List<Movie> moviesList = new ArrayList<>(); private static List<Track> videoTracks = new ArrayList<>(); private static List<Track> audioTracks = new ArrayList<>(); //将两个mp4视频进行拼接 public static void appendMp4(List<String> mMp4List,String outputpath){ try { for (int i=0;i<mMp4List.size();i++) { Movie movie=MovieCreator.build(mMp4List.get(i)); moviesList.add(movie); } } catch (IOException e) { e.printStackTrace(); } for (Movie m : moviesList) { for (Track t : m.getTracks()) { if (t.getHandler().equals("soun")) { audioTracks.add(t); } if (t.getHandler().equals("vide")) { videoTracks.add(t); } } } Movie result = new Movie(); try { if (audioTracks.size() > 0) { result.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()]))); } if (videoTracks.size() > 0) { result.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()]))); } } catch (IOException e) { e.printStackTrace(); } Container out = new DefaultMp4Builder().build(result); try { FileChannel fc = new FileOutputStream(new File(outputpath)).getChannel(); out.writeContainer(fc); fc.close(); } catch (Exception e) { e.printStackTrace(); } moviesList.clear(); } /** * 将 AAC 和 MP4 进行混合[替换了视频的音轨] * * @param aacPath .aac * @param mp4Path .mp4 * @param outPath .mp4 */ public static boolean muxAacMp4(String aacPath, String mp4Path, String outPath) { boolean flag=false; 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(); flag=true; Log.e("update_tag","merge finish"); } catch (Exception e) { e.printStackTrace(); flag=false; } return flag; } /** * 将 M4A 和 MP4 进行混合[替换了视频的音轨] * * @param m4aPath .m4a[同样可以使用.mp4] * @param mp4Path .mp4 * @param outPath .mp4 */ public static void muxM4AMp4(String m4aPath, String mp4Path, String outPath) throws IOException { Movie audioMovie = MovieCreator.build(m4aPath); Track audioTracks = null;// 获取视频的单纯音频部分 for (Track audioMovieTrack : audioMovie.getTracks()) { if ("soun".equals(audioMovieTrack.getHandler())) { audioTracks = audioMovieTrack; } } 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(audioTracks);// 音频部分 Container out = new DefaultMp4Builder().build(resultMovie); FileOutputStream fos = new FileOutputStream(new File(outPath)); out.writeContainer(fos.getChannel()); fos.close(); } /** * 分离mp4视频的音频部分,只保留视频部分 * * @param mp4Path .mp4 * @param outPath .mp4 */ public static void splitMp4(String mp4Path, String outPath){ try{ 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);// 视频部分 Container out = new DefaultMp4Builder().build(resultMovie); FileOutputStream fos = new FileOutputStream(new File(outPath)); out.writeContainer(fos.getChannel()); fos.close(); }catch (Exception e){ e.printStackTrace(); } } /** * 分离mp4的视频部分,只保留音频部分 * * @param mp4Path .mp4 * @param outPath .aac */ public static void splitAac(String mp4Path, String outPath){ try{ Movie videoMovie = MovieCreator.build(mp4Path); Track videoTracks = null;// 获取音频的单纯视频部分 for (Track videoMovieTrack : videoMovie.getTracks()) { if ("soun".equals(videoMovieTrack.getHandler())) { videoTracks = videoMovieTrack; } } Movie resultMovie = new Movie(); resultMovie.addTrack(videoTracks);// 音频部分 Container out = new DefaultMp4Builder().build(resultMovie); FileOutputStream fos = new FileOutputStream(new File(outPath)); out.writeContainer(fos.getChannel()); fos.close(); }catch (Exception e){ e.printStackTrace(); } } /** * 分离mp4视频的音频部分,只保留视频部分 * * @param mp4Path .mp4 * @param mp4OutPath mp4视频输出路径 * @param aacOutPath aac视频输出路径 */ public static void splitVideo(String mp4Path, String mp4OutPath,String aacOutPath){ try{ Movie videoMovie = MovieCreator.build(mp4Path); Track videTracks = null;// 获取视频的单纯视频部分 Track sounTracks = null;// 获取视频的单纯音频部分 for (Track videoMovieTrack : videoMovie.getTracks()) { if ("vide".equals(videoMovieTrack.getHandler())) { videTracks = videoMovieTrack; } if ("soun".equals(videoMovieTrack.getHandler())) { sounTracks = videoMovieTrack; } } Movie videMovie = new Movie(); videMovie.addTrack(videTracks);// 视频部分 Movie sounMovie = new Movie(); sounMovie.addTrack(sounTracks);// 音频部分 // 视频部分 Container videout = new DefaultMp4Builder().build(videMovie); FileOutputStream videfos = new FileOutputStream(new File(mp4OutPath)); videout.writeContainer(videfos.getChannel()); videfos.close(); // 音频部分 Container sounout = new DefaultMp4Builder().build(sounMovie); FileOutputStream sounfos = new FileOutputStream(new File(aacOutPath)); sounout.writeContainer(sounfos.getChannel()); sounfos.close(); }catch (Exception e){ e.printStackTrace(); } } /** * 对 Mp4 添加字幕 * * @param mp4Path .mp4 添加字幕之前 * @param outPath .mp4 添加字幕之后 */ public static void addSubtitles(String mp4Path, String outPath) throws IOException { Movie videoMovie = MovieCreator.build(mp4Path); TextTrackImpl subTitleEng = new TextTrackImpl();// 实例化文本通道对象 subTitleEng.getTrackMetaData().setLanguage("eng");// 设置元数据(语言) subTitleEng.getSubs().add(new TextTrackImpl.Line(0, 1000, "Five"));// 参数时间毫秒值 subTitleEng.getSubs().add(new TextTrackImpl.Line(1000, 2000, "Four")); subTitleEng.getSubs().add(new TextTrackImpl.Line(2000, 3000, "Three")); subTitleEng.getSubs().add(new TextTrackImpl.Line(3000, 4000, "Two")); subTitleEng.getSubs().add(new TextTrackImpl.Line(4000, 5000, "one")); subTitleEng.getSubs().add(new TextTrackImpl.Line(5001, 5002, " "));// 省略去测试 videoMovie.addTrack(subTitleEng);// 将字幕通道添加进视频Movie对象中 Container out = new DefaultMp4Builder().build(videoMovie); FileOutputStream fos = new FileOutputStream(new File(outPath)); out.writeContainer(fos.getChannel()); fos.close(); } /** * 将 MP4 切割 * * @param mp4Path .mp4 * @param fromSample 起始位置 不是传入的秒数 * @param toSample 结束位置 不是传入的秒数 * @param outPath .mp4 */ public static void cropMp4(String mp4Path, long fromSample, long toSample, String outPath){ try{ Movie mp4Movie = MovieCreator.build(mp4Path); Track videoTracks = null;// 获取视频的单纯视频部分 for (Track videoMovieTrack : mp4Movie.getTracks()) { if ("vide".equals(videoMovieTrack.getHandler())) { videoTracks = videoMovieTrack; } } Track audioTracks = null;// 获取视频的单纯音频部分 for (Track audioMovieTrack : mp4Movie.getTracks()) { if ("soun".equals(audioMovieTrack.getHandler())) { audioTracks = audioMovieTrack; } } Movie resultMovie = new Movie(); resultMovie.addTrack(new AppendTrack(new CroppedTrack(videoTracks, fromSample, toSample)));// 视频部分 resultMovie.addTrack(new AppendTrack(new CroppedTrack(audioTracks, fromSample, toSample)));// 音频部分 Container out = new DefaultMp4Builder().build(resultMovie); FileOutputStream fos = new FileOutputStream(new File(outPath)); out.writeContainer(fos.getChannel()); fos.close(); }catch(Exception e){ e.printStackTrace(); } } }
二、视频剪切工具类
/** * 视频剪切 * 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<Track> tracks = movie.getTracks(); movie.setTracks(new LinkedList<Track>()); //时间是否修正 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);
大致就这样了
我不是个呆若木鸡的小小英
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。