自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

鴻蒙開(kāi)源全場(chǎng)景應(yīng)用開(kāi)發(fā)—視頻編解碼

開(kāi)發(fā)
本期文章將介紹視頻編解碼模塊,視頻編解碼是視頻處理的基礎(chǔ),鴻蒙平臺(tái)為我們提供了強(qiáng)大的視頻處理能力。

[[424367]]

想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

背景

面對(duì)鴻蒙這一全新的生態(tài),廣大消費(fèi)者在積極嘗鮮的同時(shí),家中不可避免會(huì)出現(xiàn)安卓設(shè)備和鴻蒙設(shè)備并存的現(xiàn)象,短期內(nèi)可能不會(huì)形成全鴻蒙的生態(tài)環(huán)境。因此,在未來(lái)的一段時(shí)間內(nèi),鴻蒙設(shè)備和安卓設(shè)備共存的現(xiàn)象會(huì)比較普遍。那么為了給用戶帶來(lái)更加流暢的全場(chǎng)景體驗(yàn),鴻蒙和安卓設(shè)備之間的交互就顯得格外重要。

家庭合影美顏相機(jī)

家庭合影美顏相機(jī)應(yīng)用是同時(shí)基于鴻蒙和安卓設(shè)備的應(yīng)用,可以實(shí)現(xiàn)鴻蒙大屏借助安卓手機(jī)的能力進(jìn)行美顏拍照的功能,其中安卓端使用了GitHub上的開(kāi)源項(xiàng)目。具體來(lái)說(shuō),此應(yīng)用能夠?qū)Ⅷ櫭纱笃僚臄z的視頻數(shù)據(jù)實(shí)時(shí)傳輸?shù)桨沧渴謾C(jī)上;并在安卓端為其添加濾鏡,再將處理后的視頻數(shù)據(jù)傳回到鴻蒙大屏進(jìn)行渲染顯示,從而達(dá)到鴻蒙大屏進(jìn)行美顏拍照的功能,其效果可以參考下圖1:

鴻蒙開(kāi)源全場(chǎng)景應(yīng)用開(kāi)發(fā)——視頻編解碼-鴻蒙HarmonyOS技術(shù)社區(qū)

圖1 家庭合影美顏相機(jī)應(yīng)用的效果示意圖

應(yīng)用運(yùn)行后的動(dòng)態(tài)場(chǎng)景效果可以參考圖2,圖中上方橫屏顯示的是鴻蒙手機(jī),下方豎屏顯示的是安卓手機(jī)。此處需要說(shuō)明的是,由于實(shí)驗(yàn)環(huán)境缺少搭載鴻蒙系統(tǒng)的大屏設(shè)備,因此我們使用鴻蒙手機(jī)替代大屏設(shè)備模擬實(shí)驗(yàn)場(chǎng)景。

鴻蒙開(kāi)源全場(chǎng)景應(yīng)用開(kāi)發(fā)——視頻編解碼-鴻蒙HarmonyOS技術(shù)社區(qū)

圖2 應(yīng)用運(yùn)行后的效果

應(yīng)用成功運(yùn)行后的效果如下:

  • 在鴻蒙大屏設(shè)備上開(kāi)啟攝像頭訪問(wèn)權(quán)限,點(diǎn)擊主菜單界面的“點(diǎn)擊發(fā)送大屏數(shù)據(jù)”按鈕,即可將大屏拍攝到的視頻數(shù)據(jù)通過(guò)RTP協(xié)議發(fā)送到安卓手機(jī)端。
  • 在安卓手機(jī)端點(diǎn)擊主菜單界面的“GLCAMERAVIEW”按鈕,即可接收上述鴻蒙大屏傳來(lái)的視頻數(shù)據(jù),并將視頻數(shù)據(jù)顯示在手機(jī)屏幕上。
  • 安卓端在接收到視頻后,會(huì)將數(shù)據(jù)實(shí)時(shí)渲染到手機(jī)屏幕上,用戶可以選擇給視頻添加各種風(fēng)格的濾鏡;
  • 安卓端會(huì)通過(guò)RTP協(xié)議將添加濾鏡后的視頻數(shù)據(jù)傳輸?shù)进櫭啥诉M(jìn)行顯示。

