• Android(java方法)上实现mp4的分割和拼接 (二)


    这节谈一下如何在android上实现mp4文件的高效率切割。

            业务需求举例:把一段2分钟的mp4文件切割出00:42 至 01:16这段时间的视频,要求足够短的执行时间和尽量少的误差。

            分析:mp4Parser只能在关键帧切割,比如,在00:40和00:45分别存在一个可切割关键帧,那么切割视频的头和尾,都应该选择短切割。然后获取到误差的视频短,如果这个误差大于0.5S,用FFmpeg进行一帧一帧编解码切割文件。这样最多会有三段mp4文件,再次将这三段mp4拼接起来就可以了。

            下面直接上关键代码,这些代码在PC上新建一个java工程也可以实现。

            1.切割文件方法:

     /**

    需要使用isoviewer-1.0-RC-27包

    返回值是目标mp4的开头和结尾时刻

     **/       

    1. public static double[] startTrim(File src, File dst, int startMs, int endMs) throws IOException {  
    2.         Movie movie = MovieCreator.build(src.getAbsolutePath());  
    3.         List<Track> tracks = movie.getTracks();  
    4.         movie.setTracks(new LinkedList<Track>());  
    5.         double startTime = startMs/1000;  
    6.         double endTime = endMs/1000;  
    7.         boolean timeCorrected = false;  
    8.         // Here we try to find a track that has sync samples. Since we can only start decoding  
    9.         // at such a sample we SHOULD make sure that the start of the new fragment is exactly  
    10.         // such a frame  
    11.         for (Track track : tracks) {  
    12.             if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {  
    13.                 if (timeCorrected) {            
    14.                     throw new RuntimeException("The startTime has already been corrected by another track with SyncSample. Not Supported.");  
    15.                 }  
    16.                 //true,false表示短截取;false,true表示长截取  
    17.                 startTime = correctTimeToSyncSample(track, startTime, true);  
    18.                 endTime = correctTimeToSyncSample(track, endTime, false);  
    19.                 timeCorrected = true;  
    20.             }  
    21.         }  
    22.         int x = 0;  
    23.         for (Track track : tracks) {  
    24.             long currentSample = 0;  
    25.             double currentTime = 0;  
    26.             long startSample = -1;  
    27.             long endSample = -1;  
    28.             x++;  
    29.             for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {  
    30.                 TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);  
    31.                 for (int j = 0; j < entry.getCount(); j++) {  
    32.                     // entry.getDelta() is the amount of time the current sample covers.  
    33.                     if (currentTime <= startTime) {  
    34.                         // current sample is still before the new starttime  
    35.                         startSample = currentSample;  
    36.                     }  
    37.                     if (currentTime <= endTime) {  
    38.                         // current sample is after the new start time and still before the new endtime  
    39.                         endSample = currentSample;  
    40.                     } else {  
    41.                         // current sample is after the end of the cropped video  
    42.                         break;  
    43.                     }  
    44.                     currentTime += (double) entry.getDelta() / (double) track.getTrackMetaData().getTimescale();  
    45.                     currentSample++;  
    46.                 }  
    47.             }  
    48.             movie.addTrack(new CroppedTrack(track, startSample, endSample));  
    49.             break;  
    50.         }  
    51.         Container container = new DefaultMp4Builder().build(movie);    
    52.         if (!dst.exists()) {  
    53.             dst.createNewFile();  
    54.         }  
    55.    
    56.         FileOutputStream fos = new FileOutputStream(dst);  
    57.         FileChannel fc = fos.getChannel();  
    58.         container.writeContainer(fc);        
    59.         fc.close();  
    60.         fos.close();  
    61.         double[] doubleArray = new double[2] ;  
    62.         doubleArray[0] = startTime;  
    63.         doubleArray[1] = endTime;  
    64.         return doubleArray;  
    65.           
    66.     }  

    2.ffmpeg切割方法,需要jni实现。稍后补充

    1. public String getMp4ByFFmpeg(double mTimeStart,double mTimeEnd,String videoPath){  
    2.     try{  
    3.         String mFinalVideoPath = videoPath;  
    4.         int audioChannels = 2;  
    5.            FFmpegRecorder recorder = new FFmpegRecorder(  
    6.                    mFinalVideoPath, RecorderConfig.TARGET_VIDEO_WIDTH,  
    7.                    RecorderConfig.TARGET_VIDEO_HEIGHT, audioChannels);  
    8.            RecorderConfig.setRecorderConfig(recorder, RecorderConfig.CONFIG_TYPE_MPEG4_HIGH);  
    9.            int totalFrames = 0;  
    10.            FFmpegGrabber grabber = FFmpegGrabber.createDefault(mPath);  
    11.            grabber.setSquareSize(RecorderConfig.TARGET_VIDEO_WIDTH);  
    12.            int degree = VideoFileUtil.getRotate(mPath);  
    13.            grabber.setOrientation(degree);  
    14.            grabber.start();  
    15.            if (mTimeStart > 0) {  
    16.                grabber.setTimestamp((long)mTimeStart);  
    17.            }  
    18.            totalFrames = grabber.getLengthInFrames();  
    19.   
    20.            VideoClip mFinalClip = new VideoClip();  
    21.            mFinalClip.mIsFromLocal = true;  
    22.            mFinalClip.mHeight = RecorderConfig.TARGET_VIDEO_HEIGHT;  
    23.            mFinalClip.mWidth = RecorderConfig.TARGET_VIDEO_WIDTH;   
    24.            recorder.setAudioChannels(grabber.getAudioChannels());  
    25.            recorder.setSampleRate(grabber.getSampleRate());  
    26.            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);  
    27.            recorder.setFrameRate(FFmpegRecorder.DEFAULT_FRAME_RATE);  
    28.   
    29.            recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4);  
    30.            recorder.start();  
    31.            mFinalClip.mOrientation = 0;  
    32.            mFinalClip.mFrameRate = (int) recorder.getFrameRate();  
    33.            mFinalClip.mSampleRate = recorder.getSampleRate();  
    34.            mFinalClip.mAudioBitrate = recorder.getAudioBitrate();  
    35.            mFinalClip.mAudioChannels = recorder.getAudioChannels();  
    36.   
    37.            Frame grabbedFrame = new Frame();  
    38.            int j = 0;  
    39.            boolean videoTimeout = false;  
    40.            boolean audioTimeout = false;  
    41.            while (grabber.grabFrame(grabbedFrame)) {  
    42.                  
    43.                long i = grabber.getTimestamp();  
    44.                long k = grabber.getFrameNumber();  
    45.   
    46.                if (videoTimeout && audioTimeout) {  
    47.                    break;  
    48.                }  
    49.   
    50.                if (grabbedFrame.hasVideoFrame()) {  
    51.                    int progress = 100 * (int) (i - mTimeStart) / mTotalTimeSpan;  
    52.                    publishProgress(progress);  
    53.                }  
    54.   
    55.                if (i > mTimeEnd) {  
    56.                    if (grabbedFrame.hasAudioFrame()) {  
    57.                        audioTimeout = true;  
    58.                    }  
    59.                    if (grabbedFrame.hasVideoFrame()) {  
    60.                        videoTimeout = true;  
    61.                    }  
    62.                    continue;  
    63.                }  
    64.                grabbedFrame.setTimeStamp((long)(i - mTimeStart));  
    65.                recorder.recordFrameNoException(grabbedFrame);  
    66.                SLog.v(TAG, "record image at {}, #{}", i, k);  
    67.                j++;  
    68.            }  
    69.            grabbedFrame.releaseNativeAllocation();  
    70.            grabber.stop();  
    71.            grabber.release();  
    72.   
    73.            recorder.stop();  
    74.            recorder.release();  
    75.   
    76.            mFinalClip.mClipPath = mFinalVideoPath;  
    77.            mFinalClip.mDuration = (long) (MP4ParserUtil.getDuration(mFinalVideoPath) * 1000);  
    78.            mFinalClip.mTargetMills = mFinalClip.mDuration;  
    79.            return mFinalVideoPath;  
    80.        } catch (Exception ex) {  
    81.   
    82.            return null;  
    83.        }  
    84.    }  


    3.拼接三段视频代码

    1. public boolean newClipMethod(String dstFile,String srcFile){  
    2.         try {     
    3.             double[] results = ClipMp4Util.startTrim(new File(dstFile),new File(srcFile),mTimeStart,mTimeEnd);  
    4.             if(results == null){  
    5.                 return false;  
    6.             }  
    7.             Log.d("","newClipMethod-->results[0]-mTimeStart"+results[0]+" "+mTimeStart/1000);  
    8.             Log.d("","newClipMethod-->mTimeEnd-results[1]"+mTimeEnd/1000+" "+results[1]);          
    9.             //下面是短截取然后拼接的逻辑  
    10.               
    11.             if(results[0]-mTimeStart/1000>GAP){  
    12.                 String startMp4 =  <span style="font-family: Arial, Helvetica, sans-serif;">getMp4ByFFmpeg(</span><span style="font-family: Arial, Helvetica, sans-serif;">mTimeStart,results[0]*1000,begin);</span>  
    13.             }  
    14.               
    15.             if(mTimeEnd/1000-results[1]>GAP){  
    16.                 String endMp4 =  <span style="font-family: Arial, Helvetica, sans-serif;">getMp4ByCode(</span><span style="font-family: Arial, Helvetica, sans-serif;">results[1]*吧1000,mTimeEnd,end);</span>  
    17.             }  
    18.               
    19.             String[] videos = new String[3];  
    20.             videos[0] = begin;  
    21.             videos[1] = dst;  
    22.             videos[2] = end;  
    23.             appendVideo(videos);  
    24.               
    25.         } catch (Exception e) {  
    26.             //如果不是同一格式的视频,这里合成会报错,直接返回中间视频.所以长视频选取长误差的方式,前后都多截取一段  
    27.             Log.d("","new Method exception-->"+e);  
    28.             e.printStackTrace();  
    29.         }  
    30.         return true;  
    31.     }  

    相关工程后续会上传。
             1.点击下载工程1

     
  • 相关阅读:
    C#中的委托(转)
    面试总结
    int, Int32.Parse和Convert.ToInt32的不同之处(分享)
    数据结构与算法基础学习(一)
    SVN服务器搭建和使用(二)
    WCF学习笔记(第一天,1.WCF概述)
    在此计算机中仅有部分visual studio2010产品已升级到SP1,只有全部升级,产品才能正常运行解决办法
    FormsAuthentication使用指南
    Why do I get the error "The target GatherAllFilesToPublish does not exist"?
    非彼拉且数列的实现
  • 原文地址:https://www.cnblogs.com/android-blogs/p/5711171.html
Copyright © 2020-2023  润新知