2013-04-01 68 views
6

通過參考Aegonis的work 1work 2,我也得到了H.264碼流,但顏色是不正確的。我正在使用HTC Butterfly進行開發。這裏是我的代碼部分:Mediacodec和攝像頭,色彩空間不正確

攝像頭:

parameters.setPreviewSize(width, height); 
parameters.setPreviewFormat(ImageFormat.YV12); 
parameters.setPreviewFrameRate(frameRate); 

MediaCodec:

mediaCodec = MediaCodec.createEncoderByType("video/avc"); 
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 320, 240); 
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 500000); 
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15); 
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); 
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); 
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
mediaCodec.start(); 

當使用COLOR_FormatYUV420Planar錯誤顯示「[OMX.qcom.video.encoder.avc]執行不支持彩色格式19「,所以我只能使用」COLOR_FormatYUV420SemiPlanar「。有誰知道爲什麼不支持的原因?

明白了,通過使用:

int colorFormat = 0; 
    MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType); 
    for (int i = 0; i < capabilities.colorFormats.length && colorFormat == 0; i++) { 
     int format = capabilities.colorFormats[i]; 
     Log.e(TAG, "Using color format " + format);   
    } 

我們可以有顏色格式(COLOR_FormatYUV420SemiPlanar)和(沒有相應的格式),我覺得格式將改變依賴於設備。

然後,我嘗試了顏色變換從建議中work 1work 2提供:

public static byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width, final int height) { 
    /* 
    * COLOR_TI_FormatYUV420PackedSemiPlanar is NV12 
    * We convert by putting the corresponding U and V bytes together (interleaved). 
    */ 
    final int frameSize = width * height; 
    final int qFrameSize = frameSize/4; 

    System.arraycopy(input, 0, output, 0, frameSize); // Y 

    for (int i = 0; i < qFrameSize; i++) { 
     output[frameSize + i*2] = input[frameSize + i + qFrameSize]; // Cb (U) 
     output[frameSize + i*2 + 1] = input[frameSize + i]; // Cr (V) 
    } 
    return output; 
} 

public static byte[] YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) { 
    /* 
    * COLOR_FormatYUV420Planar is I420 which is like YV12, but with U and V reversed. 
    * So we just have to reverse U and V. 
    */ 
    final int frameSize = width * height; 
    final int qFrameSize = frameSize/4; 

    System.arraycopy(input, 0, output, 0, frameSize); // Y 
    System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr (V) 
    System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb (U) 

    return output; 
} 

public static byte[] swapYV12toI420(byte[] yv12bytes, int width, int height) { 
    byte[] i420bytes = new byte[yv12bytes.length]; 
    for (int i = 0; i < width*height; i++) 
     i420bytes[i] = yv12bytes[i]; 
    for (int i = width*height; i < width*height + (width/2*height/2); i++) 
     i420bytes[i] = yv12bytes[i + (width/2*height/2)]; 
    for (int i = width*height + (width/2*height/2); i < width*height + 2*(width/2*height/2); i++) 
     i420bytes[i] = yv12bytes[i - (width/2*height/2)]; 
    return i420bytes; 
} 

顯然,YV12toYUV420PackedSemiPlanar執行比其他兩種更好的色彩變換。它相對更好,但與真實顏色相比仍然看起來不同。我的代碼有問題嗎?任何評論將不勝感激。

+1

+1以這樣的方式寫的問題。 –

+0

像色度通道的「不同」是落後的,還是「不一樣」的東西都是微妙的脫落? (如果你重新排列'YV12toYUV420PackedSemiPlanar'來交換Cb/Cr通道,它看起來是不是正確?) – fadden

+0

我試圖交換Cb/Cr並且顏色不正確。我也嘗試只顯示Y的顏色,看起來這個視頻已經放在了綠色的面具上,這並不像我期望的那樣。我真的無法弄清楚發生了什麼事。 – Albert

回答

3

心動不如行動,現在顏色很好看,測試是基於HTC蝴蝶。 當分辨率設置爲320×240,你的顏色變換應該看起來像:

System.arraycopy(input, 0, output, 0, frameSize); 
    for (int i = 0; i < (qFrameSize); i++) { 
     output[frameSize + i*2] = (input[frameSize + qFrameSize + i - 32 - 320]); 
     output[frameSize + i*2 + 1] = (input[frameSize + i - 32 - 320]);    
    } 

爲640×480以上的分辨率,

System.arraycopy(input, 0, output, 0, frameSize);  
    for (int i = 0; i < (qFrameSize); i++) { 
     output[frameSize + i*2] = (input[frameSize + qFrameSize + i]); 
     output[frameSize + i*2 + 1] = (input[frameSize + i]); 
    } 

對於幀速率的問題,我們可以使用getSupportedPreviewFpsRange()檢查我們的設備支持的幀速率範圍爲:

List<int[]> fpsRange = parameters.getSupportedPreviewFpsRange(); 
for (int[] temp3 : fpsRange) { 
System.out.println(Arrays.toString(temp3));} 

並且以下設置在播放編碼的H.264 ES時是正確的,

parameters.setPreviewFpsRange(29000, 30000);  
//parameters.setPreviewFpsRange(4000,60000);//this one results fast playback when I use the FRONT CAMERA 
+0

感謝您的回答,Albert。偉大的研究。我想知道爲什麼在編寫320x240分辨率的U和V分量時有-352字節的填充?我應該使用960x720分辨率的填充? –

+0

相似的問題:http://stackoverflow.com/questions/17493169/getting-qualcomm-encoders-to-work-via-mediacode-api/19883163似乎,當發送幀到編碼器,Y組件應該由一些數字對齊特定分辨率的字節數。 –

2

讀取this discussion後事實證明,用於編碼幀更一般化的方式不同的分辨率的 是由2048個字節發送幀到MediaCodec之前對準色度平面。這是QualCommOMX.qcom.video.encoder.avc)編碼器,我相信HTC蝴蝶有,但仍然不適用於所有分辨率。根據輸出視頻,720x480176x144仍然有色度平面偏移。此外,避免分辨率的大小不能被16除。

的轉變是非常簡單的:

int padding = 0; 
if (mediaCodecInfo.getName().contains("OMX.qcom")) { 
    padding = (width * height) % 2048; 
} 
byte[] inputFrameBuffer = new byte[frame.length]; 
byte[] inputFrameBufferWithPadding = new byte[padding + frame.length]; 

ColorHelper.NV21toNV12(frame, inputFrameBuffer, width, height); 
# copy Y plane 
System.arraycopy(inputFrameBuffer, 0, inputFrameBufferWithPadding, 0, inputFrameBuffer.length); 
int offset = width * height; 
# copy U and V planes aligned by <padding> boundary 
System.arraycopy(inputFrameBuffer, offset, inputFrameBufferWithPadding, offset + padding, inputFrameBuffer.length - offset); 
+0

(參見Andrey的文章http://stackoverflow.com/questions/20699009/) – fadden