日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android实现视频剪切、视频拼接以及音视频合并

發布時間:2024/1/1 Android 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android实现视频剪切、视频拼接以及音视频合并 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
因公司項目有需求,要實現視頻剪切,視頻拼接以及音視頻合并的功能,自己通過在網上查找大量的資料終于把功能實現了,把實現的公共類提取出來,以便以后復習鞏固。

使用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<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);

大致就這樣了





我不是個呆若木雞的小小英

總結

以上是生活随笔為你收集整理的Android实现视频剪切、视频拼接以及音视频合并的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。