Android硬編轉(zhuǎn)碼相關(guān)問題

Android MediaCodec轉(zhuǎn)碼相關(guān)問題


最近在測試各種編碼的性能的時候涉及到了android硬編,由于測試的指標有PSNR、SSIM等質(zhì)量指標,中間遇到了一些坑,稍微記錄一下。

硬編的常見用法

大多數(shù)實際工程中,硬解或者硬編都是通過Surface中轉(zhuǎn)來做的,經(jīng)過Surface我們可以做很多事情,比如這個Surface可以是SurfaceView的Surface,或者我們想用Opengl作一些事情,加濾鏡或者編輯視頻,Surface的方式都提供了方便的解決方式,這也是我們搜索MediaCodec,最常見的使用方式。bigflake(http://bigflake.com/mediacodec)列舉出了很多示例代碼,其中有一個google CTS代碼DecodeEditEncode對于Surface的使用給出了很好的例子。

如果我們想做一個硬解硬編的加速轉(zhuǎn)碼(至少相較ffmpeg軟解軟編還是快一些的),可以參考github項目android-transcoder,這里給出了比較完整的transcode的示例,但是真正想用起來還是需要不少改動的,比如給出的Strategy只支持幾個簡單的比例,Cancel接口有些問題,這里不具體說了。

我最初測試的有一個很主要的目的是測試PSNR等指標,最初使用了ypresto的demo,但是最終得到的結(jié)果總是特別差,比如x264轉(zhuǎn)碼psnr能維持在40db的情況,經(jīng)過Surface android硬解然后硬編基本智能再25db左右(我們最終對比PSNR是將結(jié)果mp4解碼為YUV,然后直接再YUV三個channel上計算平均PSNR)。但是肉眼看起來其實硬編結(jié)果和x264甚至和原視頻相差并不大,

硬編轉(zhuǎn)碼PSNR很低的問題所在

想到自己平時寫shader用gpu加速來進行yuv轉(zhuǎn)rgb的時候用到了yuv轉(zhuǎn)RGB的轉(zhuǎn)換公式,可以參考另外一篇記錄color space2中YUV families的介紹,或者直接看一個綜合的整理的bt601 & bt709 color matrix,比如bt601 tv range的YUV轉(zhuǎn)RGB的公式為:

bt601 tv range YUV2RGB

bt601 tv range RGB2YUV

看一下這里的YUV2RGB的轉(zhuǎn)換公式,得到的RGB是可能有負值的,比如0, 255, 255得到的RGB就是負值,這里可能有人會說這個公式是針對tv range(也有叫studio range的,就是Y 16-235,UV 16-240),我說一下原因:

  • 1,很多情況下尤其是國內(nèi)的很多廠商錄制出來的視頻根本不帶著color range信息,即使帶著,經(jīng)過各種渠道的轉(zhuǎn)存流轉(zhuǎn),基本什么信息也都沒有了
  • 2,實際上硬解中是有KEY_COLOR_RANGE參數(shù)的,能設(shè)置兩種值COLOR_RANGE_FULL和COLOR_RANGE_LIMITIED,就是對應(yīng)著full range和tv range,但是更坑爹的是很多國內(nèi)廠商根本沒有做實現(xiàn)。這里表揚一下良心華為,錄制出來的視頻都有著完整的color range、color space、color transfer信息,華為錄制出來的基本都是tv range視頻,在<=480p的時候使用bt601,否則使用HDTV標準bt709,蘋果錄制出來的m4v文件也是比較良心的。測試了vivo x7錄制出來的視頻這幾個信息都是空著的,難道公司的主要精力都放在了“2000W柔光自拍”上?
  • 3,即使我們使用tv range內(nèi)的YUV值,比如(16,240,240)也會算出來的Green是負值。其實tv range中的很多YUV值都會算出來RGB值是負的

回歸正題,我們顯示任何東西,最終都是以RGB的形式顯示的,也就是說我們在Surface中的顯示也是RGBA的color space,但是實際上上RGBA的值都必須是正值,負值是無法顯示的,所以我們自己寫shader的時候或者直接交給Surface處理的時候,需要對RGBA的值做clamp處理,裁剪到0-255范圍內(nèi),也就是負值變成0,超過255的變成255。然后我們對這個clamp以后的RGB值如果再轉(zhuǎn)回YUV值,就跟原始的YUV值相差比較大了。有人會覺得這樣不會變色了嗎?其實并不會!舉個例子:

以yuv(0,0,0)為例(也可以拿16,240,240試試)
1. 先減128 。 (0,-128,-128)
2. 轉(zhuǎn)換為RGB并作clamp  RGB (-179.456,135.424,-226.816)->(0,135,0)
3. 轉(zhuǎn)YUV (79.245,-44.685,-56.565)
4. 色度加128 YUV(79.245,83.315,71.435)
5. 取整,YUV(79,83,71)  轉(zhuǎn)換完畢的值
再轉(zhuǎn)回去:
6. 先減128  YUV (79,-45,-57)
7. 轉(zhuǎn)換為RGB并作clamp  RGB (-0.914,135.178,-0.74)->(0,135,0)
我們發(fā)現(xiàn)顏色是一樣的,再轉(zhuǎn)一下,看看YUV還會不會變化
8. 轉(zhuǎn)YUV (79.245,-44.685,-56.565)
9. 色度加128 YUV(79.245,83.315,71.435)
10. 得到Y(jié)UV(79,83,71)  轉(zhuǎn)換完畢的值

上面這個例子我們發(fā)現(xiàn)(0,0,0) (79,83,71)實際對應(yīng)的RGB是一樣的,也就是人眼看起來是一樣的。但是這樣的處理過程對我們比較PSNR造成了很大的問題,計算出來的PSNR很低。x264進行轉(zhuǎn)碼卻不會有這個問題,還沒有自習看ff和x264轉(zhuǎn)碼的源碼,有待確認具體是怎么個實現(xiàn),猜測是沒有對RGB做clamp,或者根本沒有中轉(zhuǎn)RGB。

其他方式做轉(zhuǎn)碼

在api 21后,android提供了Image類來做硬解的輸出和硬編的輸入,Image類基本類似于ffmpeg的AVFrame,里面包含幀圖像的各種信息和各個plane,以及stride、width、height、crop x、crop y等信息,也就是說我們可以從Image中得到decode以后的YUV raw data,我們可以這樣的到decode 出來的Image:

int result = mDecoder.dequeueOutputBuffer(mBufferInfo, timeout);
Image image = mDecoder.getOutputImage(result);

對于decode以后直接存儲為YUV raw data file可以參考VideoToFrames github code(對應(yīng)的博客是android高效解碼得到Y(jié)UV file)

未完待續(xù)


References

  1. Image class & YUV_420_888
  2. YUV_420_888 convert to NV21 & I420
  3. Anroid MediaCodec stuffs
  4. VideoEncoderDecoderTest
  5. Camera and MediaCodec colorspace not match, with images, stack over flow issue
  6. color format of camera and mediacodec, google disscuss
  7. CTS samples EncodeDecodeTest
  8. How to get stride of encoder
  9. ffmpeg command: how to convert to YUV file and how to display it
  10. VideoToYUVFrames github sample code
  11. Android MediaCodec transcoder sample demo, with surface
  12. NV21 NV12 I420 YV12
  13. CTS codecUtils
  14. google CTS
  15. YUV RGB convert
  16. android mediacodec color formats
  17. CTS code: DecodeEditEncodeTest
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文地址 Android MediaCodec stuff 這篇文章是關(guān)于 MediaCodec 這一系列類,它主...
    sheepm閱讀 69,131評論 17 102
  • MediaCodec的官方文檔 一、Android MediaCodec簡單介紹 Android中可以使用Medi...
    黃海佳閱讀 6,333評論 1 17
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,725評論 25 709
  • 一、文章說明 最近工作實在太忙,很久沒有更新文章了,收到很多小伙伴催更的消息,心中實在慚愧,趁著今天有空趕緊更新。...
    風從影閱讀 19,293評論 33 118
  • 昨天是陶藝培訓(xùn)第一天,本想昨晚睡覺前總結(jié)的,可惜累癱了,躺在床上動都不想動~昨天的課程內(nèi)容安排的不算多,三大塊——...
    美禾hm閱讀 432評論 0 1

友情鏈接更多精彩內(nèi)容