上述已經(jīng)介紹過(guò),此應(yīng)用是同時(shí)基于鴻蒙和安卓設(shè)備的,因此在講解此應(yīng)用時(shí)不僅會(huì)包含鴻蒙相關(guān)知識(shí),同時(shí)也會(huì)涉及到一些安卓方面的知識(shí)。此應(yīng)用包含4個(gè)功能模塊,可參考圖3,分別是:視頻編解碼、通訊協(xié)議、美顏濾鏡和視頻渲染。其中每個(gè)模塊都會(huì)涉及到不同的技術(shù)點(diǎn),如視頻編解碼會(huì)涉及視頻流格式和編解碼器參數(shù)設(shè)置;通訊協(xié)議會(huì)涉及UDP、RTP協(xié)議等。后續(xù)我們的文章將會(huì)按不同模塊進(jìn)行講解和發(fā)布,敬請(qǐng)期待!

鴻蒙開(kāi)源全場(chǎng)景應(yīng)用開(kāi)發(fā)——視頻編解碼-鴻蒙HarmonyOS技術(shù)社區(qū)

圖3 家庭合影美顏相機(jī)功能模塊圖

視頻編解碼應(yīng)用案例解析

本期文章將介紹視頻編解碼模塊,視頻編解碼是視頻處理的基礎(chǔ),鴻蒙平臺(tái)為我們提供了強(qiáng)大的視頻處理能力。為了更具體地講解該模塊功能,我們將家庭合影美顏相機(jī)應(yīng)用中涉及實(shí)現(xiàn)視頻編解碼的代碼獨(dú)立拆分出來(lái)形成了一個(gè)視頻編解碼Demo,將在后續(xù)進(jìn)行效果展示和實(shí)現(xiàn)原理講解。

下面以拆分出來(lái)的視頻編解碼模塊Demo為例,先向大家講解鴻蒙視頻編解碼的具體實(shí)現(xiàn)原理,再對(duì)鴻蒙和安卓?jī)烧咭曨l編解碼的原理差異進(jìn)行分析。

1、運(yùn)行效果和代碼結(jié)構(gòu)

視頻編解碼Demo的運(yùn)行效果如圖4所示。開(kāi)始運(yùn)行后,會(huì)獲取攝像頭的權(quán)限,然后會(huì)在界面中間的矩形區(qū)域顯示攝像頭拍攝到的畫(huà)面,此時(shí)用戶可以點(diǎn)擊界面上的“開(kāi)始編解碼”按鈕,即可在原始視頻的下方的矩形區(qū)域中看到編解碼后視頻的渲染效果。

鴻蒙開(kāi)源全場(chǎng)景應(yīng)用開(kāi)發(fā)——視頻編解碼-鴻蒙HarmonyOS技術(shù)社區(qū)
鴻蒙開(kāi)源全場(chǎng)景應(yīng)用開(kāi)發(fā)——視頻編解碼-鴻蒙HarmonyOS技術(shù)社區(qū)

圖4 視頻編解碼Demo運(yùn)行效果圖

接著介紹一下視頻編解碼Demo的代碼結(jié)構(gòu),如圖5所示。其中MainAbilitySlice類用于頁(yè)面布局和實(shí)例化編解碼器;我們還構(gòu)建了VDEecoder類和VDDecoder類,前者用于視頻編碼并對(duì)編碼過(guò)程進(jìn)行監(jiān)聽(tīng),將編碼后的數(shù)據(jù)送去解碼,后者用于視頻解碼,對(duì)解碼過(guò)程進(jìn)行監(jiān)聽(tīng),輸出解碼后的數(shù)據(jù)。

