Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何用静态YUV帧覆盖onPreviewFrame数据?_Android_Image Processing_Android Camera_Webrtc_Yuv - Fatal编程技术网

Android 如何用静态YUV帧覆盖onPreviewFrame数据?

Android 如何用静态YUV帧覆盖onPreviewFrame数据?,android,image-processing,android-camera,webrtc,yuv,Android,Image Processing,Android Camera,Webrtc,Yuv,我的应用程序覆盖onPreviewFrame回调,将当前相机帧传递给webrtc本机函数。这很好用,但是如果我的应用程序中选择了这个选项,我希望能够切换到发送静态帧而不是视频 到目前为止,我已经创建了一个YUV NV21图像,我将其存储在assets目录中。所有将该帧传递给本机函数的尝试都会产生紫/绿色条纹,而不是实际图像 这就是我目前所拥有的 @Override public void onPreviewFrame(byte[] data, Camera camera) { previ

我的应用程序覆盖onPreviewFrame回调,将当前相机帧传递给webrtc本机函数。这很好用,但是如果我的应用程序中选择了这个选项,我希望能够切换到发送静态帧而不是视频

到目前为止,我已经创建了一个YUV NV21图像,我将其存储在assets目录中。所有将该帧传递给本机函数的尝试都会产生紫/绿色条纹,而不是实际图像

这就是我目前所拥有的

@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;
}
我也尝试过将图像转换成位图。因此,问题是如何从资源中打开YUV框架,并将其转换为合适的格式以传递给本机方法

结果如下输出


在与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 ++;
            }
        }
    }
}

关键是将图像缩放到相机预览大小,并将图像转换为YUV颜色空间。

如果您看到图像,那么您显然在更换相机输出方面取得了一些成功。您确定您的图像格式正确吗?是的,我还尝试了在线提供的YUV示例帧..:(无论您提供的图像如何,紫色/绿色条纹输出看起来是否相同?(或在后续运行中使用相同图像时不同?)我只是想弄清楚你看到的是图像的扭曲版本,还是其他地方的乱码数据。我得到的图像似乎是绿色/紫色的,在某些图像上你可以分辨出实际图像的一部分,我附上了一个例子。我对约30%的数据丢失感到困惑,是不是你的样本帧的格式是YUV 4:2:2?
@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);
}