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

鴻蒙開源全場景應用開發(fā)—通訊協(xié)議

開發(fā)
前幾期內(nèi)容中,我們對視頻編解碼和視頻渲染模塊的實現(xiàn)原理進行了解析。本期將繼續(xù)為大家講解通訊協(xié)議并簡要概述安卓美顏濾鏡的實現(xiàn)原理。

[[426394]]

想了解更多內(nèi)容,請訪問:

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

https://harmonyos.51cto.com

前言

前文提到過,已開發(fā)的家庭合影美顏相機應用是同時基于鴻蒙和安卓設備的,我們將對其包含的4個功能模塊即視頻編解碼、視頻渲染、通訊協(xié)議和美顏濾鏡進行拆分講解。

前幾期內(nèi)容中,我們對視頻編解碼和視頻渲染模塊的實現(xiàn)原理進行了解析。本期將繼續(xù)為大家講解通訊協(xié)議并簡要概述安卓美顏濾鏡的實現(xiàn)原理。

背景

RTP是用于Internet上針對流媒體傳輸?shù)囊环N基礎協(xié)議,在一對一或一對多的傳輸情況下工作,其目的是提供時間信息和實現(xiàn)流同步。它可以建立在底層的面向連接和非連接的傳輸協(xié)議上,一般使用UDP協(xié)議進行傳輸。從一個同步源發(fā)出的RTP分組序列稱為流,一個RTP會話可能包含多個RTP流。

應用效果展示

1.家庭合影美顏相機應用效果回顧

先來帶大家一起回顧下上期內(nèi)容講解的家庭合影美顏相機應用。

此應用能夠?qū)Ⅷ櫭纱笃僚臄z的視頻數(shù)據(jù)實時傳輸?shù)桨沧渴謾C上;并在安卓端為其添加濾鏡,再將處理后的視頻數(shù)據(jù)傳回到鴻蒙大屏進行渲染顯示,從而實現(xiàn)鴻蒙大屏美顏拍照的功能,應用運行后的動態(tài)場景效果可以參考圖1。

圖中下方豎屏顯示的是安卓手機,上方橫屏顯示的是鴻蒙手機(由于實驗環(huán)境缺少搭載鴻蒙系統(tǒng)的大屏設備,因此我們使用鴻蒙手機替代大屏設備模擬實驗場景),其顯示的是視頻解碼后渲染的效果。

鴻蒙開源全場景應用開發(fā)——通訊協(xié)議-鴻蒙HarmonyOS技術社區(qū)

圖1 家庭合影美顏相機應用運行效果圖

2.RTP傳輸Demo效果

為了更清晰地講解通訊協(xié)議,我們將家庭合影美顏相機應用中數(shù)據(jù)傳輸部分拆分出來,形成了一個RTP傳輸Demo,并進行了功能整理和優(yōu)化,將原本的視頻傳輸改為了圖像傳輸,視頻是由多幀圖像構成,傳輸數(shù)據(jù)類型的改變不會影響RTP傳輸原理和步驟。RTP傳輸Demo的運行效果圖如圖2所示,上圖為發(fā)送端效果,下圖為接收端效果。

成功安裝并打開應用后,在發(fā)送端點擊藍色按鈕,發(fā)送開發(fā)者選中的特定區(qū)域的圖片數(shù)據(jù);在接收端點擊粉色按鈕,接收發(fā)送端剛發(fā)送的圖片數(shù)據(jù),并在按鈕下方顯示。

鴻蒙開源全場景應用開發(fā)——通訊協(xié)議-鴻蒙HarmonyOS技術社區(qū)
鴻蒙開源全場景應用開發(fā)——通訊協(xié)議-鴻蒙HarmonyOS技術社區(qū)

圖2 RTP傳輸Demo運行效果圖(上發(fā)送端,下接收端)

RTP傳輸原理及步驟解析

接下來為大家重點解析RTP傳輸?shù)膶崿F(xiàn)原理和步驟。

RTP傳輸Demo的原理流程可參考圖3。在鴻蒙發(fā)送端(服務端),設置需要傳輸?shù)膱D像數(shù)據(jù),通過無線網(wǎng)絡,使用RTP協(xié)議和Socket點對點的數(shù)據(jù)通信方式,發(fā)送到鴻蒙接收端(客戶端)。

