Я продолжаю надеяться, что какой-то код появится в Интернете, но никуда не годится;) Я запускаю этот пример github. WebRTC входящий объект I420Frame, похоже, имеет 3 массива yuvPlanes
Обычное приложение для камеры Android получает PreviewCallback.onPreviewFrame byte [] как единый массив байтов. Моя задача - поток изображения как I420 с регулярным интервалом времени. Может ли кто-нибудь помочь мне в том, как сгенерировать I420Frames yuvPlanes из одного байтового [] массива, такого как JPEG/PNG файл?
Это очень важно. Все ответы оценены.
PreviewCallback.onPreviewFrame()
никогда не вернет поток JPEG или PNG. Вы должны проверить список своей камеры getSupportedPreviewFormats()
(обратите внимание, что это может различаться для передних и задних камер). Вы гарантированно получите NV21 в этом списке. Если вам повезет, вы можете выбрать YV12 с уровня API 12 (обратите внимание, что некоторые устройства, например Amazon Fire HD (2012), об этом говорят и фактически не могут доставлять поток YV12).
Легко построить I420Frame из массива байтов YV12:
private VideoRenderer.I420Frame mFrame;
void onPreviewFrame(byte[] yv12_data, Camera camera) {
if (mFrame == null) {
Camera.Parameters params = camera.getParameters(); // this is an expensive call, don't repeat it on every frame!
assert(params.getPreviewFormat() == ImageFormat.YV12);
int width = params.getPreviewSize().width;
int stride_y = 16 + ((width-1)/16)*16;
int stride_uv = 16 + ((stride_y/2-1)/16)*16;
int height = params.getPreviewSize().height;
mFrame = new VideoRenderer.I420Frame(width, height, 0, new int[]{stride_y, stride_uv, stride_uv}, new ByteBuffer[3], 0);
}
mFrame.yuvPlanes[0] = ByteBuffer.wrap(yv12_data, 0, mFrame.yuvStrides[0]*mFrame.height) // Y
mFrame.yuvPlanes[1] = ByteBuffer.wrap(yv12_data, mFrame.yuvStrides[0]*mFrame.height+mFrame.yuvStrides[2]*mFrame.height/2, mFrame.yuvStrides[1]*mFrame.height/2) // U
mFrame.yuvPlanes[2] = ByteBuffer.wrap(yv12_data, mFrame.yuvStrides[0]*mFrame.height, mFrame.yuvStrides[2]*mFrame.height/4) // V
... do something with the frame
}
Для NV21 вы должны выделить U и V плоскостей:
private VideoRenderer.I420Frame mFrame;
void onPreviewFrame(byte[] nv21_data, Camera camera) {
if (mFrame == null) {
Camera.Parameters params = camera.getParameters(); // this is an expensive call, don't repeat it on every frame!
assert(params.getPreviewFormat() == ImageFormat.NV21);
int width = params.getPreviewSize().width;
int height = params.getPreviewSize().height;
mFrame = new VideoRenderer.I420Frame(width, height, 0, new int[]{width, width/2, width/2}, new ByteBuffer[3], 0);
mFrame.yuvPlanes[1] = ByteBuffer.wrap(new byte[width*height/4]);
mFrame.yuvPlanes[2] = ByteBuffer.wrap(new byte[width*height/4]);
}
mFrame.yuvPlanes[0] = ByteBuffer.wrap(nv21_data, 0, mFrame.width*mFrame.height) // Y
for (int top=0, from=mFrame.width*mFrame.height; from < mFrame.width*mFrame.height*3/2; to++, from+=2) {
mframe.yuvPlanes[1][to] = nv21_data[from+1]; // U
mframe.yuvPlanes[2][to] = nv21_data[from]; // V
}
... do something with the frame
}
I420Frame onPreviewFrame(byte[] yv12_data)
{
if (mFrame == null)
{
//Camera.Parameters params = camera.getParameters(); // this is an expensive call, don't repeat it on every frame!
//assert(params.getPreviewFormat() == ImageFormat.YV12);
int width = 640;
int stride_y = 16 + ((width - 1) / 16) * 16;
int stride_uv = 16 + ((stride_y / 2 - 1) / 16) * 16;
int height = 480;
mFrame = new VideoRenderer.I420Frame(width, height, new int[] { stride_y, stride_uv, stride_uv }, new ByteBuffer[3]);
}
mFrame.YuvPlanes[0] = ByteBuffer.Wrap(yv12_data, 0, mFrame.YuvStrides[0] * mFrame.Height); // Y
mFrame.YuvPlanes[1] = ByteBuffer.Wrap(yv12_data, (mFrame.YuvStrides[0] * mFrame.Height) , mFrame.YuvStrides[1] * mFrame.Height );// U
mFrame.YuvPlanes[2] = ByteBuffer.Wrap(yv12_data, (mFrame.YuvStrides[0] * mFrame.Height )+ (mFrame.YuvStrides[1] * mFrame.Height), mFrame.YuvStrides[2] * mFrame.Height ); // V
return mFrame;
// ... do something with the frame
}