¿Cómo convertir un FFMPEG AVFrame en YUVJ420P a AVFoundation cVPixelBufferRef?

Tengo un FFMPEG AVFrame en YUVJ420P y quiero convertirlo a CVPixelBufferRef con CVPixelBufferCreateWithBytes . La razón por la que quiero hacer esto es usar AVFoundation para mostrar / codificar los frameworks.

Seleccioné kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange y traté de convertirlo ya que el AVFrame tiene los datos en tres planos Y480 Cb240 Cr240 . Y de acuerdo con lo que he investigado, esto coincide con el kCVPixelFormatType seleccionado. Al ser biplanar, necesito convertirlo en un búfer que contenga Y480 y CbCr480 Interleaved.

Intenté crear un buffer con 2 planos:

  • frame->data[0] en el primer plano,
  • frame->data[1] y frame->data[2] intercalados en el segundo plano.

Sin embargo, -6661 (invalid a) error de devolución -6661 (invalid a) de CVPixelBufferCreateWithBytes :

 "Invalid function parameter. For example, out of range or the wrong type." 

No tengo experiencia en el image processing en absoluto, por lo que se agradecen todos los indicadores de la documentation que pueda ayudarme a comenzar con el enfoque correcto de este problema. Las habilidades de mi C tampoco están en primera línea, así que tal vez estoy cometiendo un error básico aquí.

  uint8_t **buffer = malloc(2*sizeof(int *)); buffer[0] = frame->data[0]; buffer[1] = malloc(frame->linesize[0]*sizeof(int)); for(int i = 0; i<frame->linesize[0]; i++){ if(i%2){ buffer[1][i]=frame->data[1][i/2]; }else{ buffer[1][i]=frame->data[2][i/2]; } } int ret = CVPixelBufferCreateWithBytes(NULL, frame->width, frame->height, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, buffer, frame->linesize[0], NULL, 0, NULL, cvPixelBufferSample) 

El marco es el AVFrame con el rawData de FFMPEG Decoding.

Las habilidades de mi C tampoco están en primera línea, así que tal vez estoy cometiendo un error básico aquí.

Estás haciendo varios:

  • Debería estar usando CVPixelBufferCreateWithPlanarBytes() . No sé si CVPixelBufferCreateWithBytes() se puede usar para crear un marco de video plano; de ser así, requerirá un puntero a un "bloque descriptor plano" (no puedo encontrar la estructura en los documentos).
  • frame->linesize[0] es los bytes por fila , no el tamaño de la image completa. Los documentos no están claros, pero el uso es bastante inequívoco.
  • frame->linesize[0] refiere al plano Y; te preocupan los aviones UV.
  • ¿De dónde es sizeof(int) ?
  • Estás pasando en cvPixelBufferSample ; puede significar &cvPixelBufferSample .
  • No está pasando una callback de lanzamiento. La documentation no dice que puede pasar NULL .

Prueba algo como esto:

 size_t srcPlaneSize = frame->linesize[1]*frame->height; size_t dstPlaneSize = srcPlaneSize *2; uint8_t *dstPlane = malloc(dstPlaneSize); void *planeBaseAddress[2] = { frame->data[0], dstPlane }; // This loop is very naive and assumes that the line sizes are the same. // It also copies padding bytes. assert(frame->linesize[1] == frame->linesize[2]); for(size_t i = 0; i<srcPlaneSize; i++){ // These might be the wrong way round. dstPlane[2*i ]=frame->data[2][i]; dstPlane[2*i+1]=frame->data[1][i]; } // This assumes the width and height are even (it's 420 after all). assert(!frame->width%2 && !frame->height%2); size_t planeWidth[2] = {frame->width, frame->width/2}; size_t planeHeight[2] = {frame->height, frame->height/2}; // I'm not sure where you'd get this. size_t planeBytesPerRow[2] = {frame->linesize[0], frame->linesize[1]*2}; int ret = CVPixelBufferCreateWithPlanarBytes( NULL, frame->width, frame->height, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, 0, 2, planeBaseAddress, planeWidth, planeHeight, planeBytesPerRow, YOUR_RELEASE_CALLBACK, YOUR_RELEASE_CALLBACK_CONTEXT, NULL, &cvPixelBufferSample); 

La gestión de la memory se deja como un ejercicio para el lector, pero para el código de testing puede salirse con la suya en NULL en lugar de una callback de liberación.