Xuggler教程:帧捕获和视频创建

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

到目前为止,在我们的Xuggler教程系列中,我们已经对视频处理的Xuggler进行了介绍,并讨论了转码和媒体修改 。 在本教程中,我们将看到如何解码视频和捕获帧,以及如何从头开始创建视频。

让我们开始解码视频流并以预定义的时间间隔捕获一些帧。 例如,可以使用它来制作媒体文件的缩略图 。 为此,我们将再次使用MediaTool API ,这是用于解码,编码和修改视频的高级API。

概念是打开媒体文件,循环播放特定的视频流,并以特定的间隔捕获相应的帧,将其转换为图像,然后将二进制内容转储到文件中。 这是所有这些代码的样子:

package com.javacodegeeks.xuggler;import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;import javax.imageio.ImageIO;import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.MediaListenerAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.IVideoPictureEvent;
import com.xuggle.xuggler.Global;public class VideoThumbnailsExample {public static final double SECONDS_BETWEEN_FRAMES = 10;private static final String inputFilename = "c:/Java_is_Everywhere.mp4";private static final String outputFilePrefix = "c:/snapshots/mysnapshot";// The video stream index, used to ensure we display frames from one and// only one video stream from the media container.private static int mVideoStreamIndex = -1;// Time of last frame writeprivate static long mLastPtsWrite = Global.NO_PTS;public static final long MICRO_SECONDS_BETWEEN_FRAMES = (long)(Global.DEFAULT_PTS_PER_SECOND * SECONDS_BETWEEN_FRAMES);public static void main(String[] args) {IMediaReader mediaReader = ToolFactory.makeReader(inputFilename);// stipulate that we want BufferedImages created in BGR 24bit color spacemediaReader.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR);mediaReader.addListener(new ImageSnapListener());// read out the contents of the media file and// dispatch events to the attached listenerwhile (mediaReader.readPacket() == null) ;}private static class ImageSnapListener extends MediaListenerAdapter {public void onVideoPicture(IVideoPictureEvent event) {if (event.getStreamIndex() != mVideoStreamIndex) {// if the selected video stream id is not yet set, go ahead an// select this lucky video streamif (mVideoStreamIndex == -1)mVideoStreamIndex = event.getStreamIndex();// no need to show frames from this video streamelsereturn;}// if uninitialized, back date mLastPtsWrite to get the very first frameif (mLastPtsWrite == Global.NO_PTS)mLastPtsWrite = event.getTimeStamp() - MICRO_SECONDS_BETWEEN_FRAMES;// if it's time to write the next frameif (event.getTimeStamp() - mLastPtsWrite >= MICRO_SECONDS_BETWEEN_FRAMES) {String outputFilename = dumpImageToFile(event.getImage());// indicate file writtendouble seconds = ((double) event.getTimeStamp()) / Global.DEFAULT_PTS_PER_SECOND;System.out.printf("at elapsed time of %6.3f seconds wrote: %s\n",seconds, outputFilename);// update last write timemLastPtsWrite += MICRO_SECONDS_BETWEEN_FRAMES;}}private String dumpImageToFile(BufferedImage image) {try {String outputFilename = outputFilePrefix + System.currentTimeMillis() + ".png";ImageIO.write(image, "png", new File(outputFilename));return outputFilename;} catch (IOException e) {e.printStackTrace();return null;}}}}

这看起来似乎有些令人不知所措,但确实非常简单。 让我为您提供一些详细信息。 我们首先从输入文件创建IMediaReader 。 媒体读取器用于读取和解码媒体。 由于我们希望将捕获的视频帧作为图像进行处理,因此我们使用setBufferedImageTypeToGenerate方法来表示这一点。 阅读器打开一个媒体容器,从中读取数据包,解码数据,然后将有关数据的信息分发到任何已注册的IMediaListener对象。 这是我们的自定义类ImageSnapListener起作用的地方。

我们的侦听器扩展了MediaListenerAdapter ,它是实现IMediaListener接口的适配器(提供空方法)。 通知实现此接口的对象有关视频处理期间生成的事件的信息。 我们只关心处理视频事件,因此我们仅实现IMediaListener.onVideoPicture方法。 在其中,我们使用提供的IVideoPictureEvent对象查找正在处理的流(仅视频)。

由于我们希望在特定时间捕获帧,因此我们不得不在时间戳上弄乱一点。 首先,我们通过检查Global.NO_PTS常量的值来确保处理第一帧,该值意味着没有为给定对象设置时间戳。 然后,如果经过了最短的时间,我们通过调用IVideoPictureEvent.getImage方法捕获帧,该方法返回基础BufferedImage 。 请注意,我们所说的是经过的视频时间,而不是“实时”。 然后,我们使用ImageIO.write实用程序方法将图像数据转储为PNG格式的文件。 最后,我们更新最后的写入时间。

让我们运行此应用程序以查看结果。 作为输入文件,我使用的是一个古老的Sun商业广告,它宣称“ Java无处不在 ”。 我已经在本地下载了提供的MP4版本。 这是输出控制台的外观:

在经过0.000秒时写道:c:/snapshots/mysnapshot1298228503292.png
在10.010秒的经过时间写道:c:/snapshots/mysnapshot1298228504014.png
在20.020秒的经过时间写道:c:/snapshots/mysnapshot1298228504463.png

在经过时间130.063秒时写道:c:/snapshots/mysnapshot1298228509454.png
在经过时间140.007秒时写道:c:/snapshots/mysnapshot1298228509933.png
在经过150.017秒的时间时写道:c:/snapshots/mysnapshot1298228510379.png

总视频时间约为151秒,因此我们捕获了16帧。 这是我的文件夹中捕获的图像的外观:

好的,就是用来制作视频缩略图的。 现在让我们看看如何从头开始创建视频。 作为输入,我们将使用桌面上的顺序快照 。 这可以用于基本的屏幕记录应用程序。

为了创建视频,与到目前为止所见的MediaTool API相比,我们将不得不采取一些更底层的方法。 不过请不要担心,它不会很复杂。 主要思想是,我们创建一个媒体编写器,向其中添加一些流信息,对我们的媒体(屏幕截图图像)进行编码,然后关闭该编写器。 让我们看看用于实现此目的的代码:

package com.javacodegeeks.xuggler;import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.util.concurrent.TimeUnit;import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.xuggler.ICodec;public class ScreenRecordingExample {private static final double FRAME_RATE = 50;private static final int SECONDS_TO_RUN_FOR = 20;private static final String outputFilename = "c:/mydesktop.mp4";private static Dimension screenBounds;public static void main(String[] args) {// let's make a IMediaWriter to write the file.final IMediaWriter writer = ToolFactory.makeWriter(outputFilename);screenBounds = Toolkit.getDefaultToolkit().getScreenSize();// We tell it we're going to add one video stream, with id 0,// at position 0, and that it will have a fixed frame rate of FRAME_RATE.writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_MPEG4, screenBounds.width/2, screenBounds.height/2);long startTime = System.nanoTime();for (int index = 0; index < SECONDS_TO_RUN_FOR * FRAME_RATE; index++) {// take the screen shotBufferedImage screen = getDesktopScreenshot();// convert to the right image typeBufferedImage bgrScreen = convertToType(screen, BufferedImage.TYPE_3BYTE_BGR);// encode the image to stream #0writer.encodeVideo(0, bgrScreen, System.nanoTime() - startTime, TimeUnit.NANOSECONDS);// sleep for frame rate millisecondstry {Thread.sleep((long) (1000 / FRAME_RATE));} catch (InterruptedException e) {// ignore}}// tell the writer to close and write the trailer if  neededwriter.close();}public static BufferedImage convertToType(BufferedImage sourceImage, int targetType) {BufferedImage image;// if the source image is already the target type, return the source imageif (sourceImage.getType() == targetType) {image = sourceImage;}// otherwise create a new image of the target type and draw the new imageelse {image = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), targetType);image.getGraphics().drawImage(sourceImage, 0, 0, null);}return image;}private static BufferedImage getDesktopScreenshot() {try {Robot robot = new Robot();Rectangle captureSize = new Rectangle(screenBounds);return robot.createScreenCapture(captureSize);} catch (AWTException e) {e.printStackTrace();return null;}}}

我们首先从给定的输出文件创建一个IMediaWriter 。 此类对媒体进行编码和解码,同时处理音频和视频流。 Xuggler猜测文件扩展名(在我们的情况下为MP4)的输出格式,并适当设置一些默认值。 然后,我们使用addVideoStream方法添加新的视频流,并提供其索引,使用的编解码器类型 (此处为MPEG-4 )和视频尺寸。 在此示例中,尺寸设置为等于屏幕尺寸的一半。

然后,我们执行一个循环,该循环的运行次数等于所需的帧速率乘以所需的运行时间。 在循环内部,我们按照Java2D:带有Java屏幕快照的文章中的描述生成屏幕快照。 我们将屏幕快照作为BufferedImage检索,并将其转换为适当的类型( TYPE_3BYTE_BGR )(如果尚不存在)。

接下来,我们使用IMediaWriter.encodeVideo方法将图像编码为视频流。 我们提供流索引,图像,经过的视频时间和时间单位。 然后,根据所需的帧速率,我们睡适当的时间。 循环结束后,我们将关闭编写器,并在必要时编写预告片,具体取决于视频格式(这由Xuggler自动完成)。

如果我们执行该应用程序,则会创建一个视频,其中记录了您的桌面操作。 这是浏览JavaCodeGeeks网站时的静止图像:

伙计们,这是Xuggler的另一篇教程,描述了如何从输入文件捕获视频帧以及如何使用桌面快照生成视频。 与往常一样,您可以下载为本教程创建的Eclipse项目 。 请继续关注JavaCodeGeeks上的更多Xuggler教程! 别忘了分享!

相关文章:

  • Xuggler视频处理简介
  • Xuggler教程:转码和媒体修改
  • 使用wowza和xuggler将RTMP转换为RTSP

翻译自: https://www.javacodegeeks.com/2011/02/xuggler-tutorial-frames-capture-video.html

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

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

相关文章

面向对象-原型对象

创建对象 Js中可以用构造函数模式创建对象&#xff0c;如&#xff1a; function Person(name, age, job) {this.name name;this.age age;this.job job;this.sayName function () {alert(this.name);}}var person1 new Person("Nicholas", 29, "aa");v…

索引类型

1.B树索引 在Oracle中是通用索引&#xff0c;是创建索引时的默认索引。B树索引可以是单列索引&#xff0c;也可以是组合/复合索引。B树索引最多可以包括22列。 2.位图索引 位图索引时决策支持系统&#xff08;DSS&#xff09;和数据仓库的理想选择&#xff0c;它们不应该用于事…

C语言条件运算符

先看一个error error: lvalue required as left operand of assignment| i 0 ? X 1.0 : X * x;修改后 i 0 ? (X 1.0) : (X * x);也就是说条件运算符可以执行语句&#xff0c;当是赋值语句时要加括号规定优先级&#xff0c;不然会干扰程序判断。 因为条件运算符作为三目…

EJB 3.0注入和查找简介

介绍 Enterprise JavaBeans Specification v。3.0引入了简化的&#xff0c;基于注释的API&#xff0c;用于EJB注入和查找。 EJB 3.0现在是POJO&#xff0c;可以使用简单的注释将其注入其他组件&#xff08;例如EJB和Servlet&#xff09;。 EJB 3.0是Java EE 6的许多其他基于POJ…

SignalR + MVC5 简单示例

SignalR MVC5 简单示例 原文:SignalR MVC5 简单示例本文和前一篇文章很类似&#xff0c;只不过是把 SignalR 应用在了 MVC 中 新建项目&#xff0c;选择 MVC 模板 安装 SignalR Install-Package Microsoft.AspNet.SignalR 在项目中添加文件夹 Hubs 在 Hubs 文件夹中添加 Sign…

Java内存模型–快速概述和注意事项

在计算中&#xff0c; 内存模型描述了线程如何通过内存进行交互&#xff0c;或更一般地&#xff0c;它指定了为分段内存或分页内存平台生成代码时允许编译器进行的假设。 在给定程序和该程序的执行跟踪的情况下&#xff0c;它实质上描述了执行跟踪是否是该程序的合法执行。 Jav…

6-7 统计某类完全平方数 (20 分)

本题要求实现一个函数&#xff0c;判断任一给定整数N是否满足条件&#xff1a;它是完全平方数&#xff0c;又至少有两位数字相同&#xff0c;如144、676等。 函数接口定义&#xff1a; int IsTheNumber ( const int N );其中N是用户传入的参数。如果N满足条件&#xff0c;则该…

C#中数组、ArrayList和List三者的区别(转) ,加修改

在C#中数组&#xff0c;ArrayList&#xff0c;List都能够存储一组对象&#xff0c;那么这三者到底有什么样的区别呢。 数组 数组在C#中最早出现的。在内存中是连续存储的&#xff0c;所以它的索引速度非常快&#xff0c;而且赋值与修改元素也很简单。 <span style"font…

phpmyadmin mysql Access denied for user 'root'@'localhost'问题解决

centos6.4 32位的vps上装了lnmp以后&#xff0c;phpmyadmin无法连接mysql服务器&#xff0c;ssh命令行里mysql -uroot -p 命令后老是出现拒绝连接的情况。php程序里也是拒绝连接。尝试过修改phpmyadmin的config.inc.php文件&#xff0c;尝试过修改my.cnf文件&#xff0c;尝试过…

带有Spring和Maven教程的JAX–WS

Spring框架通过JAX-WS提供对Web服务的远程支持&#xff0c;实际上&#xff0c;如Spring 参考文档中所述 &#xff0c;有三种将Spring POJO服务公开为JAX-WS Web服务的方式&#xff1a; 公开基于Servlet的Web服务&#xff08;适用于Java EE 5环境&#xff09; 导出独立的Web服…

7-2 然后是几点 (15 分)

7-2 然后是几点 (15 分) 有时候人们用四位数字表示一个时间&#xff0c;比如 1106 表示 11 点零 6 分。现在&#xff0c;你的程序要根据起始时间和流逝的时间计算出终止时间。 读入两个数字&#xff0c;第一个数字以这样的四位数字表示当前时间&#xff0c;第二个数字表示分钟…

CXF学习(2) helloworld

0.新建一个项目取名wsserver. pom.xml 文件如下 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd…

Hive 接口介绍(Web UI/JDBC)

Hive 接口介绍&#xff08;Web UI/JDBC&#xff09; 实验简介 本次实验学习 Hive 的两种接口&#xff1a;Web UI 以及 JDBC。 一、实验环境说明 1. 环境登录 无需密码自动登录&#xff0c;系统用户名shiyanlou&#xff0c;密码shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubu…

Java最佳实践– Char到Byte和Byte到Char的转换

在使用Java编程语言时&#xff0c;我们将继续讨论与建议的实践有关的系列文章&#xff0c;我们将讨论String性能调优。 特别是&#xff0c;我们将重点介绍使用默认编码时如何有效地处理字符到字节和字节到字符的转换。 本文总结了两种提议的自定义方法与两种经典方法&#xff0…

IOS-C文件的创建于初始化函数.void init() write_file()

//文件初始化 void init(){ FILE * fpNULL; fpfopen("telbook.data", "rb"); int count0; if (fpNULL) //没有这个文件就把这个文件创建出来 { fpfopen("tellbook.data", "wb"); fwrite(&count, sizeof(count), 1, fp); fclose(…

7-3 逆序的三位数 (10 分)

7-3 逆序的三位数 (10 分) 程序每次读入一个正3位数&#xff0c;然后输出按位逆序的数字。注意&#xff1a;当输入的数字含有结尾的0时&#xff0c;输出不应带有前导的0。比如输入700&#xff0c;输出应该是7。 输入格式&#xff1a; 每个测试是一个3位的正整数。 输出格式&a…

抛弃优启Grub4dos和PE大多数时间可以这样用

抛弃优启Grub4dos和PE大多数时间可以这样用 在能够进入Windows的情况下&#xff0c;Grub4dos和PE大多数时间可以这样用 http://yunpan.cn/cyuuUtUQMfmGN 提取码 c2acGrub4dos的出现颠覆了传统的EZBOOT光盘启动模式&#xff0c;很多人用grub4dos来实现优盘启动&#xff0c;同时g…

Java最佳实践–队列之战和链接的ConcurrentHashMap

在使用Java编程语言时&#xff0c;我们将继续讨论与建议的实践有关的系列文章&#xff0c;我们将在四个具有相关语义的流行Queue实现类之间进行性能比较。 为了使事情变得更现实&#xff0c;我们将在多线程环境下进行测试&#xff0c;以讨论和演示如何将ArrayBlockingQueue &am…

HDU 5652 India and China Origins(二分 + BFS)

本文链接:http://www.cnblogs.com/Ash-ly/p/5398867.html 题意&#xff1a; 中国和印度之间有一片地方&#xff0c;把这片地方抽象化&#xff0c;于是就可以看成一个N * M矩阵&#xff0c;其中黑色的代表高山不能走过去&#xff0c;白色的代表平原&#xff0c;可以通行,人每次可…

C语言%.2f四舍五入

#include <stdio.h> int main() {double d 1.199;printf("%.2f", d);return 0; }输出1.20 如果不想让其四舍五入可以这样&#xff1a; #include <stdio.h> #include <math.h> int main() {double d 1.199;printf("%.2f", floor(d * 1…