鴻蒙開(kāi)源全場(chǎng)景應(yīng)用開(kāi)發(fā)——視頻編解碼-鴻蒙HarmonyOS技術(shù)社區(qū)

圖5 視頻編解碼Demo代碼結(jié)構(gòu)

2、實(shí)現(xiàn)流程解析

下面講解此Demo實(shí)現(xiàn)視頻編解碼效果的具體實(shí)現(xiàn)流程,共分為7個(gè)步驟:

步驟1. 創(chuàng)建整體顯示布局。

步驟2. 實(shí)例化編碼類VDEncoder的對(duì)象并初始化編碼器。

步驟3. 獲取相機(jī)數(shù)據(jù)并將其加入編碼隊(duì)列。

步驟4. 初始化解碼器。

步驟5. 設(shè)置Button監(jiān)聽(tīng)事件,執(zhí)行編碼操作。

步驟6. 監(jiān)聽(tīng)編碼器,獲取編碼后的數(shù)據(jù)并送去解碼。

步驟7. 執(zhí)行解碼操作。

(1)創(chuàng)建整體顯示布局

在MainAbilitySlice中,定義用于控制編解碼的Button按鈕控件、用于顯示編解碼狀態(tài)的Text文本控件、兩個(gè)分別用于顯示攝像頭拍攝的視頻和編解碼后視頻的SurfaceProvider畫(huà)面渲染控件,并設(shè)置上述控件的相關(guān)屬性,如圖4效果所示。

(2)實(shí)例化編碼類VDEncoder的對(duì)象并初始化編碼器

實(shí)例化編碼VDEncoder類對(duì)象,使用帶有參數(shù)framerate的構(gòu)造函數(shù),其中framerate代表幀速率,此處設(shè)為15。

  1. VDEncoder vdEncoder = new VDEncoder(15);// 創(chuàng)建編碼類對(duì)象 

