2014-03-28 67 views
2

我的應用程序會覆蓋onPreviewFrame回調以將當前相機幀傳遞給webrtc本機函數。這工作完美,但我希望能夠切換到發送靜態幀,而不是視頻,如果該選項已被選中我的應用程序。如何用靜態YUV幀覆蓋預覽幀數據?

到目前爲止,我創建了一個YUV NV21鏡像,我將其存儲在資產目錄中。所有嘗試將該幀傳遞給本機函數都會導致紫色/綠色條紋而不是實際圖像。

這是我到目前爲止;

@Override 
public void onPreviewFrame(byte[] data, Camera camera) { 
    previewBufferLock.lock(); 

    if (mFrameProvider.isEnabled()) { 
     mFrameProvider.overwriteWithFrame(data, expectedFrameSize); 
    } 

    if (isCaptureRunning) { 
     if (data.length == expectedFrameSize) { 
      ProvideCameraFrame(data, expectedFrameSize, context); 
      cameraUtils.addCallbackBuffer(camera, data); 
     } 
    } 
    previewBufferLock.unlock(); 
} 


@Override 
public byte[] overwriteWithPreviewFrame(byte[] data, int expectedFrameSize) { 
    if (mFrameData == null) { 
     loadPreviewFrame(); 
    } 

    for (int i=0; i < expectedFrameSize; i++) { 
     if (i < mFrameData.length) { 
     data[i] = mFrameData[i]; 
     } 
    } 

    return data; 
} 

而且

private void loadPreviewFrame() { 
    try { 
     InputStream open = mContext.getResources().getAssets().open(PREVIEW_FRAME_FILE); 

     mFrameData = IOUtils.toByteArray(open); 
     open.close(); 
    } catch (Exception e) { 
     Log.e("", "", e); 
    } 
} 

我試圖將圖像轉換爲位圖了。所以問題是如何從資產中打開一個YUV幀並將其轉換爲合適的格式傳遞給本地方法。

結果如下輸出;右後

enter image description here

+0

如果您看到* an *圖片,那麼您顯然將取代相機輸出取得一些成功。你確定你的形象是正確的嗎? – fadden

+0

是的,我也嘗試過使用YUV示例框架,可以在網上找到.. :( – Lunar

+0

無論您提供的圖像是否紫色/綠色條紋輸出看起來都一樣嗎?(或者對於相同的圖像,後續運行不同)?試圖找出是否看到圖像的扭曲版本,或從其他地方亂碼數據 – fadden

回答

3

與Android API一個長期的鬥爭我設法得到這個工作。

有兩個問題導致綠色/紫色輸出;

數據丟失:生成的YUV幀在相同分辨率下大於原始預覽幀,因此向下傳遞到本機代碼的數據大約缺少30%的圖像數據。

錯誤分辨率:本機代碼需要預覽幀而不是相機的分辨率。

以下是任何想要添加靜態框架的人的解決方案;

所以更新的代碼:

@Override 
public byte[] getPreviewFrameData(int width, int height) { 
    if (mPreviewFrameData == null) { 
     loadPreviewFrame(width, height); 
    } 

    return mPreviewFrameData; 
} 

private void loadPreviewFrame(int width, int height) { 
    try { 
     Bitmap previewImage = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.frame); 
     Bitmap resizedPreviewImage = Bitmap.createScaledBitmap(previewImage, width, height, false); 

     BitmapConverter bitmapConverter = new BitmapConverter(); 
     mPreviewFrameData = bitmapConverter.convertToNV21(resizedPreviewImage); 
    } catch (Exception e) { 
     Log.e("DisabledCameraFrameProvider", "Failed to loadPreviewFrame"); 
    } 
} 

class BitmapConverter { 
    byte [] convertToNV21(Bitmap bitmap) { 
     int inputWidth = bitmap.getWidth(); 
     int inputHeight = bitmap.getHeight(); 

     int [] argb = new int[inputWidth * inputHeight]; 

     bitmap.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight); 

     byte [] yuv = new byte[inputWidth*inputHeight*3/2]; 
     encodeYUV420SP(yuv, argb, inputWidth, inputHeight); 

     bitmap.recycle(); 

     return yuv; 
    } 

    void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) { 
     final int frameSize = width * height; 

     int yIndex = 0; 
     int uvIndex = frameSize; 

     int R, G, B, Y, U, V; 
     int index = 0; 
     for (int j = 0; j < height; j++) { 
      for (int i = 0; i < width; i++) { 
       R = (argb[index] & 0xff0000) >> 16; 
       G = (argb[index] & 0xff00) >> 8; 
       B = (argb[index] & 0xff); 

       Y = (( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16; 
       U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128; 
       V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128; 

       yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y)); 
       if (j % 2 == 0 && index % 2 == 0) { 
        yuv420sp[uvIndex++] = (byte)((V<0) ? 0 : ((V > 255) ? 255 : V)); 
        yuv420sp[uvIndex++] = (byte)((U<0) ? 0 : ((U > 255) ? 255 : U)); 
       } 

       index ++; 
      } 
     } 
    } 
} 

後來終於在回調;

public void onPreviewFrame(byte[] data, Camera camera) { 

    byte[] bytes = data; 

    if (!mProvider.isVideoEnabled()) { 
     Camera.Size previewSize = camera.getParameters().getPreviewSize(); 
     bytes = mProvider.getPreviewFrameData(previewSize.width, previewSize.height); 
    } 

    ProvideCameraFrame(bytes, bytes.length, context); 
} 

關鍵是將圖像縮放到相機預覽尺寸並將圖像轉換爲YUV顏色空間。