Xuggler教程:转码和媒体修改

注意:这是我们的“ Xuggler开发教程 ”系列的一部分。

在之前的教程中,我对视频处理Xuggler进行了简短介绍 。 在这一部分中,我们将看到Xuggler和FFmpeg提供的一些更令人兴奋的功能,例如视频转码和媒体修改。 不要忘记Xuggler是一个Java库,可用于实时解压缩,处理和压缩录制的视频或实时视频。

Xuggler提供了两种不同的编程API,可用于同一目的。 首先,我们有MediaTool API :

MediaTool是一个简单的应用程序编程接口(API),用于对Java中的视频进行解码,编码和修改。 MediaTool隐藏了许多容器,编解码器和其他内容的细节,因此您可以专注于媒体而不是工具。 就是说,MediaTool仍然提供对基础Xuggler对象的访问,因此,如果需要,您可以进行精细的控制。

还有Xuggler Advanced API ,它使您可以深入研究视频操作的细节,但又增加了一层复杂性。

首先,我们将使用MediaTool API ,在随后的教程中,我们还将处理Advanced API。

让我们开始将媒体从一种格式转码为另一种格式。 代码转换是一种编码到另一种编码的直接数模转换。 通常在目标设备不支持格式或存储容量有限(要求减小文件大小)或将不兼容或过时的数据转换为更好支持的格式或现代格式的情况下执行此操作。 转码通常是一个有损过程 ,其中“有损”压缩是一种数据编码方法,为了达到其目标,它会丢弃(丢失)某些数据,结果是,对数据进行解压缩会产生与原始数据不同的内容,尽管足够相似,以某种方式有用。

让我们看一些用于转码的高级代码,稍后我将详细解释。

