ffmpegh.264cuvid

Why there is no AVFrame->data[2] data when decode h264 by ffmpeg use "h264_cuvid"


env: ubuntu 16.04 64 bit; ffmpeg 3.3.2 build whih cuda cuvid libnpp...
use ffmpeg cmd: ffmpeg -vsync 0 -c:v h264_cuvid -i test.264 -f rawvideo test.yuv works fine, the generated yuv file is ok.
BUT When I decode this 264 file by my code use 'h264_cuvid' decoder, something problem happens, this is my code:

#include <stdio.h>

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#ifdef __cplusplus
};
#endif
#endif


//test different codec
#define TEST_H264  1
#define TEST_HEVC  0

int main(int argc, char* argv[])
{
    AVCodec *pCodec;
    AVCodecContext *pCodecCtx= NULL;
    AVCodecParserContext *pCodecParserCtx=NULL;

    FILE *fp_in;
    FILE *fp_out;
    AVFrame *pFrame;

    const int in_buffer_size=4096;
    unsigned char in_buffer[in_buffer_size + FF_INPUT_BUFFER_PADDING_SIZE]= {0};
    unsigned char *cur_ptr;
    int cur_size;
    AVPacket packet;
    int ret, got_picture;


#if TEST_HEVC
    enum AVCodecID codec_id=AV_CODEC_ID_HEVC;
    char filepath_in[]="bigbuckbunny_480x272.hevc";
#elif TEST_H264
    AVCodecID codec_id=AV_CODEC_ID_H264;
    char filepath_in[]="2_60_265to264.264";
#else
    AVCodecID codec_id=AV_CODEC_ID_MPEG2VIDEO;
    char filepath_in[]="bigbuckbunny_480x272.m2v";
#endif

    char filepath_out[]="mainSend.yuv";
    int first_time=1;


    //av_log_set_level(AV_LOG_DEBUG);

    avcodec_register_all();

//    pCodec = avcodec_find_decoder(codec_id);
    pCodec = avcodec_find_decoder_by_name("h264_cuvid");
    if (!pCodec)
    {
        printf("Codec not found\n");
        return -1;
    }
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (!pCodecCtx)
    {
        printf("Could not allocate video codec context\n");
        return -1;
    }

    pCodecParserCtx=av_parser_init(pCodec->id);
    if (!pCodecParserCtx)
    {
        printf("Could not allocate video parser context\n");
        return -1;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("Could not open codec\n");
        return -1;
    }
    //Input File
    fp_in = fopen(filepath_in, "rb");
    if (!fp_in)
    {
        printf("Could not open input stream\n");
        return -1;
    }
    //Output File
    fp_out = fopen(filepath_out, "wb");
    if (!fp_out)
    {
        printf("Could not open output YUV file\n");
        return -1;
    }

    pFrame = av_frame_alloc();
    av_init_packet(&packet);

    while (1)
    {

        cur_size = fread(in_buffer, 1, in_buffer_size, fp_in);
        if (cur_size == 0)
            break;
        cur_ptr=in_buffer;

        while (cur_size>0)
        {

            int len = av_parser_parse2(
                          pCodecParserCtx, pCodecCtx,
                          &packet.data, &packet.size,
                          cur_ptr, cur_size,
                          AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);

            cur_ptr += len;
            cur_size -= len;

            if(packet.size==0)
                continue;

            //Some Info from AVCodecParserContext
            printf("[Packet]Size:%6d\t",packet.size);
            switch(pCodecParserCtx->pict_type)
            {
            case AV_PICTURE_TYPE_I:
                printf("Type:I\tNumber:%4d\n",pCodecParserCtx->output_picture_number);
                break;
            case AV_PICTURE_TYPE_P:
                printf("Type:P\t");
                break;
            case AV_PICTURE_TYPE_B:
                printf("Type:B\t");
                break;
            default:
                printf("Type:Other\t");
                break;
            }
            printf("Number:%4d\n",pCodecParserCtx->output_picture_number);
            AVFrame* myFrame = av_frame_alloc();
            ret = avcodec_decode_video2(pCodecCtx, myFrame, &got_picture, &packet);
            if (ret < 0)
            {
                printf("Decode Error.\n");
                return ret;
            }
            if (got_picture)
            {
                if(first_time)
                {
                    printf("\nCodec Full Name:%s\n",pCodecCtx->codec->long_name);
                    printf("width:%d\nheight:%d\n\n",pCodecCtx->width,pCodecCtx->height);
                    first_time=0;
                }
                //Y, U, V
                for(int i=0; i<myFrame->height; i++)
                {
                    fwrite(myFrame->data[0]+myFrame->linesize[0]*i,1,myFrame->width,fp_out);
                }
                for(int i=0; i<myFrame->height/2; i++)
                {
                    fwrite(myFrame->data[1]+myFrame->linesize[1]*i,1,myFrame->width/2,fp_out);
                }
                for(int i=0; i<myFrame->height/2; i++)
                {
                    fwrite(myFrame->data[2]+myFrame->linesize[2]*i,1,myFrame->width/2,fp_out);
                }
//                printf("pframe's width height %d %d\t key frame %d\n",myFrame->width,myFrame->height,myFrame->key_frame);
                printf("Succeed to decode 1 frame!\n");
                av_frame_free(&myFrame);
            }
        }

    }

    fclose(fp_in);
    fclose(fp_out);


    av_parser_close(pCodecParserCtx);

    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    av_free(pCodecCtx);

    return 0;
}

In this demo code, I call h264_cuvid by vcodec_find_decoder_by_name("h264_cuvid"); BUT the code crash at fwrite(myFrame->data[2]+myFrame->linesize[2]*i,1,myFrame->width/2,fp_out);
So after debug with codeblocks, I found that there is no data in myFrame->data[2] codeblocks watching window

Any suggestion? thanks!


Solution

  • "h264" decoder's frame pix format is AV_PIX_FMT_YUV420P
    BUT "h264_cuvid" decoder's frame pix format is AV_PIX_FMT_NV12
    so, edit code to

                for(int i=0; i<myFrame->height; i++)
                {
                    fwrite(myFrame->data[0]+myFrame->linesize[0]*i,1,myFrame->width,fp_out);
                }
                for(int i=0; i<myFrame->height/2; i++)
                {
                    fwrite(myFrame->data[1]+myFrame->linesize[1]*i,1,myFrame->width,fp_out);
                }
    

    Works fine