在構(gòu)造函數(shù)中,需要進(jìn)行編碼器初始化操作,如設(shè)置編碼器格式如圖像大小、比特率、顏色格式、幀率、關(guān)鍵幀間隔時(shí)間、比特率模式等。需要注意的是,要選擇合適的比特率、幀率等參數(shù),不然極有可能出現(xiàn)編解碼后視頻顯示不出來(lái)或顯示效果異常的情況。在設(shè)置完各屬性參數(shù)后,通過(guò)set()方法將上述格式屬性配置到編碼器對(duì)象中;再設(shè)置監(jiān)聽(tīng)用于獲取編碼輸出數(shù)據(jù);使用start()方法控制編碼器開(kāi)始執(zhí)行;并初始化自定義的單例線程池用于編碼線程,由于攝像頭獲取到的數(shù)據(jù)會(huì)被按順序放入視頻隊(duì)列YUVQueue中,因此需要使用線程來(lái)提高處理效率。

  1. public VDEncoder(int framerate){ 
  2.     Format fmt = new Format();// 創(chuàng)建編碼器格式 
  3.     fmt.putStringValue("mime""video/avc"); 
  4.     fmt.putIntValue("width", 640);// 視頻圖像寬度 
  5.     fmt.putIntValue("height", 480);// 視頻圖像高度 
  6.     fmt.putIntValue("bitrate", 392000);// 比特率 
  7.     fmt.putIntValue("color-format", 21);// 顏色格式 
  8.     fmt.putIntValue("frame-rate", framerate);// 幀率 
  9.     fmt.putIntValue("i-frame-interval", 1);// 關(guān)鍵幀間隔時(shí)間 
  10.     fmt.putIntValue("bitrate-mode", 1);// 比特率模式 
  11.  
  12.     mCodec.setCodecFormat(fmt);// 設(shè)置編碼器格式 
  13.     mCodec.registerCodecListener(encoderlistener);// 設(shè)置監(jiān)聽(tīng) 
  14.     mCodec.start();// 編碼器開(kāi)始執(zhí)行 
  15.     singleThreadExecutor = new SingleThreadExecutor();// 初始化自定義單例線程池 

(3)獲取相機(jī)數(shù)據(jù)并將其加入編碼隊(duì)列

在正式開(kāi)始編碼之前,需要通過(guò)相機(jī)的圖像監(jiān)聽(tīng)事件ImageReceiver.IImageArrivalListener,獲取實(shí)時(shí)返回的原生視頻數(shù)據(jù)并將其存放在ByteBuffer類對(duì)象中,再逐個(gè)讀取成byte數(shù)組的形式,存儲(chǔ)在YUV_DATA中。

  1. private final ImageReceiver.IImageArrivalListener imagerArivalListener = new ImageReceiver.IImageArrivalListener() { 
  2.     @Override 
  3.     public void onImageArrival(ImageReceiver imageReceiver) {// 當(dāng)相機(jī)開(kāi)始運(yùn)行后,用于監(jiān)聽(tīng),實(shí)時(shí)返回視頻原始數(shù)據(jù) 
  4.         mLog.log("imagearival""arrival"); 
  5.         Image mImage = imageReceiver.readNextImage();// 用于讀取視頻畫(huà)面 
  6.         if(mImage != null){ 
  7.             ByteBuffer mBuffer; 
  8.             byte[] YUV_DATA = new byte[VIDEO_HEIGHT * VIDEO_WIDTH * 3 / 2];// 存放從相機(jī)獲取的原始 YUV 視頻數(shù)據(jù) 
  9.             ... 
  10.        // 從相機(jī)獲取實(shí)時(shí)拍攝的視頻數(shù)據(jù),并將 Image 讀取到的視頻流數(shù)據(jù)存放在 mBuffer  
  11.             mBuffer = mImage.getComponent(ImageFormat.ComponentType.YUV_Y).getBuffer(); 
  12.             // 從視頻流mBuffer逐個(gè)讀取成 byte 數(shù)組的形式,并存儲(chǔ)在 YUV_DATA 中 
  13.             for(i=0;i< VIDEO_WIDTH * VIDEO_HEIGHT;i++){ 
  14.                 YUV_DATA[i] = mBuffer.get(i); 
  15.             } 
  16.             ... 
  17.             vdEncoder.addFrame(YUV_DATA);// 將視頻數(shù)據(jù) YUV_DATA 加入到隊(duì)列等待編解碼 
  18.             mImage.release();// 獲取完視頻數(shù)據(jù)之后及時(shí)釋放 
  19.             return
  20.         } 
  21.     } 
  22. }; 

(4)初始化解碼器

通過(guò)VDEncoder類對(duì)象調(diào)用prepareDecoder()方法,初始化解碼器,并將用于顯示編解碼后視頻的SurfaceProvider對(duì)象作為入?yún)魅敕椒ㄖ小?/p>

  1. vdEncoder.prepareDecoder(surfaceview2); 

