Android MediaCodec轉(zhuǎn)碼相關(guān)問(wèn)題
最近在測(cè)試各種編碼的性能的時(shí)候涉及到了android硬編,由于測(cè)試的指標(biāo)有PSNR、SSIM等質(zhì)量指標(biāo),中間遇到了一些坑,稍微記錄一下。
硬編的常見用法
大多數(shù)實(shí)際工程中,硬解或者硬編都是通過(guò)Surface中轉(zhuǎn)來(lái)做的,經(jīng)過(guò)Surface我們可以做很多事情,比如這個(gè)Surface可以是SurfaceView的Surface,或者我們想用Opengl作一些事情,加濾鏡或者編輯視頻,Surface的方式都提供了方便的解決方式,這也是我們搜索MediaCodec,最常見的使用方式。bigflake(http://bigflake.com/mediacodec)列舉出了很多示例代碼,其中有一個(gè)google CTS代碼DecodeEditEncode對(duì)于Surface的使用給出了很好的例子。
如果我們想做一個(gè)硬解硬編的加速轉(zhuǎn)碼(至少相較ffmpeg軟解軟編還是快一些的),可以參考github項(xiàng)目android-transcoder,這里給出了比較完整的transcode的示例,但是真正想用起來(lái)還是需要不少改動(dòng)的,比如給出的Strategy只支持幾個(gè)簡(jiǎn)單的比例,Cancel接口有些問(wèn)題,這里不具體說(shuō)了。
我最初測(cè)試的有一個(gè)很主要的目的是測(cè)試PSNR等指標(biāo),最初使用了ypresto的demo,但是最終得到的結(jié)果總是特別差,比如x264轉(zhuǎn)碼psnr能維持在40db的情況,經(jīng)過(guò)Surface android硬解然后硬編基本智能再25db左右(我們最終對(duì)比PSNR是將結(jié)果mp4解碼為YUV,然后直接再YUV三個(gè)channel上計(jì)算平均PSNR)。但是肉眼看起來(lái)其實(shí)硬編結(jié)果和x264甚至和原視頻相差并不大,
硬編轉(zhuǎn)碼PSNR很低的問(wèn)題所在
想到自己平時(shí)寫shader用gpu加速來(lái)進(jìn)行yuv轉(zhuǎn)rgb的時(shí)候用到了yuv轉(zhuǎn)RGB的轉(zhuǎn)換公式,可以參考另外一篇記錄color space2中YUV families的介紹,或者直接看一個(gè)綜合的整理的bt601 & bt709 color matrix,比如bt601 tv range的YUV轉(zhuǎn)RGB的公式為:
看一下這里的YUV2RGB的轉(zhuǎn)換公式,得到的RGB是可能有負(fù)值的,比如0, 255, 255得到的RGB就是負(fù)值,這里可能有人會(huì)說(shuō)這個(gè)公式是針對(duì)tv range(也有叫studio range的,就是Y 16-235,UV 16-240),我說(shuō)一下原因:
- 1,很多情況下尤其是國(guó)內(nèi)的很多廠商錄制出來(lái)的視頻根本不帶著color range信息,即使帶著,經(jīng)過(guò)各種渠道的轉(zhuǎn)存流轉(zhuǎn),基本什么信息也都沒(méi)有了
- 2,實(shí)際上硬解中是有KEY_COLOR_RANGE參數(shù)的,能設(shè)置兩種值COLOR_RANGE_FULL和COLOR_RANGE_LIMITIED,就是對(duì)應(yīng)著full range和tv range,但是更坑爹的是很多國(guó)內(nèi)廠商根本沒(méi)有做實(shí)現(xiàn)。這里表?yè)P(yáng)一下良心華為,錄制出來(lái)的視頻都有著完整的color range、color space、color transfer信息,華為錄制出來(lái)的基本都是tv range視頻,在<=480p的時(shí)候使用bt601,否則使用HDTV標(biāo)準(zhǔn)bt709,蘋果錄制出來(lái)的m4v文件也是比較良心的。測(cè)試了vivo x7錄制出來(lái)的視頻這幾個(gè)信息都是空著的,難道公司的主要精力都放在了“2000W柔光自拍”上?
- 3,即使我們使用tv range內(nèi)的YUV值,比如(16,240,240)也會(huì)算出來(lái)的Green是負(fù)值。其實(shí)tv range中的很多YUV值都會(huì)算出來(lái)RGB值是負(fù)的
回歸正題,我們顯示任何東西,最終都是以RGB的形式顯示的,也就是說(shuō)我們?cè)赟urface中的顯示也是RGBA的color space,但是實(shí)際上上RGBA的值都必須是正值,負(fù)值是無(wú)法顯示的,所以我們自己寫shader的時(shí)候或者直接交給Surface處理的時(shí)候,需要對(duì)RGBA的值做clamp處理,裁剪到0-255范圍內(nèi),也就是負(fù)值變成0,超過(guò)255的變成255。然后我們對(duì)這個(gè)clamp以后的RGB值如果再轉(zhuǎn)回YUV值,就跟原始的YUV值相差比較大了。有人會(huì)覺(jué)得這樣不會(huì)變色了嗎?其實(shí)并不會(huì)!舉個(gè)例子:
以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還會(huì)不會(huì)變化
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)換完畢的值
上面這個(gè)例子我們發(fā)現(xiàn)(0,0,0) (79,83,71)實(shí)際對(duì)應(yīng)的RGB是一樣的,也就是人眼看起來(lái)是一樣的。但是這樣的處理過(guò)程對(duì)我們比較PSNR造成了很大的問(wèn)題,計(jì)算出來(lái)的PSNR很低。x264進(jìn)行轉(zhuǎn)碼卻不會(huì)有這個(gè)問(wèn)題,還沒(méi)有自習(xí)看ff和x264轉(zhuǎn)碼的源碼,有待確認(rèn)具體是怎么個(gè)實(shí)現(xiàn),猜測(cè)是沒(méi)有對(duì)RGB做clamp,或者根本沒(méi)有中轉(zhuǎn)RGB。
其他方式做轉(zhuǎn)碼
在api 21后,android提供了Image類來(lái)做硬解的輸出和硬編的輸入,Image類基本類似于ffmpeg的AVFrame,里面包含幀圖像的各種信息和各個(gè)plane,以及stride、width、height、crop x、crop y等信息,也就是說(shuō)我們可以從Image中得到decode以后的YUV raw data,我們可以這樣的到decode 出來(lái)的Image:
int result = mDecoder.dequeueOutputBuffer(mBufferInfo, timeout);
Image image = mDecoder.getOutputImage(result);
對(duì)于decode以后直接存儲(chǔ)為YUV raw data file可以參考VideoToFrames github code(對(duì)應(yīng)的博客是android高效解碼得到Y(jié)UV file)
未完待續(xù)
References
- Image class & YUV_420_888
- YUV_420_888 convert to NV21 & I420
- Anroid MediaCodec stuffs
- VideoEncoderDecoderTest
- Camera and MediaCodec colorspace not match, with images, stack over flow issue
- color format of camera and mediacodec, google disscuss
- CTS samples EncodeDecodeTest
- How to get stride of encoder
- ffmpeg command: how to convert to YUV file and how to display it
- VideoToYUVFrames github sample code
- Android MediaCodec transcoder sample demo, with surface
- NV21 NV12 I420 YV12
- CTS codecUtils
- google CTS
- YUV RGB convert
- android mediacodec color formats
- CTS code: DecodeEditEncodeTest