在鴻蒙接收端(客戶端),接收到發(fā)送端發(fā)來的圖像數(shù)據(jù)后,進行圖像繪制。接下來將針對RTP傳輸Demo的實現(xiàn)步驟進行解析。

鴻蒙開源全場景應用開發(fā)——通訊協(xié)議-鴻蒙HarmonyOS技術社區(qū)

圖3 RTP傳輸原理流程圖

服務端數(shù)據(jù)發(fā)送

在服務端,將待發(fā)送的圖像置于resources->base->media文件夾下,如圖4所示。然后對待發(fā)送的圖像數(shù)據(jù)進行格式轉(zhuǎn)換。通過無線網(wǎng)絡,使用RTP協(xié)議和Socket點對點的數(shù)據(jù)通信方式,將圖像數(shù)據(jù)傳輸至鴻蒙接收端。

鴻蒙開源全場景應用開發(fā)——通訊協(xié)議-鴻蒙HarmonyOS技術社區(qū)

圖4 圖片在項目結構中的位置

服務端的數(shù)據(jù)發(fā)送流程包含以下三個步驟:

步驟1. 通過資源ID獲取位圖對象;

步驟2. 將位圖指定區(qū)域像素進行格式轉(zhuǎn)換;

步驟3. 數(shù)據(jù)傳輸;

(1)通過資源ID獲取位圖對象

通過getResource()方法,以資源IDdrawableID對象作為入?yún)?,獲取資源輸入流drawableInputStream;實例化圖像設置類ImageSource.SourceOptions對象,并設置圖像源格式為png;創(chuàng)建圖像源,參數(shù)為資源輸入流和圖像源ImageSource類對象;實例化圖像參數(shù)類DecodingOptions的對象,為其初始化圖像尺寸、區(qū)域并設置位圖格式;根據(jù)像參數(shù)類對象decodingOptions,通過圖像源ImageSource類對象創(chuàng)建位圖對象;返回位圖對象。

  1. //通過資源ID獲取位圖對象 
  2. private PixelMap getPixelMap(int drawableId) { 
  3.     InputStream drawableInputStream = null
  4.     try { 
  5.         //以資源ID作為入?yún)?,獲取資源輸入流 
  6.         drawableInputStream=this.getResourceManager().getResource(drawableId); 
  7.         //實例化圖像源ImageSource類對象 
  8.         ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions(); 
  9.         sourceOptions.formatHint = "image/png";//設置圖像源格式 
  10.         //創(chuàng)建圖像源,參數(shù)為資源輸入流和圖像源ImageSource類對象 
  11.         ImageSource imageSource = ImageSource.create(drawableInputStream, sourceOptions); 
  12.         //實例化圖像源解碼操作DecodingOptions類對象 
  13.         ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions(); 
  14.         decodingOptions.desiredSize = new Size(0, 0);//設置圖像尺寸 
  15.         decodingOptions.desiredRegion = new Rect(0, 0, 0, 0); 
  16.         decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;//設置位圖格式 
  17.         PixelMap pixelMap = imageSource.createPixelmap(decodingOptions);//根據(jù)解碼操作類對象,創(chuàng)建位圖 
  18.         return pixelMap;//返回位圖 
  19.     } 
  20.     ... 

(2)將位圖指定區(qū)域像素進行格式轉(zhuǎn)換

在得到位圖對象后,實例化矩形Rect矩形類對象,用于為開發(fā)者選中特定的圖像區(qū)域(該區(qū)域應不大于resources->base->media路徑下圖像的大小);通過位圖對象pixelMap調(diào)用readPixels()方法將指定區(qū)域像素轉(zhuǎn)換為int[]類型數(shù)據(jù);調(diào)用intToBytes()方法再將int[]類型數(shù)據(jù)格式轉(zhuǎn)換為byte類型數(shù)據(jù)。

  1. // 讀取指定區(qū)域像素 
  2. Rect region = new Rect(0, 0, 30, 30);//實例化舉行類對象,規(guī)定指定區(qū)域 
  3. pixelMap.readPixels(pixelArray,0,30,region);//將指定區(qū)域像素轉(zhuǎn)換為int[]類型數(shù)據(jù) 
  4. pic = intToBytes(pixelArray);//將int[]類型數(shù)據(jù)昂虎子你換位byte類型數(shù)據(jù) 

 (3)數(shù)據(jù)傳輸

實例化RTP發(fā)送類對象RtpSenderWrapper,將IP地址設置為接收端手機IP地址,端口號設置為5005;調(diào)用sendAvcPacket()方法發(fā)送圖像數(shù)據(jù)。

由于對RTP傳輸?shù)臄?shù)據(jù)類型做了簡化,因此圖像RTP傳輸會相對容易,而如果是原應用中的視頻RTP傳輸,則需要逐幀對視頻數(shù)據(jù)進行格式轉(zhuǎn)換,并將從攝像頭獲取的YUV類型的原始視頻數(shù)據(jù)壓縮為h264類型的視頻數(shù)據(jù),以方便Socket進行傳輸。

  1. mRtpSenderWrapper = new RtpSenderWrapper("192.168.31.12", 5005, false); 
  2. mRtpSenderWrapper.sendAvcPacket(pic, 0, pic.length, 0);//發(fā)送數(shù)據(jù) 