package com.javacodegeeks.xuggler;import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.IMediaViewer;
import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;public class TranscodingExample {private static final String inputFilename = "c:/myvideo.mp4";private static final String outputFilename = "c:/myvideo.flv";public static void main(String[] args) {// create a media readerIMediaReader mediaReader = ToolFactory.makeReader(inputFilename);// create a media writerIMediaWriter mediaWriter = ToolFactory.makeWriter(outputFilename, mediaReader);// add a writer to the reader, to create the output filemediaReader.addListener(mediaWriter);// create a media viewer with stats enabledIMediaViewer mediaViewer = ToolFactory.makeViewer(true);// add a viewer to the reader, to see the decoded mediamediaReader.addListener(mediaViewer);// read and decode packets from the source file and// and dispatch decoded audio and video to the writerwhile (mediaReader.readPacket() == null) ;}}

只需几行代码,我们就可以将MPEG-4输入文件转换为FLV文件。 我们首先创建一个IMediaReader ,用于读取和解码媒体。 它打开一个媒体容器,从中读取数据包,解码数据,然后将有关数据的信息分发到任何已注册的IMediaListener对象。 这是IMediaWriter类起作用的地方。 它对媒体进行编码和解码,同时处理音频和视频流。 为了使事情变得更有趣,我们还将IMediaViewer附加到我们的阅读器上。 它用作调试工具,使我们可以在解码视频时观看视频。 此外,在此过程中,我们还会看到各种统计信息。 请注意,该类处于实验模式,这意味着它有一些可能会挂起的错误,因此请谨慎处理。

本质上,使用上面的代码,我们将两个侦听器IMediaWriter和IMediaViewer附加到IMediaReader对象,并在读取器读取和解码源文件中的数据包的同时处理回调。 这是在“ while”循环中执行的。 如果我们使用示例输入文件运行该应用程序,将显示类似于以下的屏幕:

该过程完成后(由于我们正在同时实时查看,因此该过程将与源视频文件一样长),将以FLV格式创建一个新的输出文件。

让我们从命令行使用Ffmpeg来比较输入和输出文件:

ffmpeg.exe -ic:/myvideo.mp4
似乎流1编解码器的帧速率与容器的帧速率不同:59.92(14981/250)-> 29.96(14981/500)
从'c:/myvideo.mp4'输入#0,mov,mp4,m4a,3gp,3g2,mj2:
元数据:
major_brand:mp42
minor_version:0
兼容品牌:isomavc1mp42
持续时间:00:04:20.96,开始:0.000000,比特率:582 kb / s
流#0.0(und):音频:aac,44100 Hz,立体声,s16、115 kb / s
流#0.1(und):视频:h264,yuv420p,480×270 [PAR 1:1 DAR 16:9],464 kb / s,29.96 fps,29.96 tbr,29962 tbn,59.92 tbc

在原始视频文件中,容器为MPEG-4 ,有两个流:使用44100Hz的AAC的音频流和使用H.264的视频流。

ffmpeg.exe -ic:/myvideo.flv
似乎流0编解码器的帧速率与容器的帧速率不同:1000.00(1000/1)-> 29.97(30000/1001)
从'c:/myvideo.flv'输入#0 flv:
元数据:
持续时间:261
宽度:480
高度:270
videodatarate:62
帧率:30
videocodecid:2
音频数据率:62
音频采样率:44100
音频样本大小:16
立体声:真
音频编解码器:2
文件大小:43117478
持续时间:00:04:20.98,开始:0.000000,比特率:128 kb / s
流#0.0:视频:flv,yuv420p,480×270、64 kb / s,29.97 tbr,1k tbn,1k tbc
流#0.1:音频:mp3,44100 Hz,2声道,s16,64 kb / s

转码后,生成的Flash视频文件使用FLV视频流和MP3音频流。

现在,我们准备使用Xuggler修改媒体文件。 但是在编写代码之前,我们需要了解MediaTool的工作原理 :

MediaTool使用事件侦听器范例。 写入器会自动作为“侦听器”添加到读取器,并接收所有解码的媒体。 IMediaViewer和IMediaWriter接口(查看器和编写器实际上是什么)实现IMediaListener接口,并且可以作为侦听器添加到IMediaReader。

我们通过前面的示例确认了这一点。 问题是,为了对输入文件进行各种修改 ,我们必须建立一个“媒体管道”。 我们创建IMediaTool的自定义实现,然后以链式方式在每个工具上设置侦听器,以便它们将数据从一个传递到另一个。

假设我们希望在视频中添加静态图像,同时希望减少音频量。 在这种情况下,我们将创建两个自定义IMediaTool对象:

  • StaticImageMediaTool:拍摄视频图片并在屏幕上的特定位置标记静态图像文件。
  • VolumeAdjustMediaTool:按恒定因子调整音量。

另外,我们创建一个IMediaWriter对象,该对象将用于创建输出文件。 通过所有这些,我们创建了一条链,如下所示:

读取器-> addStaticImage-> reduceVolume->写入器

让我们看看实现以上所有功能的代码:

package com.javacodegeeks.xuggler;import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ShortBuffer;import javax.imageio.ImageIO;import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.IMediaTool;
import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.MediaToolAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.IAudioSamplesEvent;
import com.xuggle.mediatool.event.IVideoPictureEvent;public class ModifyMediaExample {private static final String inputFilename = "c:/myvideo.mp4";private static final String outputFilename = "c:/myvideo.flv";private static final String imageFilename = "c:/jcg_logo_small.png";public static void main(String[] args) {// create a media readerIMediaReader mediaReader = ToolFactory.makeReader(inputFilename);// configure it to generate BufferImagesmediaReader.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR);IMediaWriter mediaWriter = ToolFactory.makeWriter(outputFilename, mediaReader);IMediaTool imageMediaTool = new StaticImageMediaTool(imageFilename);IMediaTool audioVolumeMediaTool = new VolumeAdjustMediaTool(0.1);// create a tool chain:// reader -> addStaticImage -> reduceVolume -> writermediaReader.addListener(imageMediaTool);imageMediaTool.addListener(audioVolumeMediaTool);audioVolumeMediaTool.addListener(mediaWriter);while (mediaReader.readPacket() == null) ;}private static class StaticImageMediaTool extends MediaToolAdapter {private BufferedImage logoImage;public StaticImageMediaTool(String imageFile) {try {logoImage = ImageIO.read(new File(imageFile));} catch (IOException e) {e.printStackTrace();throw new RuntimeException("Could not open file");}}@Overridepublic void onVideoPicture(IVideoPictureEvent event) {BufferedImage image = event.getImage();// get the graphics for the imageGraphics2D g = image.createGraphics();Rectangle2D bounds = new Rectangle2D.Float(0, 0, logoImage.getWidth(), logoImage.getHeight());// compute the amount to inset the time stamp and // translate the image to that positiondouble inset = bounds.getHeight();g.translate(inset, event.getImage().getHeight() - inset);g.setColor(Color.WHITE);g.fill(bounds);g.setColor(Color.BLACK);g.drawImage(logoImage, 0, 0, null);// call parent which will pass the video onto next tool in chainsuper.onVideoPicture(event);}}private static class VolumeAdjustMediaTool extends MediaToolAdapter {// the amount to adjust the volume byprivate double mVolume;public VolumeAdjustMediaTool(double volume) {mVolume = volume;}@Overridepublic void onAudioSamples(IAudioSamplesEvent event) {// get the raw audio bytes and adjust it's valueShortBuffer buffer = event.getAudioSamples().getByteBuffer().asShortBuffer();for (int i = 0; i < buffer.limit(); ++i) {buffer.put(i, (short) (buffer.get(i) * mVolume));}// call parent which will pass the audio onto next tool in chainsuper.onAudioSamples(event);}}}

与往常一样,我们首先创建一个IMediaReader并在调用IMediaListener.onVideoPicture时使用setBufferedImageTypeToGenerate方法生成BufferedImage图像。 为了将我们的自定义图像覆盖在实际的视频图片上,这是必要的。 然后,我们创建IMediaWriter和媒体工具对象,并如上所述配置工具链。 让我们仔细看看自定义媒体工具。

首先,我们有StaticImageMediaTool类。 它扩展了MediaToolAdapter并覆盖了onVideoPicture方法,因为我们希望使用这一方法来处理视频流。 在构造函数中,我们已经使用ImageIO.read方法加载了一个图像文件。 JavaCodeGeeks徽标用于此目的(实际上是其缩小版本)。 然后,在已实现的onVideoPicture方法中,我们通过调用IVideoPictureEvent.getImage获得基础的BufferedImage并创建一个Graphics2D对象。 然后,我们使用Graphics.drawImage方法覆盖静态图像。 最后,我们调用父级onVideoPicture方法,该方法会将视频传递到链中的下一个工具。

然后,我们有了VolumeAdjustMediaTool。 它还扩展了MediaToolAdapter ,但覆盖了onAudioSamples方法,该方法在对音频样本进行解码或编码后调用。 在那里,我们通过调用IAudioSamplesEvent.getAudioSamples来获取原始音频字节,并使用相应的ShortBuffer类调整其值。 再次,在自定义处理之后,我们调用父onAudioSamples方法,该方法会将音频传递到链中的下一个工具。 如果现在运行此应用程序,我们将在原始视频的顶部看到添加的图像,并且音频音量将大大降低。
而已。 Xuggler支持的转码和媒体处理。 与往常一样,您可以下载为本教程创建的Eclipse项目 。 请继续关注JavaCodeGeeks上的更多Xuggler教程! 别忘了分享!

相关文章:

  • Xuggler视频处理简介
  • Xuggler教程:帧捕获和视频创建
  • 使用wowza和xuggler将RTMP转换为RTSP

翻译自: https://www.javacodegeeks.com/2011/02/xuggler-tutorial-transcoding-media.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374771.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web工程师必备的43款可视化工具

国外站点DATAVISUALIZATION.CH为大家总结出了当前热用的43款可视化工具&#xff0c;包括Arbor、Chroma.js、D3.js、Google Chart Tools等&#xff0c;绝对让你一饱眼福。 1.Arbor.js Arbor是一个免费的、可视化的图形库&#xff0c;基于矢量创建动态的连接图。它为图形组织和屏…

力扣7. 整数反转

方法一&#xff1a;官方给的&#xff0c;自己懂了后照着敲了一遍 class Solution {public int reverse(int x) {int rev 0;while (x ! 0) {//if判断条件过于复杂&#xff0c;我好不容易看懂了if (rev < Integer.MIN_VALUE / 10 || rev > Integer.MAX_VALUE / 10) {retu…

AndroidManifest.xml中的application中的name属性

被这个不起眼的属性折磨了一天&#xff0c;终于解决了。 由于项目需要&#xff0c;要合并两个android应用&#xff0c;于是拷代码&#xff0c;拷布局文件&#xff0c;拷values&#xff0c;所有的都搞定之后程序还是频频崩溃&#xff0c;一直没有找到原因&#xff0c;学android…

在域驱动设计中使用状态模式

域驱动设计&#xff08;DDD&#xff09;是一种软件开发方法&#xff0c;其中&#xff0c;通过将实现与核心业务概念的不断发展的模型相连接&#xff0c;可以解决问题的复杂性。 该术语是由Eric Evans创造的&#xff0c;并且有一个DDD专用站点可以促进其使用。 根据其定义&#…

Java Fork / Join进行并行编程

最近几年&#xff0c;计算机处理器领域发生了范式转变。 多年来&#xff0c;处理器制造商一直在提高时钟频率&#xff0c;因此开发人员享受到这样的事实&#xff0c;即他们的单线程软件执行得更快&#xff0c;而无需他们付出任何努力。 现在&#xff0c;处理器制造商青睐多核芯…

arm-elf-gcc交叉编译器的使用教程

arm-elf-gcc交叉编译器的使用教程 一开始需要安装arm-elf-gcc&#xff0c;但是这是一个32位的程序&#xff0c;我是安装了64位的系统&#xff0c;据说安装ia32.libs依赖库能运行这个&#xff0c;但是看到博客上面前人安装完了系统图标少了一半&#xff0c;然后就怕了。经过了翻…

程序员需要谨记的九大安全编码规则

历史已经证明&#xff0c;软件设计的缺陷一直是导致其漏洞被利用的最主要的罪魁祸首。安全专家发现&#xff0c;多数漏洞源自常见软件中相对有限的一些漏洞。软件开发者和设计者应当严格检查程序中的各种错误&#xff0c;尽量在软件部署之前就减少或清除其中的漏洞。 下面列举的…

力扣合并两个有序数组

题目&#xff1a;给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff1a;最终&#…

单调栈3_水到极致的题 HDOJ4252

A Famous City 题目大意 给出正视图 每一列为楼的高度 最少有几座楼 坑点 楼高度可以为0 代表没有楼 贡献了两发RE 原因 if(!s.empty()&&tem){s.push(tem); continue;}并不能筛去 空栈且 tem为0的情况 改为 if(!s.empty()){if(tem) s.push(tem); continue;} 后AC 题目…

力扣两数之和 II - 输入有序数组

题目:给定一个已按照 非递减顺序排列 的整数数组 numbers &#xff0c;请你从数组中找出两个数满足相加之和等于目标数 target 。 我的代码&#xff1a; 对撞指针 class Solution {public int[] twoSum(int[] numbers, int target) {int low 0;//指向头int high numbers.le…

[团队项目3.0]Scrum团队成立

Scrum团队成立 5.Scrum团队成立 5.1 团队名称&#xff0c;团队目标、团队口号、团队照&#xff1b; 5.2 角色分配 产品负责人: 决定开发内容和优先级排序&#xff0c;最大化产品以及开发团队工作的价值。 Scrum Master&#xff1a; 负责确保团队遵循 Scrum 的理论、实践和规则。…

Character.isLetterOrDigit(ch)判断ch是否为字母或数字

Character.isLetter(ch) 判断ch是否为字母 Character.isDigit(ch) 判断ch是否为数字 Character.isLetterOrDigit(ch) 判断ch是否为字母或数字 /* 例子*/char ch q;System.out.println(Character.isLetter(ch));System.out.println(Character.isDigit(ch));System.out.print…

高级SmartGWT教程,第1部分

贾斯汀&#xff08;Justin&#xff09;&#xff0c;帕特&#xff08;Pat&#xff09;和我已经开始着手一个需要用户界面进行管理和管理的副项目。 在与SmartGWT和GWT共同工作了一段时间之后&#xff0c;我们决定使用SmartGWT创建接口。 我们非常喜欢视觉组件&#xff08;请查看…

Linux Mint---ATI显卡驱动安装篇

显卡驱动可谓是至关重要&#xff0c;当时折腾debian驱动的时候可是弄了好几天才搞定的&#xff0c;现在却非常容易就是装上&#xff0c; 详见这篇博客&#xff1a;http://www.yyearth.com/article/14-03/amd13.html 在此表示感谢&#xff01; 我的話&#xff0c;全在圖形界面下…

百度云推送的简单集成

1.在百度云推送的应用管理页面&#xff0c;创建自己的应用&#xff0c;创建应用时&#xff0c;需要提供两个证书&#xff0c;开发环境的推送证书和正式环境的推送证书。证书的格式是pem格式的&#xff0c;需要先在apple 开发者中心配置好推送证书&#xff0c;安装到mac上&#…

高级SmartGWT教程,第2部分

这是我的教程的第二部分&#xff0c;有关使用SmartGWT快速进行UI开发。 在本教程的第一部分中 &#xff0c;我们创建了基本的界面布局并添加了一些基本组件。 现在是时候解决这个问题&#xff0c;并使用SmartGWT的真正功能了。 在继续之前&#xff0c;让我们记住到目前为止我们…

使用 SqlDataSource 插入、更新和删除数据49

简介 正如在 数据插入、更新和删除概述 中讨论的那样&#xff0c;GridView 控件提供内置的更新和删除功能&#xff0c;而DetailsView 和 FormView 控件则包含对插入、编辑和删除功能的支持。这些数据修改功能无需编写任何代码&#xff0c;可直接嵌入数据源控件。 数据插入、更新…

Java最佳实践–多线程环境中的DateFormat

这是有关使用Java编程语言时的拟议实践的系列文章的第一篇。 所有讨论的主题均基于用例&#xff0c;这些用例来自于电信行业的关键任务超高性能生产系统的开发。 在阅读本文的每个部分之前&#xff0c;强烈建议您参考相关的Java API文档以获取详细信息和代码示例。 所有测试…

IntentDemo

Intent通信示例&#xff1a; 两个Button&#xff0c;一个startBrowser, 一个startPhone. 其中&#xff0c;OnClickListener()是类View的一个interface&#xff0c;需要实现其中的onClick()函数。 startActivity()开启另一个Activity&#xff0c;本示例中开启Browser或Phone. In…

androidstudio新建项目中在布局文件中不显示title的方法

在androidstudio新建项目的时候&#xff0c;在布局文件里有时候会出现如下情况&#xff1a; 上面的标题栏非常碍眼&#xff0c;要想隐藏标题栏的话&#xff0c;可以在Manifest文件的theme标签里进行配置&#xff0c;自定义一个theme&#xff0c;加上如下两句。或者直接在当前th…