Converting to YUV format

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Converting to YUV format

Gonzalo Garramuño

In my player, I've been using RGB for displaying images, using:

   output.data[0] = image->data().get();
   output.data[1] = output.data[2] = output.data[3] = NULL;
   output.linesize[0] = width * 4;
   output.linesize[1] = output.linesize[2] = output.linesize[3] = 0;

   static const int sws_flags = SWS_BICUBIC;
   _convert_ctx = sws_getCachedContext(_convert_ctx,
                                      stream->codec->width,
                                      stream->codec->height,
                                      stream->codec->pix_fmt,
                                      width,
                                      height,
       PIX_FMT_RGBA32, sws_flags,
                                      NULL, NULL, NULL);
   sws_scale(_convert_ctx, _av_frame->data, _av_frame->linesize,
            0, stream->codec->height, output.data, output.linesize);


I am now trying to add support for YUV (particularly YUV420) for pixel
shaders.

I'm trying to obtain a YUV420 image as a set of planar images.

Either as 3 gray images or as one image with a red channel containing
the Y, and the green and blue channels containing the U and V at 1/2th
the size.
The original movie is also encoded as YUV420.

I'm having some trouble understanding how to do this correctly and
efficiently.

Given, for example:

   unsigned int sum = width * height;
   boost::uint8_t* ptr = image->data().get();
   output.data[0] = ptr;
   output.data[1] = ptr + width;
   output.data[2] = ptr + width*2;
   output.data[3] = ptr + width*3;
   output.linesize[0] = width*4;
   output.linesize[1] = width*4;
   output.linesize[2] = width*4;
   output.linesize[3] = width*4;


   static const int sws_flags = SWS_BICUBIC;
   _convert_ctx = sws_getCachedContext(_convert_ctx,
                                      stream->codec->width,
                                      stream->codec->height,
                                      stream->codec->pix_fmt,
                                      width,
                                      height,
       PIX_FMT_YUV420P, sws_flags,
                                      NULL, NULL, NULL);
   sws_scale(_convert_ctx, _av_frame->data, _av_frame->linesize,
            0, stream->codec->height, output.data, output.linesize);


I obtain:
     a gray RGB Y image that is 1/4th the width (but full height).
     a gray RGB U image that is 1/8th the width and 1/2th the height.
     a gray RGB V image that is 1/8th the width and 1/2th the height.

The proportions are correct (ie. U and V are half the Y size), but I
expected the Y image to cover the width of the image completely, not be
1/4th the width.

Also I was expecting to see only one channel filled, yet I see the three
image channels (RGB) filled with the same value.

Can anyone clarify a little bit?  Is what I am seeing normal and expected?

--
Gonzalo Garramuño
[hidden email]

AMD4400 - ASUS48N-E
GeForce7300GT
Xubuntu Gutsy
_______________________________________________
ffmpeg-user mailing list
[hidden email]
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user
Reply | Threaded
Open this post in threaded view
|

Re: Converting to YUV format

Dennis Meuwissen
Gonzalo Garramuño wrote:

