免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4231 | 回复: 2

[Android] Android(java方法)上实现mp4的分割和拼接 [复制链接]

论坛徽章:
80
20周年集字徽章-庆
日期:2020-10-28 14:09:1215-16赛季CBA联赛之北京
日期:2020-10-28 13:32:5315-16赛季CBA联赛之北控
日期:2020-10-28 13:32:4815-16赛季CBA联赛之天津
日期:2020-10-28 13:13:35黑曼巴
日期:2020-10-28 12:29:1520周年集字徽章-周	
日期:2020-10-31 15:10:0720周年集字徽章-20	
日期:2020-10-31 15:10:07ChinaUnix元老
日期:2015-09-29 11:56:3020周年集字徽章-年
日期:2020-10-28 14:14:56
发表于 2015-10-28 13:56 |显示全部楼层
最近正在处理android上的mp4切割问题。学习了很多mp4的知识,mp4文件按照编码类型,分为mpeg-4,avc这两种;这两种类型的mp4在后面的处理中会有不同的地方。
            在android系统下,MP4的分割大致有三种可选择的方法,(1)FFmpeg框架,对视频进行一帧一帧解码编码处理。(2)第三方java开源库,比如优秀的mp4parser。(3)Android4.1中新加入的api:MediaCodec。
            以上三种方法都有自己的优劣势,简单介绍:
           (1)FFmpeg,需要做jni实现。编码解码可以裁剪任意长度视频的任意中间长度区间,支持对视频加入水印,配乐,滤镜处理;支持裁剪屏幕尺寸。最致命的缺点是:
             效率低的难以忍受,大概是1s的视频1.5S的处理时间,如果Android应用中这样处理让用户等待完全找死行为。
             (2)mp4Parser。有非常详细的资料和demo可以学习,是一个大牛的个人作品。但是它对视频的裁剪处理非常粗糙,只能从关键帧进行裁剪。经过我测试:mpeg-4格式的mp4文件,大概2S-10S左右会有一个关键帧;avc格式的mp4文件大概0.3S有一个关键帧。
                很大的有点是运行效率非常高,可以忽略时间成本。
               但是这样会引入很明显的问题,如果你的业务需求需要准确截取,无论你是选择上一个节点还是下一个,头尾大概都会引入平均3-4S的长度误差。这个误差有时难以忍受。
              (3)mediaCodec是Android4.1才引入的。如果以来这个Lib进行操作,会导致4.1之前的版本无法使用。另外,需要说明,这个api以来很多C层和硬件层的实现,不要试图将所有代码分隔出来加到自己的项目中。
                下篇给出一个可行的方案来解决android上的mp4切割问题。

论坛徽章:
80
20周年集字徽章-庆
日期:2020-10-28 14:09:1215-16赛季CBA联赛之北京
日期:2020-10-28 13:32:5315-16赛季CBA联赛之北控
日期:2020-10-28 13:32:4815-16赛季CBA联赛之天津
日期:2020-10-28 13:13:35黑曼巴
日期:2020-10-28 12:29:1520周年集字徽章-周	
日期:2020-10-31 15:10:0720周年集字徽章-20	
日期:2020-10-31 15:10:07ChinaUnix元老
日期:2015-09-29 11:56:3020周年集字徽章-年
日期:2020-10-28 14:14:56
发表于 2015-10-28 13:57 |显示全部楼层
这节谈一下如何在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.     }  
复制代码

论坛徽章:
80
20周年集字徽章-庆
日期:2020-10-28 14:09:1215-16赛季CBA联赛之北京
日期:2020-10-28 13:32:5315-16赛季CBA联赛之北控
日期:2020-10-28 13:32:4815-16赛季CBA联赛之天津
日期:2020-10-28 13:13:35黑曼巴
日期:2020-10-28 12:29:1520周年集字徽章-周	
日期:2020-10-31 15:10:0720周年集字徽章-20	
日期:2020-10-31 15:10:07ChinaUnix元老
日期:2015-09-29 11:56:3020周年集字徽章-年
日期:2020-10-28 14:14:56
发表于 2015-10-28 13:57 |显示全部楼层
2.拼接三段视频代码

public boolean newClipMethod(String dstFile,String srcFile){  
        try {     
            double[] results = ClipMp4Util.startTrim(new File(dstFile),new File(srcFile),mTimeStart,mTimeEnd);  
            if(results == null){  
                return false;  
            }  
            Log.d("","newClipMethod-->results[0]-mTimeStart"+results[0]+" "+mTimeStart/1000);  
            Log.d("","newClipMethod-->mTimeEnd-results[1]"+mTimeEnd/1000+" "+results[1]);         
            //下面是短截取然后拼接的逻辑  
              
            if(results[0]-mTimeStart/1000>GAP){  
                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>  
            }  
              
            if(mTimeEnd/1000-results[1]>GAP){  
                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>  
            }  
              
            String[] videos = new String[3];  
            videos[0] = begin;  
            videos[1] = dst;  
            videos[2] = end;  
            appendVideo(videos);  
              
        } catch (Exception e) {  
            //如果不是同一格式的视频,这里合成会报错,直接返回中间视频.所以长视频选取长误差的方式,前后都多截取一段  
            Log.d("","new Method exception-->"+e);  
            e.printStackTrace();  
        }  
        return true;  
    }  
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP