FFMPEG最简解码H264(NVIDIA硬解)

简介: 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;
}
目录
相关文章
|
4月前
|
数据采集 大数据 Python
FFmpeg 在爬虫中的应用案例:流数据解码详解
在大数据背景下,网络爬虫与FFmpeg结合,高效采集小红书短视频。需准备FFmpeg、Python及库如Requests和BeautifulSoup。通过设置User-Agent、Cookie及代理IP增强隐蔽性,解析HTML提取视频链接,利用FFmpeg下载并解码视频流。示例代码展示完整流程,强调代理IP对避免封禁的关键作用,助你掌握视频数据采集技巧。
FFmpeg 在爬虫中的应用案例:流数据解码详解
|
4月前
|
语音技术 C语言 Windows
语音识别------ffmpeg的使用01,ffmpeg的安装,会做PPT很好,ffmpeg不具备直接使用,只可以操作解码数据,ffmpeg用C语言写的,得学C语言,ffmpeg的安装
语音识别------ffmpeg的使用01,ffmpeg的安装,会做PPT很好,ffmpeg不具备直接使用,只可以操作解码数据,ffmpeg用C语言写的,得学C语言,ffmpeg的安装
|
5月前
|
Linux 编解码 Python
FFmpeg开发笔记(二十四)Linux环境给FFmpeg集成AV1的编解码器
AV1是一种高效免费的视频编码标准,由AOM联盟制定,相比H.265压缩率提升约27%。各大流媒体平台倾向使用AV1。本文介绍了如何在Linux环境下为FFmpeg集成AV1编解码库libaom、libdav1d和libsvtav1。涉及下载源码、配置、编译和安装步骤,包括设置环境变量以启用这三个库。
244 3
FFmpeg开发笔记(二十四)Linux环境给FFmpeg集成AV1的编解码器
|
5月前
|
编解码 Linux 计算机视觉
python 调用ffmpeg使用usb摄像头录制视频,输出h264格式,自动获取摄像头的最佳帧率和最大画面尺寸
使用 Python 调用 FFmpeg 进行 USB 摄像头视频录制,需先确保安装 FFmpeg 和 Python 的 `subprocess` 模块。代码示例展示了如何自动获取摄像头的最佳帧率和最大分辨率,然后录制视频。首先通过 FFmpeg 列出摄像头格式获取信息,解析出帧率和分辨率,选择最优值。之后调用 FFmpeg 命令录制视频,设置帧率、分辨率等参数。注意 `/dev/video0` 是 Linux 的摄像头设备路径,Windows 系统需相应调整。代码中未直接实现自动获取最佳参数,通常需要借助其他库如 OpenCV。
|
6月前
|
编解码 5G Linux
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
AVS3是中国首个8K及5G视频编码标准,相比AVS2和HEVC性能提升约30%。解码器libuavs3d支持8K/60P视频实时解码,兼容多种平台。《FFmpeg开发实战》书中介绍了在Windows环境下如何集成libuavs3d到FFmpeg。集成步骤包括下载源码、使用Visual Studio 2022编译、调整配置、安装库文件和头文件,以及重新配置和编译FFmpeg以启用libuavs3d。
95 0
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
|
6月前
|
编解码 Linux 5G
FFmpeg开发笔记(二十)Linux环境给FFmpeg集成AVS3解码器
AVS3,中国制定的第三代音视频标准,是首个针对8K和5G的视频编码标准,相比AVS2和HEVC性能提升约30%。uavs3d是AVS3的解码器,支持8K/60P实时解码,且在各平台有优秀表现。要为FFmpeg集成AVS3解码器libuavs3d,需从GitHub下载最新源码,解压后配置、编译和安装。之后,重新配置FFmpeg,启用libuavs3d并编译安装,通过`ffmpeg -version`确认成功集成。
99 0
FFmpeg开发笔记(二十)Linux环境给FFmpeg集成AVS3解码器
|
6月前
|
存储 缓存 调度
FFmpeg开发笔记(十九)FFmpeg开启两个线程分别解码音视频
《FFmpeg开发实战》第10章示例playsync.c在处理音频流和视频流交错的文件时能实现同步播放,但对于分开存储的格式,会出现先播放全部声音再快速播放视频的问题。为解决此问题,需改造程序,增加音频处理线程和队列,以及相关锁,先将音视频帧读入缓存,再按时间戳播放。改造包括声明新变量、初始化线程和锁、修改数据包处理方式等。代码修改后在playsync2.c中,编译运行成功,控制台显示日志,SDL窗口播放视频并同步音频,证明改造有效。
103 0
FFmpeg开发笔记(十九)FFmpeg开启两个线程分别解码音视频
|
6月前
|
存储 编解码 编译器
FFmpeg 7.0 正式登场:全新 VVC 解码器
【4月更文挑战第9天】最新版本的流行视频处理软件FFmpeg 7.0,代号为“Dijkstra”,已正式发布。
231 0
|
19天前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
82 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
26天前
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
46 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
下一篇
无影云桌面