到目前為止,在我們的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