Search code examples
ffmpegyuvlibav

Saving raw YUV420P frame FFmpeg/Libav


Previously i successfully opened, decoded and saved frames from .mp4 file. Raw frames were in YUV420P format which i converted to RGB24 using sws_scale() function and saved them into a .ppm file. What i'm trying to do now is to keep raw decoded frames in YUV420P, or convert frames that i get to YUV420P just to make sure they are in YUV420P. But the problem is that i don't know which type of file should i use, i'm guessing .yuv? And another problem is that i also don't know how to save YUV420P data.

I used to save RGB24 in .ppmlike this:

for (y = 0; y < height; y++)
{
fwrite(frame->data[0] + y*frame->linesize[0], 1, width * 3, pf);
}

which worked fine since i was using only 1 plane. But YUV420P uses 3 planes (Y,Cb,Cr). What i tried to do is to save Y component first, and Cb/Cr after that.

for (y = 0; y < height; y++)
{
    fwrite(frame->data[0] + y*frame->linesize[0], 1, width, pf);
}

for (y = 0; y < height / 2; y++)
{
    fwrite(frame->data[1] + y*frame->linesize[1], 1, width, pf);
    fwrite(frame->data[2] + y*frame->linesize[2], 1, width, pf);
}

But as you can guess that is not working at all. Actually it does, but it saves only Y component. Could anyone guide me to right direction please?

EDIT I've changed mine saveFrame() function like you said, but instead of interleaved chroma writing i used planar. But, i'm still getting only Y component picture (black-white).

fprintf(pf, "P5\n%d %d\n255\n", width, height);

for (y = 0; y < height; y++)
{
    fwrite(frame->data[0] + y*frame->linesize[0], 1, width, pf);
}

for (y = 0; y < height / 2; y++)
{
    fwrite(frame->data[1] + y*frame->linesize[1], 1, width / 2, pf);
}

for (y = 0; y < height / 2; y++)
{
    fwrite(frame->data[2] + y*frame->linesize[2], 1, width / 2, pf);
}

Solution

  • Your chroma code is wrong:

    for (y = 0; y < height / 2; y++)
    {
        fwrite(frame->data[1] + y*frame->linesize[1], 1, width / 2, pf);
        fwrite(frame->data[2] + y*frame->linesize[2], 1, width / 2, pf);
    }
    

    I'd also claim that height / 2 or width / 2 is wrong, since you should round up, not down, but that's relatively minor.