客戶端接收數(shù)據(jù)

在發(fā)送端通過RTP協(xié)議成功發(fā)送數(shù)據(jù)后,接收端就可以正常開始接收了。發(fā)送端接收數(shù)據(jù)的流程主要分為以下5個步驟:

步驟1. 創(chuàng)建數(shù)據(jù)接收線程;

步驟2. 接收數(shù)據(jù);

步驟3. 在線程間進行數(shù)據(jù)傳遞;

步驟4. 處理位圖數(shù)據(jù)得到pixelMapHolder;

步驟5. 繪制圖像。

(1)創(chuàng)建數(shù)據(jù)接收線程

創(chuàng)建子線程作為數(shù)據(jù)接收線程。

  1. new Thread(new Runnable())//新開一個數(shù)據(jù)接收線程 

 (2)接收數(shù)據(jù)

在子線程接收線程中,實例化數(shù)據(jù)包DatagramPacket;通過Socket類對象調(diào)用receive()方法,接收發(fā)送端的數(shù)據(jù)到數(shù)據(jù)包DatagramPacket中;通過數(shù)據(jù)包DatagramPacket調(diào)用getData()方法獲取數(shù)據(jù)包中的RTP數(shù)據(jù)。

  1. datagramPacket = new DatagramPacket(data,data.length);//實例化數(shù)據(jù)包 
  2. socket.receive(datagramPacket);//接收數(shù)據(jù)到數(shù)據(jù)包中 
  3. rtpData = datagramPacket.getData();獲取數(shù)據(jù)包中的RTP數(shù)據(jù) 

 (3)在線程間進行數(shù)據(jù)傳遞

待子線程拿到RTP發(fā)送數(shù)據(jù)后,需要將RTP數(shù)據(jù)從子線程傳遞到主線程。這就涉及到線程間的數(shù)據(jù)傳遞。在此應用中,我們使用了Java類的SynchronousQueue并發(fā)隊列來實現(xiàn)子線程和主線程間的數(shù)據(jù)傳遞。先實例化一個byte[]類型的并發(fā)隊列SynchronousQueue類對象;將h264類型的數(shù)據(jù)放入并發(fā)隊列中;再從隊列中獲取數(shù)據(jù)。

  1. SynchronousQueue<byte[]> queue = new SynchronousQueue<byte[]>();//實例化byte[]類型的并發(fā)隊列 
  2. queue.put(h264Data);//將h264類型的數(shù)據(jù)放入并發(fā)隊列中 
  3. rgbData = queue.take();//從隊列中獲取數(shù)據(jù) 

(4)處理解碼后的位圖數(shù)據(jù)得到PixelMapHolder

