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

2023-11-02

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<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实现。稍后补充

[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 =  <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.     }  
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • UncaughtExceptionHandler加Process 让应用程序不异常崩溃退出

    我们在开发应用程序时难免会遇到出现没有被try catch抓住的RunTimeException信息 从而导致程序异常崩溃退出 大大的影响了用户体验 那么有没有什么方法能避免这一现象呢 网上一查资料 果然有 UncaughtExceptio
  • ViewPager 详解(二)---详解四大函数

    http blog csdn net harvic880925 article details 38487149
  • 【Android】WebView控件最全使用解析

    WebView控件最全使用解析 一 WebView 概述 二 WebView使用基础篇 2 1添加方式 2 2 加载远程网页 2 3 加载本地网页 2 4 加载HTML片段 2 5 WebView 常用方法 三 WebView 进阶篇 3
  • Android开发基础 -- android studio 使用第三方模拟器连接方法,如海马玩模拟器

    安装完模拟器后 要使用adb命令Android studio才能识别出来 打开cmd 输入 adb connect 127 0 0 1 26944 如下 海马玩模拟器的端口号是26944 逍遥安卓模拟器的端口号是21503 夜神玩模拟器的端
  • 源码环境下添加系统Service流程

    关于系统服务的分析 以及如何实现添加系统服务 分析详细跳转链接 Android系统服务 SystemService 简介 添加系统Service涉及的文件 修改文件 Android mk api current txt api system
  • Android Studio连接夜神模拟器

    Android Studio连接夜神模拟器 一 下载夜神模拟器 二 夜神模拟器连接Android Studio 三 其他操作 3 1 屏幕旋转 3 2 创建其他模拟器 一 下载夜神模拟器 官网 https www yeshen com 下载
  • Android中定时执行任务的3种实现方法

    在Android开发中 定时执行任务的3种实现方法 一 采用Handler与线程的sleep long 方法 不建议使用 java的实现方式 二 采用Handler的postDelayed Runnable long 方法 最简单的andr
  • Android开发——菜单(Menu)-——选项菜单(OptionMenu)

    Menu 在Android3 0以前的menu显示 是用户点击手机下方操作按钮的菜单按钮时 会从界面底部向上弹出菜单 菜单内容出现在屏幕底部 可以包含六个及以上的菜单项 超出的部分则以 更多 来显示 在Android3 0以后的更高版本的系
  • 『Android Studio』用Fragment实现一个简易新闻浏览界面

    Fragment意思为碎片 片段 在Android中有些Activity在手机上看起来很美观 但放在屏幕更大的平板类的设备上 可能就不一样了 而Fragment能在一个Activity中内嵌多个独立的小Activity 有效的解决了app在
  • Android Service最全面的解析

    本篇文章再次来自 刘明渊 话说刘明渊已经是我公众号的老熟人了 这是第三次发表他投稿的文章 前两篇关于Intent的译文都广受大家好评 而本篇对于Service的译文同样精彩 其实像这种官方文档翻译类文章的投稿我都是非常欢迎的 因为官方文档的
  • 谈谈App混合开发

    混合开发的App Hybrid App 就是在一个App中内嵌一个轻量级的浏览器 一部分原生的功能改为Html 5来开发 这部分功能不仅能够在不升级App的情况下动态更新 而且可以在Android或iOS的App上同时运行 让用户的体验更好
  • Android基础-Service和IntentService知识点详细总结

    Service 对于广大的Android开发者来说算是耳熟能详了 作为Android的四大组件之一 在我们的开发中也起着重要的作用 在Android面试中 Service相关的问题也是面试官问得比较多的 当别人问你 Service 到底是什
  • hint和输入都是在EditText的左上角

    2012 8 8 09 59 55 上传 下载附件 2 16 KB 如何实现这种效果 hint和输入都是在EditText的左上角 android gravity top left
  • 实现一个Android锁屏App的难点总结

    http blog csdn net ldld1717 article details 69389125 https segmentfault com a 1190000007157971 自定义一个漂亮实用的锁屏app 如果能赢得用户的认
  • adb logcat命令查看并过滤android输出log

    http blog csdn net hansel article details 38088583 cmd命令行中使用adb logcat命令查看Android系统和应用的log dos窗口按ctrl c中断输出log记录 logcat日
  • 使用ADB指令永久隐藏或禁用状态栏和虚拟按键

    原理是强大的ADB命令 1 手机开启开发者模式 允许ADB调试 2 电脑上下载ADB包 adb rar 529 77 KB 下载次数 7681 3 解压到任意位置 比如c adb 4 cmd命令定位到adb目录 比如 cd c adb 5
  • Android Socket 简单介绍

    文章目录 前言 一 Socket是什么 百度百科的解释 我自己的理解 二 简单示例 1 服务端 2 客户端 3 布局 4 实现 参考 总结 前言 最近需求需要使用Socket进行通讯 我在工作后的安卓开发中没有接触过 所以有了这篇文章 写的
  • ndk错误总结

    1 ndk Unresolved inclusion
  • 【Android】相对布局(RelativeLayout)最全解析

    Android 相对布局 RelativeLayout 最全解析 一 相对布局 RelativeLayout 概述 二 根据父容器定位 三 根据兄弟控件定位 一 相对布局 RelativeLayout 概述 相对布局 RelativeLay
  • Android RecyclerView最全使用详解

    本文目录 RecyclerView概述 RecyclerView使用 基础篇 第一步 添加RecyclerView 第二步 添加布局文件 第三步 添加逻辑代码 运行效果 RecyclerView使用 进阶篇 布局管理器 线性布局管理器 网格