> In my player, I've been using RGB for displaying images, using:
>
>    output.data[0] = image->data().get();
>    output.data[1] = output.data[2] = output.data[3] = NULL;
>    output.linesize[0] = width * 4;
>    output.linesize[1] = output.linesize[2] = output.linesize[3] = 0;
>
>    static const int sws_flags = SWS_BICUBIC;
>    _convert_ctx = sws_getCachedContext(_convert_ctx,
>      stream->codec->width,
>      stream->codec->height,
>      stream->codec->pix_fmt,
>      width,
>      height,
>        PIX_FMT_RGBA32, sws_flags,
>      NULL, NULL, NULL);
>    sws_scale(_convert_ctx, _av_frame->data, _av_frame->linesize,
>    0, stream->codec->height, output.data, output.linesize);
>
>
> I am now trying to add support for YUV (particularly YUV420) for pixel
> shaders.
>
> I'm trying to obtain a YUV420 image as a set of planar images.
>
> Either as 3 gray images or as one image with a red channel containing
> the Y, and the green and blue channels containing the U and V at 1/2th
> the size.
> The original movie is also encoded as YUV420.
>
> I'm having some trouble understanding how to do this correctly and
> efficiently.
>
> Given, for example:
>
>    unsigned int sum = width * height;
>    boost::uint8_t* ptr = image->data().get();
>    output.data[0] = ptr;
>    output.data[1] = ptr + width;
>    output.data[2] = ptr + width*2;
>    output.data[3] = ptr + width*3;
>    output.linesize[0] = width*4;
>    output.linesize[1] = width*4;
>    output.linesize[2] = width*4;
>    output.linesize[3] = width*4;
>
>
>    static const int sws_flags = SWS_BICUBIC;
>    _convert_ctx = sws_getCachedContext(_convert_ctx,
>      stream->codec->width,
>      stream->codec->height,
>      stream->codec->pix_fmt,
>      width,
>      height,
>        PIX_FMT_YUV420P, sws_flags,
>      NULL, NULL, NULL);
>    sws_scale(_convert_ctx, _av_frame->data, _av_frame->linesize,
>    0, stream->codec->height, output.data, output.linesize);
>
>
> I obtain:
>      a gray RGB Y image that is 1/4th the width (but full height).
>      a gray RGB U image that is 1/8th the width and 1/2th the height.
>      a gray RGB V image that is 1/8th the width and 1/2th the height.
>
> The proportions are correct (ie. U and V are half the Y size), but I
> expected the Y image to cover the width of the image completely, not be
> 1/4th the width.
>
> Also I was expecting to see only one channel filled, yet I see the three
> image channels (RGB) filled with the same value.
>
> Can anyone clarify a little bit?  Is what I am seeing normal and expected?
>
>  
FFmpeg itself outputs a green image when using swScaler and the bicubic,
bilinear and bilinear_fast scaling filters,
something I've reported a couple of months ago in
http://roundup.mplayerhq.hu/roundup/ffmpeg/issue229 . Perhaps this
is related, and changing the sws_flags to lanczos for example might be a
workaround.
_______________________________________________
ffmpeg-user mailing list
[hidden email]
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user
Reply | Threaded
Open this post in threaded view
|

Re: Converting to YUV format

Rich Felker
In reply to this post by Gonzalo Garramuño
On Thu, Jan 10, 2008 at 01:09:03PM -0300, Gonzalo Garramuño wrote:
>    unsigned int sum = width * height;

This is an odd use of the word "sum"...

>    boost::uint8_t* ptr = image->data().get();
>    output.data[0] = ptr;
>    output.data[1] = ptr + width;
>    output.data[2] = ptr + width*2;
>    output.data[3] = ptr + width*3;
>    output.linesize[0] = width*4;
>    output.linesize[1] = width*4;
>    output.linesize[2] = width*4;
>    output.linesize[3] = width*4;

These are definitely incorrect. data[1] does not mean the start of
line 1 but the start of _plane_ 1.

Rich
_______________________________________________
ffmpeg-user mailing list
[hidden email]
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user
Reply | Threaded
Open this post in threaded view
|

Re: Converting to YUV format

Kevin Tang

--- Rich Felker <[hidden email]> wrote:

> On Thu, Jan 10, 2008 at 01:09:03PM -0300, Gonzalo
> Garramuño wrote:
> >    unsigned int sum = width * height;
>
> This is an odd use of the word "sum"...
>
> >    boost::uint8_t* ptr = image->data().get();
> >    output.data[0] = ptr;
> >    output.data[1] = ptr + width;
> >    output.data[2] = ptr + width*2;
> >    output.data[3] = ptr + width*3;
> >    output.linesize[0] = width*4;
> >    output.linesize[1] = width*4;
> >    output.linesize[2] = width*4;
> >    output.linesize[3] = width*4;
>
> These are definitely incorrect. data[1] does not
> mean the start of
> line 1 but the start of _plane_ 1.