在VDEncoder類的prepareDecoder()方法中,先實(shí)例化解碼VDDecoder類對(duì)象,并使用SurfaceProvider類對(duì)象surfaceview顯示編解碼后的視頻,再調(diào)用start()方法控制開(kāi)始解碼。

  1. public void prepareDecoder(SurfaceProvider surfaceview){ 
  2.     vdDecoder = new VDDecoder(surfaceview);// 創(chuàng)建解碼類對(duì)象,并使用surfaceview顯示解碼后的視頻 
  3.     vdDecoder.start();// 開(kāi)始解碼 

視頻解碼的初始化方法beginCodec()與編碼初始化實(shí)現(xiàn)原理類似,也需要對(duì)各種格式進(jìn)行配置,此處不再進(jìn)行贅述,唯一不同之處是幀率和關(guān)鍵幀間隔時(shí)間的設(shè)置,具體含義可參考下面代碼中的注釋信息。

  1. private synchronized void beginCodec() {//初始化解碼器各參數(shù) 
  2.     System.out.println("isSurfaceCreated = " + Boolean.toString(isSurfaceCreated)); 
  3.     if (isSurfaceCreated) { 
  4.         isSurfaceCreated = false
  5.         Format fmt = new Format();// 創(chuàng)建解碼器格式 
  6.         fmt.putStringValue("mime""video/avc"); 
  7.         fmt.putIntValue("width", 640);// 視頻圖像寬度 
  8.         fmt.putIntValue("height", 480);// 視頻圖像高度 
  9.         fmt.putIntValue("bitrate", 392000);// 比特率 
  10.         fmt.putIntValue("color-format", 21);// 顏色格式 
  11.         fmt.putIntValue("frame-rate", 30);// 幀率 
  12.         fmt.putIntValue("i-frame-interval", -1);// 關(guān)鍵幀間隔時(shí)間 
  13.         fmt.putIntValue("bitrate-mode", 1);// 比特率模式 
  14.  
  15.         mCodec.setCodecFormat(fmt);// 設(shè)置解碼器格式 
  16.         mCodec.registerCodecListener(decoderlistener);// 設(shè)置監(jiān)聽(tīng) 
  17.         mCodec.start();// 解碼器開(kāi)始執(zhí)行 
  18.         isMediaCodecInit = true
  19.     } 

(5)設(shè)置Button監(jiān)聽(tīng)事件,執(zhí)行編碼操作

為整體顯示布局中用于控制是否開(kāi)始編解碼的Button按鈕設(shè)置onCilick()點(diǎn)擊事件,調(diào)用VDEncoder類對(duì)象的start()方法控制開(kāi)始編碼。判斷如果編碼正在進(jìn)行,則顯示當(dāng)前編碼狀態(tài)。

  1. button.setClickedListener(component -> {// 按鈕被點(diǎn)擊 
  2.     mLog.log("button""start"); 
  3.     vdEncoder.start();// 開(kāi)始編碼 
  4.     if(vdEncoder.isRuning){// 如果編碼正在進(jìn)行,顯示當(dāng)前編碼狀態(tài) 
  5.         text.setText("成功進(jìn)行編解碼,并顯示在下方"); 
  6.     } 
  7. }); 

在具體執(zhí)行編碼操作的線程中,會(huì)先調(diào)用Codec類的getAvailableBuffer()方法在指定索引處獲取編碼時(shí)的可用緩沖區(qū)ByteBuffer,其中參數(shù)timeout表示用于填充有效數(shù)據(jù)的緩沖區(qū)索引;再創(chuàng)建緩沖區(qū)信息BufferInfo,注意ByteBuffer和BufferInfo要成對(duì)使用,調(diào)用setInfo()方法設(shè)置相關(guān)信息如偏移量、數(shù)據(jù)長(zhǎng)度、時(shí)間戳和緩沖區(qū)類型;接著將數(shù)據(jù)通過(guò)put()方法放入緩沖區(qū)ByteBuffer中;并通過(guò)Codec類的WriteBuffer()方法將傳入的ByteBuffer和BufferInfo進(jìn)行處理。

  1. private void startEncoderThread() { 
  2.     singleThreadExecutor.execute(new Runnable() { 
  3.         @Override 
  4.         public void run() { 
  5.             byte[] data; 
  6.             while (isRuning) { 
  7.                 try { 
  8.                     data = YUVQueue.take();// 從隊(duì)列中獲取原相機(jī)得到的原生視頻數(shù)據(jù) 
  9.                 } catch (InterruptedException e) { 
  10.                     e.printStackTrace(); 
  11.                     break; 
  12.                 } 
  13.                 // 將數(shù)據(jù)以 Buffer 和 BufferUnfo 的形式通過(guò) Codec 類進(jìn)行編碼 
  14.                 ByteBuffer buffer =  mCodec.getAvailableBuffer(-1); 
  15.                 BufferInfo bufferInfo = new BufferInfo();//與ByteBuffer成對(duì)使用 
  16.                 buffer.put(data);//將數(shù)據(jù)放入緩沖區(qū) 
  17.                 bufferInfo.setInfo(0, data.length, System.currentTimeMillis(), 0);//設(shè)置數(shù)據(jù)相關(guān)信息 
  18.                 mCodec.writeBuffer(buffer, bufferInfo);//對(duì)緩沖區(qū)數(shù)據(jù)進(jìn)行處理 
  19.             } 
  20.         } 
  21.     }); 

(6)監(jiān)聽(tīng)編碼器,獲取編碼后的數(shù)據(jù)并送去解碼

設(shè)置編碼器監(jiān)聽(tīng)事件,監(jiān)聽(tīng)編碼器行為。重寫(xiě)onReadBuffer()方法獲取編碼后的輸出緩沖區(qū)ByteBuffer和緩沖區(qū)信息BufferInfo,通過(guò)ByteBuffer類對(duì)象調(diào)用get()方法獲得輸出數(shù)據(jù),并存放在byte數(shù)組data中;再通過(guò)當(dāng)前解碼類對(duì)象vdDecoder調(diào)用toDecoder()方法,即可將完成編碼后的視頻數(shù)據(jù)送去解碼。

  1. private Codec.ICodecListener encoderlistener = new Codec.ICodecListener() { 
  2.     // 用于監(jiān)聽(tīng)編碼器,獲取編碼完成后的數(shù)據(jù) 
  3.     @Override 
  4.     public void onReadBuffer(ByteBuffer byteBuffer, BufferInfo bufferInfo, int i) { 
  5.         byte[] data = new byte[bufferInfo.size]; 
  6.         byteBuffer.get(data);// 從編碼器的 byteBuffer 中獲取數(shù)據(jù) 
  7.         mLog.log("pushdata""encoded data:" + data.length); 
  8.         vdDecoder.toDecoder(data);// 通過(guò)解碼類的 toDecoder()方法,將編碼完成的視頻數(shù)據(jù)送去解碼 
  9.     }... 
  10. }; 

(7)執(zhí)行解碼操作

通過(guò)decoder()方法執(zhí)行解碼操作,其原理和上述講解過(guò)的編碼原理相同。使用Codec類的getAvailableBuffer()方法在指定索引處獲取解碼時(shí)的可用緩沖區(qū)ByteBuffer;創(chuàng)建緩沖區(qū)信息BufferInfo,調(diào)用setInfo()方法設(shè)置相關(guān)信息并將數(shù)據(jù)放入緩沖區(qū)ByteBuffer、WriteBuffer()方法處理傳入的ByteBuffer和BufferInfo。在完成解碼之后,通過(guò)事件監(jiān)聽(tīng)類獲得輸出數(shù)據(jù),并按需對(duì)解碼后的視頻數(shù)據(jù)進(jìn)行畫(huà)面渲染顯示等相關(guān)操作。

  1. private void decoder(byte[] video) {//解碼器具體執(zhí)行流程 
  2.     ByteBuffer mBuffer = mCodec.getAvailableBuffer(-1); 
  3.     BufferInfo info = new BufferInfo();//與ByteBuffer成對(duì)使用 
  4.     info.setInfo(0, video.length, 0, 0);//設(shè)置數(shù)據(jù)相關(guān)信息 
  5.     mBuffer.put(video);//將數(shù)據(jù)放入緩沖區(qū) 
  6.     mCodec.writeBuffer(mBuffer, info);//對(duì)緩沖區(qū)數(shù)據(jù)進(jìn)行處理 

鴻蒙編解碼器Codec和安卓編解碼器MediaCodec的區(qū)別

MediaCodec類作為安卓多媒體基礎(chǔ)框架的一部分,通過(guò)訪問(wèn)底層的媒體編解碼器,即編解碼器組件,來(lái)實(shí)現(xiàn)對(duì)于音視頻的編解碼功能。MediaCodec共支持4種數(shù)據(jù)類型,分別是原始的音、視頻數(shù)據(jù),和壓縮后的音、視頻數(shù)據(jù)。鴻蒙平臺(tái)的Codec編解碼類同樣支持上述提到的4種數(shù)據(jù)類型,相比較安卓平臺(tái)的MediaCodec類,二者區(qū)別主要體現(xiàn)在使用方式上,即輸出數(shù)據(jù)的獲取方式和Index緩沖區(qū)索引的使用。先來(lái)對(duì)比觀察一下鴻蒙和安卓編解碼實(shí)現(xiàn)原理的代碼:

  1. //鴻蒙Codec編解碼: 
  2. private void decoder(byte[] video) {//將數(shù)據(jù)以 Buffer 和 BufferUnfo 的形式通過(guò) Codec 類進(jìn)行解碼 
  3.     ByteBuffer mBuffer = mCodec.getAvailableBuffer(-1); 
  4.     BufferInfo info = new BufferInfo(); 
  5.     info.setInfo(0, video.length, 0, 0); 
  6.     mBuffer.put(video);//數(shù)據(jù)放入 
  7.     mCodec.writeBuffer(mBuffer, info); 
  8. //鴻蒙監(jiān)聽(tīng)類 
  9. private Codec.ICodecListener decoderlistener = new Codec.ICodecListener() { 
  10.     // 用于監(jiān)聽(tīng)編碼器,獲取解碼完成后的數(shù)據(jù) 
  11.     @Override 
  12.     public void onReadBuffer(ByteBuffer byteBuffer, BufferInfo bufferInfo, int i) { 
  13.         byte[] bytes = new byte[bufferInfo.size];//自定義數(shù)組用來(lái)存放輸出數(shù)據(jù) 
  14.         byteBuffer.get(bytes);// 從緩沖區(qū)的 byteBuffer 中獲取數(shù)據(jù) 
  15.     } 
  16. }; 
  1. //安卓MediaCodec編解碼: 
  2. ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); 
  3. ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); 
  4. //放入處理數(shù)據(jù) 
  5. int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); 
  6. ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex);//獲取編碼器傳入數(shù)據(jù)ByteBuffer 
  7. inputBuffer.clear();//清除以前數(shù)據(jù) 
  8. inputBuffer.put(PCMbuffer);//PCMbuffer需要編碼器處理數(shù)據(jù) 
  9. mediaCodec.queueInputBuffer(inputBufferIndex, 0, inputBuffer.limit(), 0, 0);//通知編碼器,數(shù)據(jù)放入 
  10.  
  11. //處理完成數(shù)據(jù) 
  12. int outputBufferIndex = mediaCodec.dequeueOutputBuffer(timeoutUs); 
  13. while (outputBufferIndex >= 0) { 
  14. outputBuffers = mediaCodec.getOutputBuffer(outputBufferIndex );//獲取編碼數(shù)據(jù) 
  15. //outputBuffer 編碼器處理完成的數(shù)據(jù) 
  16.      mediaCodec.releaseOutputBuffer(outputBufferIndex , false);//告訴編碼器數(shù)據(jù)處理完成 
  17.      outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 1000);//可能一次放入的數(shù)據(jù)處理會(huì)輸出多個(gè)數(shù)據(jù)  

(1)獲取輸出數(shù)據(jù)的方式

先簡(jiǎn)單解釋一下安卓中編解碼器的原理,可結(jié)合圖6理解。當(dāng)請(qǐng)求(或接收)一個(gè)空的輸入緩沖區(qū)(input buffers)時(shí),先將待處理的數(shù)據(jù)填充到這個(gè)緩沖區(qū),并將其發(fā)送到編解碼器進(jìn)行處理;然后編解碼器將處理完成后的數(shù)據(jù)填充到空的輸出緩沖區(qū)(output buffers);這樣就可以請(qǐng)求(或接收)輸出緩沖區(qū)中的數(shù)據(jù),在數(shù)據(jù)獲取完成之后再將緩沖區(qū)釋放掉。 在請(qǐng)求輸出緩沖區(qū)數(shù)據(jù)的過(guò)程中,通過(guò)while循環(huán)驗(yàn)證輸出緩沖區(qū)的索引(outputBufferIndex)是否大于等于0,當(dāng)滿足上述條件時(shí),表示可以讀取輸出緩沖區(qū)的數(shù)據(jù),否則一直等待輸出緩沖區(qū)的數(shù)據(jù)。

鴻蒙開(kāi)源全場(chǎng)景應(yīng)用開(kāi)發(fā)——視頻編解碼-鴻蒙HarmonyOS技術(shù)社區(qū)

圖6 安卓編解碼器原理圖(來(lái)源于網(wǎng)絡(luò),侵權(quán)必刪)

在鴻蒙中,也需要使用輸入緩存區(qū)(mBuffer)和輸出緩存區(qū)(byteBuffer)來(lái)裝載數(shù)據(jù),但是在請(qǐng)求輸出緩沖區(qū)數(shù)據(jù)的過(guò)程中,鴻蒙采用了編解碼監(jiān)聽(tīng)的方式。通過(guò)ICodecListener類來(lái)監(jiān)聽(tīng)編解碼器的數(shù)據(jù)輸出,當(dāng)可以從輸出緩存區(qū)獲取輸出數(shù)據(jù)時(shí),可在重寫(xiě)方法onReadBuffer(ByteBuffer byteBuffer, BufferInfo bufferInfo, int i)中獲取數(shù)據(jù),在數(shù)據(jù)獲取完成之后再將緩沖區(qū)釋放掉。

(2)Index緩沖區(qū)索引的使用

安卓與鴻蒙端的另一區(qū)別是,安卓在處理輸入數(shù)據(jù)時(shí)還使用了一組對(duì)應(yīng)的dequeueInputBuffer()和queueInputBuffer()方法用來(lái)處理輸入數(shù)據(jù)流,標(biāo)記緩沖區(qū)索引。其中,dequeueInputBuffer()用于返回輸入緩沖區(qū)的索引;queueInputBuffer()用于告知編碼器數(shù)據(jù)已經(jīng)被放入指定的輸入緩沖區(qū)中,這樣才可以正確釋放輸入緩沖區(qū)。同理,在處理輸出數(shù)據(jù)時(shí)也會(huì)使用一組實(shí)現(xiàn)原理相同的方法dequeueOutputBuffer()和queueOutputBuffer(),此處不進(jìn)行贅述。

鴻蒙端并未采用上述緩沖區(qū)索引的概念,因此視頻編解碼的過(guò)程更加流暢精簡(jiǎn)。

想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

 

責(zé)任編輯:jianghua 來(lái)源: 鴻蒙社區(qū)
相關(guān)推薦

2021-10-08 10:02:50

鴻蒙HarmonyOS應(yīng)用

2021-09-28 14:38:44

鴻蒙HarmonyOS應(yīng)用

2023-08-23 14:53:05

火山引擎視頻云視頻編解碼

2022-03-24 15:00:09

Harmony編解碼鴻蒙

2020-03-30 10:50:17

AI 數(shù)據(jù)人工智能

2024-02-28 08:22:07

2020-12-21 06:58:12

Web安全編解碼工具

2021-10-08 09:38:57

NettyChannelHand架構(gòu)

2021-06-11 21:46:31

RocketMQ數(shù)據(jù)JSON
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)