随机推荐

  • SpringBoot + MyBatis 结合 MVC框架设计 第1关:项目整合 - SpringBoot + MyBatis

    目录 任务描述 相关知识 使用MyBatis Spring Boot Starter进行整合SpringBoot MyBatis 使用SpringBoot MyBatis编写一个查询用户信息的接口 编程要求 测试说明 参考代码 任务描述 本
  • PCL 4PCS算法实现点云配准

    4PCS算法 一 算法原理 1 算法流程 2 参考文献 二 代码实现 1 主要参数 2 完整代码 三 结果展示 四 相关链接 一 算法原理 1 算法流程 4PCS算法是计算机图形学中一种流行的配准工具 给定两个点集 P Q P Q
  • Android系统运动传感器

    转自 https blog csdn net liang123l article details 53992197 Android平台提供了多种感应器 让你监控设备的运动 传感器的架构因传感器类型而异 重力 线性加速度 旋转矢量 重要运动
  • Windows 10 安装安卓子系统 WSA(Magisk/KernelSU)使用 WSA 工具箱安装 APK

    from https blog zhjh top archives XokySA7Rc1pkVvnxAEP5E 前提是系统为 Windows 10 22H2 10 0 19045 2311 或更高版本 尽量新 步骤 使用 WSAPatch
  • android真机和模拟器(emulator)的判断

    最近收到领导需求要判断真机和模拟器 先前项目里是有的 可能当时能用 但现在都不能用了 然后 baidu上能够找到的其实都不能用了 包括说使用cache来区分cpu架构是哈佛结构还是冯诺伊曼结构来判断的 这个其实是最不靠谱的 因为硬件结构是会
  • C语言函数大全-- p 开头的函数

    p 开头的函数 1 perror 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 pieslice 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 pow powf powl 3 1 函数说明 3 2 演示示例 3 3
  • 数据结构-冒泡排序,选择排序,插入排序,快速排序,希尔排序,堆排序

    冒泡排序 冒泡排序的思想 从头开始数据两两比较 将大的放到后面小的放到前面 经过一轮比较后就找到了该序列的最大数且将它放到了最后 再循环上述步骤找出第二大的数 第三大的数 int maoapo int a int len a为数组的首地址
  • 期货开户顺大市而逆小市

    期货的行情 有人愿意以更高的价来买入 就会涨 有人买意以更低的价格卖出 就会跌 现货市场上 一个馒头5角钱的时候 在期货市场上 如果有很多人争着买 这个馒头可能会涨到5块 或者50块 也是可能的 在这个馒头5块钱一个的时候 你感觉这个馒头太
  • ShiroFilter设计原理与实现

    Shiro提供了与Web集成的支持 其通过一个ShiroFilter入口来拦截需要安全控制的URL 然后进行相应的控制 ShiroFilter类似于如Strut2 SpringMVC这种web框架的前端控制器 其是安全控制的入口点 其负责读
  • Postgre 还原导入sql文件

    postgresql 如何导入sql文件 打开sql shell 执行如下操作 密码不显示 直接输入完成后按回车键 i C Users fulong Desktop trest3 sql 注意路径不要使用 不支持这种写法
  • Linux Kernel SMP (Symmetric Multi-Processors) 開機流程解析 Part(3) Linux 多核心啟動流程從rest_init到kernel_init與CPU

    http loda hala01 com 2011 08 android E7 AD 86 E8 A8 98 linux kernel smp symmetric multi processors E9 96 8B E6 A9 9F E6
  • Java-IO流篇-DataOutputStream

    DataOutputStream DataOutputStreams是OutputStream的子类 是数据输出流 此类继承自FillterOutputStream类 同时实现DataOutput接口 在DataOutput接口定义了一系列
  • 更新k8s证书(续签)

    下载 kubeadm x86 md5 7951a9348655b4f508b84ced66fcf371kubeadm arm md5 b11c4ce93722b07f96c2acdeaaa07e74 cd etc kubernetes cp
  • iframe的基本介绍与使用

    一 介绍 iframe 内嵌框架 是 HTML 中一种用于将一个网页嵌入到另一个网页中的标签 它可以在一个页面中显示来自其他页面的内容 在网页中 使用标签可以将一个网页嵌套在另一个网页中 实现网页间的互联互通 二 使用 标签的基本用法如下
  • MapReduce过程中setPartitionerClass、setSortComparatorClass和setGroupingComparatorClass三者关系

    Map首先将输出写到环形缓存当中 开始spill过程 job setPartitionerClass PartitionClass class 按key分区 map阶段最后调用 对key取hash值 或其它处理 指定进入哪一个reduce
  • 【测试开发】Junit 框架

    目录 一 认识 Junit 二 Junit 的常用注解 1 Test 2 Disabled 3 BeforeAll 4 AfterAll 5 BeforeEach 6 AfterEach 7 执行测试 三 参数化 1 引入依赖 2 单参数
  • 分析key原理

    总结 key是虚拟dom对象的标识 当数据发生变化时 vue会根据新数据生成新的虚拟dom 随后vue进行新虚拟dom与旧虚拟dom的差异比较 比较规则 旧虚拟dom中找到了与新虚拟dom相同的key 若虚拟dom中的内容没变 直接使用之前
  • 将一列具有相同数据的行合并到同一行

    如何将第一列具有相同数据的行合并到同一行 但要保护重复内容 将重复内容依次填充到重复行中第一行后面 首列相同的 将后面对应列各单元格内容合并到重复行中第一行后对应的单元格内 并且用 连接 对应列只有一个单元格有内容 则不添加 符号 若为空
  • 【sql】mysql索引问题笔记

    q 使用了索引就会有优化 a 然而并不是这样 一下情况都是没有作用的 1 索引字段并没有在查询条件中使用 2 条件查询的过滤结果占比过多 既索引字段为可重复的字段 常固发生此情况 3 对小表查询 此处指索引建立在小表上 联查到数据多的表的时
  • Android(java方法)上实现mp4的分割和拼接 (二)

    http blog csdn net banking17173 article details 20646251 这节谈一下如何在Android上实现mp4文件的高效率切割 业务需求举例 把一段2分钟的mp4文件切割出00 42 至 01