If YUV planar mode, assuming ptr points to a
contiguous buffer of 1 line of YUV, the configuration
should be
    output.linesize[0] = width;
    output.linesize[1] = output.linesize[2] =
((width+1)>>1);
    output.linesize[3] = 0;
    output.data[0] = ptr;
    output.data[1] = ptr + output.linesize[0];
    output.data[2] = output.data[1] +
output.linesize[1];
    output.data[3] = output.data[2] +
output.linesize[2];
>
> Rich
> _______________________________________________
> ffmpeg-user mailing list
> [hidden email]
>
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user



      ____________________________________________________________________________________
Looking for last minute shopping deals?  
Find them fast with Yahoo! Search.  http://tools.search.yahoo.com/newsearch/category.php?category=shopping
_______________________________________________
ffmpeg-user mailing list
[hidden email]
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user
Reply | Threaded
Open this post in threaded view
|

Re: Converting to YUV format

Rich Felker
On Thu, Jan 10, 2008 at 08:42:18PM -0800, Kevin Tang wrote:

>
> --- Rich Felker <[hidden email]> wrote:
>
> > On Thu, Jan 10, 2008 at 01:09:03PM -0300, Gonzalo
> > Garramuño wrote:
> > >    unsigned int sum = width * height;
> >
> > This is an odd use of the word "sum"...
> >
> > >    boost::uint8_t* ptr = image->data().get();
> > >    output.data[0] = ptr;
> > >    output.data[1] = ptr + width;
> > >    output.data[2] = ptr + width*2;
> > >    output.data[3] = ptr + width*3;
> > >    output.linesize[0] = width*4;
> > >    output.linesize[1] = width*4;
> > >    output.linesize[2] = width*4;
> > >    output.linesize[3] = width*4;
> >
> > These are definitely incorrect. data[1] does not
> > mean the start of
> > line 1 but the start of _plane_ 1.
>
> If YUV planar mode, assuming ptr points to a
> contiguous buffer of 1 line of YUV, the configuration
> should be
>     output.linesize[0] = width;
>     output.linesize[1] = output.linesize[2] =
> ((width+1)>>1);
>     output.linesize[3] = 0;
>     output.data[0] = ptr;
>     output.data[1] = ptr + output.linesize[0];

No, this is absolutely wrong. It should be

      output.data[1] = ptr + output.linesize[0] * height;

This was my original point.

Even if you intended for height to be 1, that's bogus because a height
of 1 makes no sense with 4:2:0 sampling.

Rich
_______________________________________________
ffmpeg-user mailing list
[hidden email]
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user
Reply | Threaded
Open this post in threaded view
|

Re: Converting to YUV format

Kevin Tang

--- Rich Felker <[hidden email]> wrote:

> On Thu, Jan 10, 2008 at 08:42:18PM -0800, Kevin Tang
> wrote:
> >
> > --- Rich Felker <[hidden email]> wrote:
> >
> > > On Thu, Jan 10, 2008 at 01:09:03PM -0300,
> Gonzalo
> > > Garramuño wrote:
> > > >    unsigned int sum = width * height;
> > >
> > > This is an odd use of the word "sum"...
> > >
> > > >    boost::uint8_t* ptr = image->data().get();
> > > >    output.data[0] = ptr;
> > > >    output.data[1] = ptr + width;
> > > >    output.data[2] = ptr + width*2;
> > > >    output.data[3] = ptr + width*3;
> > > >    output.linesize[0] = width*4;
> > > >    output.linesize[1] = width*4;
> > > >    output.linesize[2] = width*4;
> > > >    output.linesize[3] = width*4;
> > >
> > > These are definitely incorrect. data[1] does not
> > > mean the start of
> > > line 1 but the start of _plane_ 1.
> >
> > If YUV planar mode, assuming ptr points to a
> > contiguous buffer of 1 line of YUV, the
> configuration
> > should be
> >     output.linesize[0] = width;
> >     output.linesize[1] = output.linesize[2] =
> > ((width+1)>>1);
> >     output.linesize[3] = 0;
> >     output.data[0] = ptr;
> >     output.data[1] = ptr + output.linesize[0];
>
> No, this is absolutely wrong. It should be
>
>       output.data[1] = ptr + output.linesize[0] *
> height;