主線程從隊列中拿到圖像RGB數(shù)據(jù)后即可進行圖像繪制。PixelMap是接收得到的位圖數(shù)據(jù),PixelMapHolder 使用 PixelMap 生成渲染后端所需的數(shù)據(jù),并提供數(shù)據(jù)作為 Canvas 中方法的輸入?yún)?shù)。因此為了后續(xù)能夠?qū)ξ粓D進行渲染,需要在圖像數(shù)據(jù)從子線程傳遞到主線程后,將圖像數(shù)據(jù)pixelmap轉(zhuǎn)換為pixelMapHolder類對象,即在實例化pixelMapHolder類對象時,將pixelmap位圖數(shù)據(jù)作為入?yún)魅雽嵗椒ㄖ小?/p>

  1. public void putPixelMap(PixelMap pixelMap){ 
  2.         if (pixelMap != null) {//判斷接收到的位圖數(shù)據(jù)是否為空 
  3.             rectSrc = new RectFloat(0, 0, pixelMap.getImageInfo().size.width, pixelMap.getImageInfo().size.height); 
  4.             pixelMapHolder = new PixelMapHolder(pixelMap);//實例化PixelMapHolder類對象 
  5.         }else
  6.             pixelMapHolder = null;//若接收到的位圖為空,則全部置為空 
  7.             setPixelMap(null); 
  8.         } 
  9.     } 

 (5)繪制圖像

實例化一個矩形Rect類對象,設置圖像信息并規(guī)定指定的區(qū)域如寬和高;添加一個同步繪制任務,先判斷pixelMapHolder是否為空,若為空則直接返回,不為空則開始繪制任務;在繪制任務中,調(diào)用drawPixelMapHolderRoundRectShape()方法將PixelMapHolder類對象繪制到實例化得到的矩形Rect類對象中,并設置其為圓角效果;其位置由rectDst指定;繪制完成后釋放pixelMapHolder,將其置為空。

  1. private void onDraw(){ 
  2.     this.addDrawTask((view, canvas) -> { //添加繪制任務 
  3.         if (pixelMapHolder == null){//判斷pixelMapHolder是否為空 
  4.             return
  5.         } 
  6.         synchronized (pixelMapHolder) {//在同步任務中繪制圖像 
  7.             canvas.drawPixelMapHolderRoundRectShape(pixelMapHolder, rectSrc, rectDst, radius, radius);//繪制圖像為圓角效果 
  8.             pixelMapHolder = null;//繪制完成后將pixelMapHolder釋放 
  9.         } 
  10.     }); 

安卓端美顏濾鏡效果實現(xiàn)

美顏濾鏡的部分我們參考了GitHub上的開源項目(https://github.com/google/grafika、https://github.com/cats-oss/android-gpuimage、https://github.com/wuhaoyu1990/MagicCamera),使用GPU著色器實現(xiàn)添加濾鏡和切換濾鏡的效果。由于不涉及鴻蒙的能力,此部分不作為重點講述,只簡要概括下其實現(xiàn)流程,可分為如下5個步驟:

(1)設置不同的濾鏡

使用著色器語言,設置所需的多種代碼。

鴻蒙開源全場景應用開發(fā)——通訊協(xié)議-鴻蒙HarmonyOS技術社區(qū)

圖5 美顏相機使用的濾鏡

(2)opengl繪制;

  1. import android.opengl.GLES20; 
  2. ... 
  3. // add the vertex shader to program 
  4. GLES20.glAttachShader(mProgram, vertexShader);    
  5. // add the fragment shader to program 
  6. GLES20.glAttachShader(mProgram, fragmentShader);  
  7. // creates OpenGL ES program executables 
  8. GLES20.glLinkProgram(mProgram); 

 (3)添加濾鏡;

  1. private List<FilterFactory.FilterType>filters = new ArrayList<>(); 
  2.   ... 
  3.   filters.add(FilterFactory.FilterType.Original); 
  4.   filters.add(FilterFactory.FilterType.Sunrise); 
  5.   ... 

 (4)開啟或關閉美顏濾鏡;

  1. mCameraView.enableBeauty(true); 

 (5)設置美顏程度;

  1. mCameraView.setBeautyLevel(0.5f); 

 (6)設置切換濾鏡和切換鏡頭,再設置相機拍攝和拍攝完成后的回調(diào)即可。

  1. mCameraView.updateFilter(filters.get(pos));//切花濾鏡 
  2. mCameraView.switchCamera();//切換鏡頭 

想了解更多內(nèi)容,請訪問:

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

https://harmonyos.51cto.com

 

責任編輯:jianghua 來源: 鴻蒙社區(qū)
相關推薦
點贊
收藏

51CTO技術棧公眾號