ffmpeg:RGB到YUV转换失去颜色和比例

时间:2014-02-21 15:38:21

标签: ffmpeg video-encoding libavcodec libav libavformat

我试图在ffmpeg / libav中将RGB帧转换为YUV420P格式。以下是转换代码以及转换前后的图像。转换后的图像会丢失所有颜色信息,并且尺度也会发生显着变化。有谁知道如何处理这个?我是ffmpeg / libav的新手!

// Did we get a video frame?
   if(frameFinished)
   {
       i++;
       sws_scale(img_convert_ctx, (const uint8_t * const *)pFrame->data,
                 pFrame->linesize, 0, pCodecCtx->height,
                 pFrameRGB->data, pFrameRGB->linesize);                   

       //==============================================================
       AVFrame *pFrameYUV = avcodec_alloc_frame();
       // Determine required buffer size and allocate buffer
       int numBytes2 = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,                                 
                                          pCodecCtx->height);
       uint8_t *buffer = (uint8_t *)av_malloc(numBytes2*sizeof(uint8_t));

       avpicture_fill((AVPicture *)pFrameYUV, buffer, PIX_FMT_RGB24,
                       pCodecCtx->width, pCodecCtx->height);


       rgb_to_yuv_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,  
                                       PIX_FMT_RGB24,
                                       pCodecCtx->width,pCodecCtx->height, 
                                       PIX_FMT_RGB24,
                                       SWS_BICUBIC, NULL,NULL,NULL);

       sws_scale(rgb_to_yuv_ctx, pFrameRGB->data, pFrameRGB->linesize, 0, 
                 pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

       sws_freeContext(rgb_to_yuv_ctx);

       SaveFrame(pFrameYUV, pCodecCtx->width, pCodecCtx->height, i);

       av_free(buffer);
       av_free(pFrameYUV);
   }

original RGB24 frame

frame after RGB24->YUV420P conversion

1 个答案:

答案 0 :(得分:10)

对于初学者我会假设你拥有的地方:

rgb_to_yuv_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,  
                                   PIX_FMT_RGB24,
                                   pCodecCtx->width,pCodecCtx->height, 
                                   PIX_FMT_RGB24,
                                   SWS_BICUBIC, NULL,NULL,NULL);

你真的打算:

rgb_to_yuv_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,  
                                   PIX_FMT_RGB24,
                                   pCodecCtx->width,pCodecCtx->height, 
                                   PIX_FMT_YUV420P,
                                   SWS_BICUBIC, NULL,NULL,NULL);

我也不确定你为什么两次打电话给swscale!

YUV是一种平面格式。这意味着所有三个通道都是独立存储的。 RGB存储如下:     RGBRGBRGB

YUV420P是这样的商店:   YYYYYYYYYYYYYYYY..UUUUUUUUUU..VVVVVVVV

所以swscale要求你给它三个指针。

接下来,您希望您的行间距为16或32的倍数,因此可以使用处理器的矢量单位。最后,Y平面的尺寸需要被2整除(因为U和V平面是Y平面的四分之一尺寸)。

所以,让我们改写一下:

#define RNDTO2(X) ( ( (X) & 0xFFFFFFFE )
#define RNDTO32(X) ( ( (X) % 32 ) ? ( ( (X) + 32 ) & 0xFFFFFFE0 ) : (X) )




if(frameFinished)
{
    static SwsContext *swsCtx = NULL;
    int width    = RNDTO2 ( pCodecCtx->width );
    int height   = RNDTO2 ( pCodecCtx->height );
    int ystride  = RNDTO32 ( width );
    int uvstride = RNDTO32 ( width / 2 );
    int ysize    = ystride * height;
    int vusize   = uvstride * ( height / 2 );
    int size     = ysize + ( 2 * vusize )

    void * pFrameYUV = malloc( size );
    void *plane[] = { pFrameYUV, pFrameYUV + ysize, pFrameYUV + ysize + vusize, 0 };
    int *stride[] = { ystride, vustride, vustride, 0 };

    swsCtx = sws_getCachedContext ( swsCtx, pCodecCtx->width, pCodecCtx->height,
    pCodecCtx->pixfmt, width, height, AV_PIX_FMT_YUV420P, 
    SWS_LANCZOS | SWS_ACCURATE_RND , NULL, NULL, NULL );
    sws_scale ( swsCtx, pFrameRGB->data, pFrameRGB->linesize, 0, 
    pFrameRGB->height, plane, stride );
}    

我还将您的算法切换为使用SWS_LANCZOS | SWS_ACCURATE_RND。这将为您提供更好看的图像。如果它要慢,请改回来。我还使用了源帧中的像素格式,而不是一直假设RGB。