In the case ptr points multi line YUV, then for 420 it
should be

      output.data[1] = ptr + output.linesize[0] *
((height+1)>>1);

Same with data[2], height should be taken the ceiling
of its half.

>
> This was my original point.
>
> Even if you intended for height to be 1, that's
> bogus because a height
> of 1 makes no sense with 4:2:0 sampling.
>
> Rich
> _______________________________________________
> ffmpeg-user mailing list
> [hidden email]
>
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user



      ____________________________________________________________________________________
Never miss a thing.  Make Yahoo your home page.
http://www.yahoo.com/r/hs
_______________________________________________
ffmpeg-user mailing list
[hidden email]
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user
Reply | Threaded
Open this post in threaded view
|

Re: Converting to YUV format

Kevin Tang

--- Kevin Tang <[hidden email]> wrote:

>
> --- Rich Felker <[hidden email]> wrote:
>
> > On Thu, Jan 10, 2008 at 08:42:18PM -0800, Kevin
> Tang
> > wrote:
> > >
> > > --- Rich Felker <[hidden email]> wrote:
> > >
> > > > On Thu, Jan 10, 2008 at 01:09:03PM -0300,
> > Gonzalo
> > > > Garramuño wrote:
> > > > >    unsigned int sum = width * height;
> > > >
> > > > This is an odd use of the word "sum"...
> > > >
> > > > >    boost::uint8_t* ptr =
> image->data().get();
> > > > >    output.data[0] = ptr;
> > > > >    output.data[1] = ptr + width;
> > > > >    output.data[2] = ptr + width*2;
> > > > >    output.data[3] = ptr + width*3;
> > > > >    output.linesize[0] = width*4;
> > > > >    output.linesize[1] = width*4;
> > > > >    output.linesize[2] = width*4;
> > > > >    output.linesize[3] = width*4;
> > > >
> > > > These are definitely incorrect. data[1] does
> not
> > > > mean the start of
> > > > line 1 but the start of _plane_ 1.
> > >
> > > If YUV planar mode, assuming ptr points to a
> > > contiguous buffer of 1 line of YUV, the
> > configuration
> > > should be
> > >     output.linesize[0] = width;
> > >     output.linesize[1] = output.linesize[2] =
> > > ((width+1)>>1);
> > >     output.linesize[3] = 0;
> > >     output.data[0] = ptr;
> > >     output.data[1] = ptr + output.linesize[0];
> >
> > No, this is absolutely wrong. It should be
> >
> >       output.data[1] = ptr + output.linesize[0] *
> > height;
>
> In the case ptr points multi line YUV, then for 420
> it
> should be
>
>       output.data[1] = ptr + output.linesize[0] *
> ((height+1)>>1);
>
> Same with data[2], height should be taken the
> ceiling
> of its half.

correcting myself....  :(
      output.data[1] = ptr + output.linesize[0] *
height;
      output.data[2] = output.data[1] +
output.linesize[1] * ((height+1)>>1);

>
> >
> > This was my original point.
> >
> > Even if you intended for height to be 1, that's
> > bogus because a height
> > of 1 makes no sense with 4:2:0 sampling.
> >
> > Rich
> > _______________________________________________
> > ffmpeg-user mailing list
> > [hidden email]
> >
>
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user
>
>
>
>      
>
____________________________________________________________________________________
> Never miss a thing.  Make Yahoo your home page.
> http://www.yahoo.com/r/hs
> _______________________________________________
> ffmpeg-user mailing list
> [hidden email]
>
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user
>



      ____________________________________________________________________________________
Never miss a thing.  Make Yahoo your home page.
http://www.yahoo.com/r/hs
_______________________________________________
ffmpeg-user mailing list
[hidden email]
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-user