FFMPEG最簡解碼H264(NVIDIA硬解)

  關鍵:NVIDIA DECODER的結果是NV12,需要轉換爲YUV420P。

#include <stdio.h>
#include <stdlib.h>


extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>

#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
}

#define LOG_HERE() printf("%s-%d\n", __func__, __LINE__)

#define DUMP_FRAME(frame) { \
   printf( "%s-%d AVFrame:format=%2d, key_frame=%d, pict_type=%d, width=%4d, height=%4d, data=(%d, %d, %d), linesize=(%4d, %4d, %4d)\n", \
    __func__, __LINE__, \
    frame->format, frame->key_frame, frame->pict_type, \
    frame->width,  frame->height, \
   (frame->data[0] != NULL), \
   (frame->data[1] != NULL), \
   (frame->data[2] != NULL),\
    frame->linesize[0], \
    frame->linesize[1], \
    frame->linesize[2] \
    );}


#define NVIDIA_H264_DECODER "h264_cuvid"

#ifdef NVIDIA_H264_DECODER

// NVIDIA DECODER result is NV12, filter to YUV420P
static AVFilterContext* decoder_filter_out = NULL;
static AVFilterContext* decoder_filter_in  = NULL;
static AVFilterGraph*   decoder_graph      = NULL;
static int decoder_width  = 0;
static int decoder_height = 0;
static int decoder_format = AV_PIX_FMT_NONE;


static int configure_decoder_filter_graph(AVFilterGraph *graph, 
     AVFilterContext *source_ctx, AVFilterContext *sink_ctx)
{
    int ret;
    AVFilterInOut *outputs = NULL, *inputs = NULL;
    if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) >= 0)
    {
       ret = avfilter_graph_config(graph, NULL);
    }

    avfilter_inout_free(&outputs);
    avfilter_inout_free(&inputs);
    return ret;
}

static int configure_decoder_video_filters(AVFilterGraph *graph, const int width, const int height, const int format)
{
    int pix_fmts[2] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
    char buffersrc_args[256] = {0};
    AVFilterContext *filt_src = NULL, *filt_out = NULL;
    int ret;

    snprintf(buffersrc_args, sizeof(buffersrc_args),
             "video_size=%dx%d:pix_fmt=%d:time_base=1/1200000",
             width, height, format);
    if ((ret = avfilter_graph_create_filter(&filt_src,
        avfilter_get_by_name("buffer"), "ffplay_buffer", buffersrc_args,
        NULL, graph)) < 0)
    {
        goto fail;
    }

    ret = avfilter_graph_create_filter(&filt_out,
          avfilter_get_by_name("buffersink"),
          "ffplay_buffersink", NULL, NULL, graph);
    if (ret < 0)
    {
        goto fail;
    }

    if ((ret = av_opt_set_int_list(filt_out, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0)
    {
        goto fail;
    }
    if ((ret = configure_decoder_filter_graph(graph, filt_src, filt_out)) < 0)
    {
        goto fail;
    }

    decoder_filter_in  = filt_src;
    decoder_filter_out = filt_out;

fail:
    return ret;
}

#endif // NVIDIA_H264_DECODER


int main(int argc, char* argv[])
{
    AVFormatContext *pFormatCtx = NULL;
    AVCodecContext  *pCodecCtx  = NULL;
    AVCodec  *pCodec  = NULL;
    AVFrame  *pFrame  = NULL;
    AVPacket *pPacket = NULL;
    int ret = -1;
    int frame_cnt = 0;
    const char* filepath = "sample_720p-2.h264";

    //分配空間
    pFormatCtx = avformat_alloc_context();

    //打開文件
    if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0)
    {
        printf("Couldn't open input file.\n");
        return -1;
    }
    av_dump_format(pFormatCtx, 0, filepath, 0);

    //分配空間
    pCodecCtx = avcodec_alloc_context3(NULL);

#ifdef NVIDIA_H264_DECODER

    pCodec = avcodec_find_decoder_by_name(NVIDIA_H264_DECODER);
    printf("Codec %s found %s\n", NVIDIA_H264_DECODER, (pCodec ? "OK." : "failed!"));

#endif

    if (pCodec == NULL)
    {
#if 1
        pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
#else
        if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[0]->codecpar) < 0)
        {
            return -1;
        }
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
#endif
        if (pCodec == NULL)
        {
            printf("Couldn't find codec.\n");
            return -1;
        }
         printf("Codec found with name %d(%s)\n", pCodec->id, pCodec->long_name);
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
         printf("Couldn't open codec.\n");
         return -1;
    }

    pFrame = av_frame_alloc();

    pPacket = (AVPacket *)av_malloc(sizeof(AVPacket));
    while (av_read_frame(pFormatCtx, pPacket) >= 0)
    {
        ret = avcodec_send_packet(pCodecCtx, pPacket);
        if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
        {
            av_packet_unref(pPacket);
            return -1;
        }

        //printf("pPacket->size=%d\n", pPacket->size);
        ret = avcodec_receive_frame(pCodecCtx, pFrame);
        if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
        {
            av_packet_unref(pPacket);
            return -1;
        }

        if (pFrame->data == NULL || pFrame->data[0] == NULL)
        {
            continue;
        }

#ifdef NVIDIA_H264_DECODER
        if (   decoder_width  !=      pFrame->width
            || decoder_height !=      pFrame->height
            || decoder_format != (int)pFrame->format)
        {
            decoder_width  =      pFrame->width;
            decoder_height =      pFrame->height;
            decoder_format = (int)pFrame->format;

            decoder_graph = avfilter_graph_alloc();
            configure_decoder_video_filters(decoder_graph, decoder_width, decoder_height, decoder_format);
        }

        if (pFrame->format != AV_PIX_FMT_YUV420P)
        {
            //printf("pFrame->format=%d, AV_PIX_FMT_NV12=%d, AV_PIX_FMT_YUV420P=%d\n", pFrame->format, AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P);
            //DUMP_FRAME(pFrame);
            ret = av_buffersrc_add_frame(decoder_filter_in, pFrame);
            //DUMP_FRAME(pFrame);
            ret = av_buffersink_get_frame_flags(decoder_filter_out, pFrame, 0);
        }

#endif

        //DUMP_FRAME(pFrame);
        printf("Frame count=%4d, CodecCtx=(%d, %d)\n", frame_cnt, pCodecCtx->width, pCodecCtx->height);
        frame_cnt++;
        if (frame_cnt > 10)
        {
            break;
        }
    }

    av_packet_unref(pPacket);
    av_frame_unref(pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章