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

mp4parser库

功能介绍https:github.comsanniesmp4parserJavaMP4Parser是一个读取和写入MP4容器的javaapi。直接操作容器而不是对音视频进行编解

功能介绍

https://github.com/sannies/mp4parser

Java MP4 Parser是一个读取和写入MP4容器的java api。直接操作容器而不是对音视频进行编解码。

功能:

  • MP4parser的典型功能如下:
  • 混合音频视频到MP4文件中
  • 合并同样编码设置的MP4文件
  • 增加或者改变MP4文件的metadata
  • 通过省略帧的方式分割MP4文件

例子采用的音频编码格式是H264和AAC,这两种格式对于MP4文件来说非常常见。当然也有AC-3格式的,以及并不常用的H263/MPEG-2视频轨道。

不能把两个编码格式不同的MP4文件进行合并。

项目使用


项目集成

在gradle中添加依赖

compile 'com.googlecode.mp4parser:isoparser:1.1.21'

视频合并

File sdDir;if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {sdDir &#61; Environment.getExternalStorageDirectory();}else{sdDir &#61; Environment.getDataDirectory().getParentFile();}String sdpath &#61; sdDir.getAbsolutePath();//获取指定图片路径String v0 &#61; sdpath &#43; File.separator &#43; "1.mp4";String v1 &#61; sdpath &#43; File.separator &#43; "2.mp4";final ArrayList strings &#61; new ArrayList<>();strings.add(v0);strings.add(v1);final File mergeVideoFile &#61; new File(sdpath &#43; File.separator &#43; "merge.mp4");findViewById(R.id.tv_1).setOnClickListener(new View.OnClickListener() {&#64;Overridepublic void onClick(View view) {String s &#61; Mp4ParserUtils.mergeVideo(strings, mergeVideoFile);Toast.makeText(Mp4PaserActivity.this, "合并成功" &#43; s, Toast.LENGTH_LONG).show();}});

视频分割

final double [] times &#61; {0,3}; //剪切1~3秒final String srcVideoPath &#61; sdpath &#43; File.separator &#43; "merge.mp4";final String resVideoPath &#61; sdpath &#43; File.separator;findViewById(R.id.tv_2).setOnClickListener(new View.OnClickListener() {&#64;Overridepublic void onClick(View view) {try {Mp4ParserUtils.cutVideo(srcVideoPath, resVideoPath, times);Toast.makeText(Mp4PaserActivity.this, "剪切成功", Toast.LENGTH_LONG).show();} catch (IOException e) {}}});

工具类

package com.dianping.test;import android.content.Context;import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.AppendTrack;
import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;public class Mp4ParserUtils {/*** 合并视频** &#64;param videoList* &#64;param mergeVideoFile* &#64;return*/public static String mergeVideo(ArrayList videoList, File mergeVideoFile) {FileOutputStream fos &#61; null;FileChannel fc &#61; null;try {List sourceMovies &#61; new ArrayList<>();for (String video : videoList) {sourceMovies.add(MovieCreator.build(video));}List videoTracks &#61; new LinkedList<>();List audioTracks &#61; new LinkedList<>();for (Movie movie : sourceMovies) {for (Track track : movie.getTracks()) {if ("soun".equals(track.getHandler())) {audioTracks.add(track);}if ("vide".equals(track.getHandler())) {videoTracks.add(track);}}}Movie mergeMovie &#61; new Movie();if (audioTracks.size() > 0) {mergeMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));}if (videoTracks.size() > 0) {mergeMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));}Container out &#61; new DefaultMp4Builder().build(mergeMovie);fos &#61; new FileOutputStream(mergeVideoFile);fc &#61; fos.getChannel();out.writeContainer(fc);fc.close();fos.close();return mergeVideoFile.getAbsolutePath();} catch (Exception e) {e.printStackTrace();} finally {if (fc !&#61; null) {try {fc.close();} catch (IOException e) {e.printStackTrace();}}if (fos !&#61; null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}return null;}/*** 剪切视频* &#64;param srcVideoPath* &#64;param dstVideoPath* &#64;param times* &#64;throws IOException*/public static void cutVideo(String srcVideoPath, String dstVideoPath, double[] times) throws IOException {int dstVideoNumber &#61; times.length / 2;String[] dstVideoPathes &#61; new String[dstVideoNumber];for (int i &#61; 0; i "cutOutput-" &#43; i &#43; ".mp4";}int timesCount &#61; 0;for (int idst &#61; 0; idst //Movie movie &#61; new MovieCreator().build(new RandomAccessFile("/home/sannies/suckerpunch-distantplanet_h1080p/suckerpunch-distantplanet_h1080p.mov", "r").getChannel());Movie movie &#61; MovieCreator.build(srcVideoPath);List tracks &#61; movie.getTracks();movie.setTracks(new LinkedList());// remove all tracks we will create new tracks from the olddouble startTime1 &#61; times[timesCount];double endTime1 &#61; times[timesCount &#43; 1];timesCount &#61; timesCount &#43; 2;boolean timeCorrected &#61; false;// Here we try to find a track that has sync samples. Since we can only start decoding// at such a sample we SHOULD make sure that the start of the new fragment is exactly// such a framefor (Track track : tracks) {if (track.getSyncSamples() !&#61; null && track.getSyncSamples().length > 0) {if (timeCorrected) {// This exception here could be a false positive in case we have multiple tracks// with sync samples at exactly the same positions. E.g. a single movie containing// multiple qualities of the same video (Microsoft Smooth Streaming file)throw new RuntimeException("The startTime has already been corrected by another track with SyncSample. Not Supported.");}startTime1 &#61; correctTimeToSyncSample(track, startTime1, false);endTime1 &#61; correctTimeToSyncSample(track, endTime1, true);timeCorrected &#61; true;}}for (Track track : tracks) {long currentSample &#61; 0;double currentTime &#61; 0;double lastTime &#61; -1;long startSample1 &#61; -1;long endSample1 &#61; -1;for (int i &#61; 0; i long delta &#61; track.getSampleDurations()[i];if (currentTime > lastTime && currentTime <&#61; startTime1) {// current sample is still before the new starttimestartSample1 &#61; currentSample;}if (currentTime > lastTime && currentTime <&#61; endTime1) {// current sample is after the new start time and still before the new endtimeendSample1 &#61; currentSample;}lastTime &#61; currentTime;currentTime &#43;&#61; (double) delta / (double) track.getTrackMetaData().getTimescale();currentSample&#43;&#43;;}//movie.addTrack(new AppendTrack(new ClippedTrack(track, startSample1, endSample1), new ClippedTrack(track, startSample2, endSample2)));movie.addTrack(new CroppedTrack(track, startSample1, endSample1));}long start1 &#61; System.currentTimeMillis();Container out &#61; new DefaultMp4Builder().build(movie);long start2 &#61; System.currentTimeMillis();FileOutputStream fos &#61; new FileOutputStream(String.format(dstVideoPathes[idst]));FileChannel fc &#61; fos.getChannel();out.writeContainer(fc);fc.close();fos.close();long start3 &#61; System.currentTimeMillis();}}private static double correctTimeToSyncSample(Track track, double cutHere, boolean next) {double[] timeOfSyncSamples &#61; new double[track.getSyncSamples().length];long currentSample &#61; 0;double currentTime &#61; 0;for (int i &#61; 0; i long delta &#61; track.getSampleDurations()[i];if (Arrays.binarySearch(track.getSyncSamples(), currentSample &#43; 1) >&#61; 0) {// samples always start with 1 but we start with zero therefore &#43;1timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample &#43; 1)] &#61; currentTime;}currentTime &#43;&#61; (double) delta / (double) track.getTrackMetaData().getTimescale();currentSample&#43;&#43;;}double previous &#61; 0;for (double timeOfSyncSample : timeOfSyncSamples) {if (timeOfSyncSample > cutHere) {if (next) {return timeOfSyncSample;} else {return previous;}}previous &#61; timeOfSyncSample;}return timeOfSyncSamples[timeOfSyncSamples.length - 1];}}

局限性

只支持MP4文件
经过尝试对于一些MP4文件分割不了
功能比较少
目前点评有采用这种方案做视频的合并


推荐阅读
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文详细介绍了 GWT 中 PopupPanel 类的 onKeyDownPreview 方法,提供了多个代码示例及应用场景,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 深入了解 Windows 窗体中的 SplitContainer 控件
    SplitContainer 控件是 Windows 窗体中的一种复合控件,由两个可调整大小的面板和一个可移动的拆分条组成。本文将详细介绍其功能、属性以及如何通过编程方式创建复杂的用户界面。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
  • 资源推荐 | TensorFlow官方中文教程助力英语非母语者学习
    来源:机器之心。本文详细介绍了TensorFlow官方提供的中文版教程和指南,帮助开发者更好地理解和应用这一强大的开源机器学习平台。 ... [详细]
author-avatar
x修者x
無限者:www.wuxianzhe.cn
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有