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.pswp.cn/news/374771.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374771.shtml
英文地址,請注明出處:http://en.pswp.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…

完美單身

我不時遇到一些Java程序員&#xff0c;他們不確定他們應該如何正確實現Singleton模式。 &#xff08;如果您不知道什么是Singleton&#xff0c;請嘗試Wikipedia&#xff1a; Singleton模式 &#xff09;。 我并不是在談論在線程環境中的正確實現。 但是&#xff0c;使用最常見的…

力扣移動零

給定一個數組 nums&#xff0c;編寫一個函數將所有 0 移動到數組的末尾&#xff0c;同時保持非零元素的相對順序。 我寫的代碼思路是從開始元素找0&#xff0c;找到了就將這個0后面的元素向前移一位 class Solution {public void moveZeroes(int[] nums) {int n nums.length;…

Doclava:來自Google的自定義Javadoc Doclet

Doclava是Google的自定義Javadoc Doclet&#xff0c;由Google Guice使用&#xff08;請參閱稍冷的 Javadocs &#xff09;。 Doclava使用JSilver作為其模板引擎&#xff0c;這是Clearsilver的純Java實現。 我個人喜歡聯合文檔的想法&#xff0c;它允許文檔在打開的項目上相互鏈…

linux cat顯示若干行

【一】從第3000行開始&#xff0c;顯示1000行。即顯示3000~3999行 cat filename | tail -n 3000 | head -n 1000 【二】顯示1000行到3000行 cat filename| head -n 3000 | tail -n 1000 *注意兩種方法的順序 分解&#xff1a; tail -n 1000&#xff1a;顯示最后1000行 tail -n …

【Stackoverflow好問題】java在,如何推斷陣列Array是否包括指定的值

問題java中&#xff0c;怎樣推斷數組Array是否包括指定的值精華回答1.Arrays.asList(...).contains(...) 2.使用 Apache Commons Lang包中的ArrayUtils.containsString[] fieldsToInclude { "id", "name", "location" };if ( ArrayUtils.contai…

力扣移除元素

給你一個數組 nums 和一個值 val&#xff0c;你需要 原地 移除所有數值等于 val 的元素&#xff0c;并返回移除后數組的新長度。 不要使用額外的數組空間&#xff0c;你必須僅使用 O(1) 額外空間并 原地 修改輸入數組。 元素的順序可以改變。你不需要考慮數組中超出新長度后面…

在域驅動設計中使用狀態模式

域驅動設計&#xff08;DDD&#xff09;是一種軟件開發方法&#xff0c;其中&#xff0c;通過將實現與核心業務概念的不斷發展的模型相連接&#xff0c;可以解決問題的復雜性。 該術語是由Eric Evans創造的&#xff0c;并且有一個DDD專用站點可以促進其使用。 根據其定義&#…

使用selenium進行密碼破解(繞過賬號密碼JS加密)

經常碰到網站&#xff0c;賬號密碼通過js加密后進行提交。通過burp攔截抓到的賬號密碼是加密后的&#xff0c;所以無法通過burp instruder進行破解。只能模擬瀏覽器填寫表單并點擊登錄按鈕進行破解。于是想到了自動化web測試工具selenium&#xff0c;代碼如下&#xff0c;測試效…

力扣刪除排序數組中的重復項

給你一個有序數組 nums &#xff0c;請你 原地 刪除重復出現的元素&#xff0c;使每個元素 只出現一次 &#xff0c;返回刪除后數組的新長度。 不要使用額外的數組空間&#xff0c;你必須在 原地 修改輸入數組 并在使用 O(1) 額外空間的條件下完成。 我沒注意到“有序”這一條…

POJ1789-Truck History .

題目鏈接&#xff1a;http://poj.org/problem?id1789 題目的大概意思就是給你n個字符串。每個字符串只有7的長度。然后分別給這些字符串編號。不同編號之間的距離就是他們有多少個不同的字母。&#xff08;同一個位置字母不相同也算&#xff09;然后一個編號只能由另一個派生…

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;然后就怕了。經過了翻…

力扣刪除排序數組中的重復項 II

給你一個有序數組 nums &#xff0c;請你 原地 刪除重復出現的元素&#xff0c;使每個元素 最多出現兩次 &#xff0c;返回刪除后數組的新長度。 不要使用額外的數組空間&#xff0c;你必須在 原地 修改輸入數組 并在使用 O(1) 額外空間的條件下完成。 思路&#xff1a; 雙指針…

2 android學習資料

http://blog.csdn.net/lmj623565791 http://blog.csdn.net/harvic880925/article/details/50995268轉載于:https://www.cnblogs.com/YyuTtian/p/5440930.html

建立自己的GWT Spring Maven原型

大家好&#xff0c; 在觀看Justin撰寫的有關Spring和GWT的非常有趣的文章時&#xff0c;我認為展示如何構建自己的自定義Maven原型非常有用。我們將展示的原型基于Justin的上一個項目&#xff0c;并包括各種技術&#xff0c;例如Spring &#xff0c; GWT &#xff0c; AspectJ…

C# 連接Oracle數據庫異常總結

這2天因為工作需要連接Oracle數據庫&#xff0c;中間發生了很多問題 一、使用OleDbConnection連接數據庫 ------------------ ProviderOraOLEDB.Oracle.1;User IDsajet;Passwordtech;Data Source(DESCRIPTION (ADDRESS_LIST (ADDRESS (PROTOCOL TCP)(HOST 192.168.66.225)(…

力扣顏色分類

給定一個包含紅色、白色和藍色&#xff0c;一共 n 個元素的數組&#xff0c;原地對它們進行排序&#xff0c;使得相同顏色的元素相鄰&#xff0c;并按照紅色、白色、藍色順序排列。 此題中&#xff0c;我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。 思路:將紅色和藍色…