热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

http:blog.csdn.netbanking17173articledetails20646251这节谈一下如何在Android上实现mp4文件的高效率切割。业务需求

http://blog.csdn.net/banking17173/article/details/20646251

这节谈一下如何在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的开头和结尾时刻

 **/       

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public static double[] startTrim(File src, File dst, int startMs, int endMs) throws IOException {  
  2.         Movie movie = MovieCreator.build(src.getAbsolutePath());  
  3.         List tracks = movie.getTracks();  
  4.         movie.setTracks(new LinkedList());  
  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实现。稍后补充

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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.拼接三段视频代码

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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 =  "font-family: Arial, Helvetica, sans-serif;">getMp4ByFFmpeg("font-family: Arial, Helvetica, sans-serif;">mTimeStart,results[0]*1000,begin);  
  13.             }  
  14.               
  15.             if(mTimeEnd/1000-results[1]>GAP){  
  16.                 String endMp4 =  "font-family: Arial, Helvetica, sans-serif;">getMp4ByCode("font-family: Arial, Helvetica, sans-serif;">results[1]*吧1000,mTimeEnd,end);  
  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.     }  

推荐阅读
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 本文介绍了一种自定义的Android圆形进度条视图,支持在进度条上显示数字,并在圆心位置展示文字内容。通过自定义绘图和组件组合的方式实现,详细展示了自定义View的开发流程和关键技术点。示例代码和效果展示将在文章末尾提供。 ... [详细]
  • 【问题】在Android开发中,当为EditText添加TextWatcher并实现onTextChanged方法时,会遇到一个问题:即使只对EditText进行一次修改(例如使用删除键删除一个字符),该方法也会被频繁触发。这不仅影响性能,还可能导致逻辑错误。本文将探讨这一问题的原因,并提供有效的解决方案,包括使用Handler或计时器来限制方法的调用频率,以及通过自定义TextWatcher来优化事件处理,从而提高应用的稳定性和用户体验。 ... [详细]
  • 在《ChartData类详解》一文中,我们将深入探讨 MPAndroidChart 中的 ChartData 类。本文将详细介绍如何设置图表颜色(Setting Colors)以及如何格式化数据值(Formatting Data Values),通过 ValueFormatter 的使用来提升图表的可读性和美观度。此外,我们还将介绍一些高级配置选项,帮助开发者更好地定制和优化图表展示效果。 ... [详细]
  • 在使用 Qt 进行 YUV420 图像渲染时,由于 Qt 本身不支持直接绘制 YUV 数据,因此需要借助 QOpenGLWidget 和 OpenGL 技术来实现。通过继承 QOpenGLWidget 类并重写其绘图方法,可以利用 GPU 的高效渲染能力,实现高质量的 YUV420 图像显示。此外,这种方法还能显著提高图像处理的性能和流畅性。 ... [详细]
  • 在C#编程中,设计流畅的用户界面是一项重要的任务。本文分享了实现Fluent界面设计的技巧与方法,特别是通过编写领域特定语言(DSL)来简化字符串操作。我们探讨了如何在不使用`+`符号的情况下,通过方法链式调用来组合字符串,从而提高代码的可读性和维护性。文章还介绍了如何利用静态方法和扩展方法来实现这一目标,并提供了一些实用的示例代码。 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • Unity3D 中 AsyncOperation 实现异步场景加载及进度显示优化技巧
    在Unity3D中,通过使用`AsyncOperation`可以实现高效的异步场景加载,并结合进度条显示来提升用户体验。本文详细介绍了如何利用`AsyncOperation`进行异步加载,并提供了优化技巧,包括进度条的动态更新和加载过程中的性能优化方法。此外,还探讨了如何处理加载过程中可能出现的异常情况,确保加载过程的稳定性和可靠性。 ... [详细]
  • 在编译 PHP7 的 PDO MySQL 扩展时,可能会遇到 `[mysql_driver.lo]` 错误 1。该问题通常出现在 `pdo_mysql_fetch_error_func` 函数中。本文详细介绍了导致这一错误的常见原因,包括依赖库版本不匹配、编译选项设置不当等,并提供了具体的解决步骤和调试方法,帮助开发者快速定位并解决问题。 ... [详细]
  • 提升视觉效果:Unity3D中的HDR与Bloom技术(高动态范围成像与光线散射)
    提升视觉效果:Unity3D中的HDR与Bloom技术(高动态范围成像与光线散射) ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
  • 本文深入解析了WCF Binding模型中的绑定元素,详细介绍了信道、信道管理器、信道监听器和信道工厂的概念与作用。从对象创建的角度来看,信道管理器负责信道的生成。具体而言,客户端的信道通过信道工厂进行实例化,而服务端则通过信道监听器来接收请求。文章还探讨了这些组件之间的交互机制及其在WCF通信中的重要性。 ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • 深入理解Java中的多态性概念及其应用
    多态是面向对象编程中的三大核心特性之一,与封装和继承共同构成了面向对象的基础。多态使得代码更加灵活和可扩展,封装和继承则为其提供了必要的支持。本文将深入探讨多态的概念及其在Java中的具体应用,帮助读者全面理解和掌握这一关键知识点。 ... [详细]
author-avatar
吕兵涛_836
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有