当前位置: 首页 > article >正文

ffmpeg硬件解码一般流程

流程

  1. 根据硬件名称,查询是否是支持的类型
const char *device_name = "qsv"; //cuda
enum AVHWDeviceType type = av_hwdevice_find_type_by_name(device_name);
if(type == AV_HWDEVICE_TYPE_NONE)
{
	//如果一个硬件类型是不支持的,打印所有支持的硬件名称
    printf("Device type %s is not supported.\n", device_name);
    fprintf(stderr, "Available device types:");
	while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)
	{
              fprintf(stderr, " %s", av_hwdevice_get_type_name(type));
     }
     fprintf(stderr, "\n");
}
  1. 通用过程
avformat_open_input
avformat_find_stream_info
av_find_best_stream
  1. 获取硬件编码器
 AVCodec *pCodec;
    for (i = 0;; i++) {
          const AVCodecHWConfig *config = avcodec_get_hw_config(pCodec, i);
          if (!config) {
              fprintf(stderr, "Decoder %s does not support device type %s.\n",
                      pCodec->name, av_hwdevice_get_type_name(type));
              return;
          }
          if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
              config->device_type == type) {
              hw_pix_fmt = config->pix_fmt;
              break;
          }
      }
  1. 查找解码器
 if (!(pCodecCtx = avcodec_alloc_context3(pCodec)))
        return;
  1. 复制参数
 video = pFormatCtx->streams[videoStream];
    if (avcodec_parameters_to_context(pCodecCtx, video->codecpar) < 0)
        return;

    pCodecCtx->get_format = get_hw_format;
  1. 初始化硬件解码器与打开解码器
    if (hw_decoder_init(pCodecCtx, type) < 0)
        return;
//打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return;
    }
  1. 读取视频包,发送视频包到解码器,解码
  2. 将显存中的内容复制内存中
ret = av_hwframe_transfer_data(swFrame, pFrame, 0);
                if(ret < 0)
                {
                    qDebug() << "Error transferring the data to system memory";
                    break;
                }

完整的代码

    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameRGB, *swFrame, *tempFrame;
    uint8_t *out_buffer;
    AVPacket packet;
    AVStream *video = NULL;

    static struct SwsContext *img_convert_ctx;

    int videoStream, i, numBytes;
    int ret, got_picture;

    avformat_network_init();
    //Allocate an AVFormatContext.
    pFormatCtx = avformat_alloc_context();

    const char *device_name = "cuda"; //cuda
    enum AVHWDeviceType type = av_hwdevice_find_type_by_name(device_name);
    if(type == AV_HWDEVICE_TYPE_NONE)
    {
        printf("Device type %s is not supported.\n", device_name);
        fprintf(stderr, "Available device types:");
          while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)
              fprintf(stderr, " %s", av_hwdevice_get_type_name(type));
          fprintf(stderr, "\n");
    }

    AVDictionary *opt = nullptr;
//    av_dict_set(&opt,"buffer_size","1024000",0);
//    av_dict_set(&opt,"max_delay","0",0);
    av_dict_set(&opt,"rtsp_transport","tcp",0);
    av_dict_set(&opt,"stimeout","5000000",0);
    if (avformat_open_input(&pFormatCtx, mFileName.toUtf8().data(), NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return;
    }

    ret = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &pCodec, 0);
      if (ret < 0) {
          fprintf(stderr, "Cannot find a video stream in the input file\n");
          return;
      }
      videoStream = ret;

    for (i = 0;; i++) {
          const AVCodecHWConfig *config = avcodec_get_hw_config(pCodec, i);
          if (!config) {
              fprintf(stderr, "Decoder %s does not support device type %s.\n",
                      pCodec->name, av_hwdevice_get_type_name(type));
              return;
          }
          if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
              config->device_type == type) {
              hw_pix_fmt = config->pix_fmt;
              break;
          }
      }
    ///查找解码器
    if (!(pCodecCtx = avcodec_alloc_context3(pCodec)))
        return;

    video = pFormatCtx->streams[videoStream];
    if (avcodec_parameters_to_context(pCodecCtx, video->codecpar) < 0)
        return;

    pCodecCtx->get_format = get_hw_format;

    if (hw_decoder_init(pCodecCtx, type) < 0)
        return;

    ///打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return;
    }

    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();
    tempFrame = av_frame_alloc();
    swFrame = av_frame_alloc();

    ///这里我们改成了 将解码后的YUV数据转换成RGB32
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
            AV_PIX_FMT_NV12, pCodecCtx->width, pCodecCtx->height,
            AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);

    numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);

    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB32,
            pCodecCtx->width, pCodecCtx->height);

    av_dump_format(pFormatCtx, 0, mFileName.toUtf8().data(), 0); //输出视频信息

    while (1)
    {
        if (av_read_frame(pFormatCtx, &packet) < 0)
        {
            break; //这里认为视频读取完了
        }

        if (packet.stream_index == videoStream) {
            ret = avcodec_send_packet(pCodecCtx, &packet);
            if (ret < 0) {
                printf("decode error.\n");
                return;
            }

            while (1) {
                ret = avcodec_receive_frame(pCodecCtx, pFrame);
                if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                {
                    break;
                }else if(ret < 0){
                    qDebug() << "Error while decoding";
                    break;
                }

                ret = av_hwframe_transfer_data(swFrame, pFrame, 0);
                if(ret < 0)
                {
                    qDebug() << "Error transferring the data to system memory";
                    break;
                }
                sws_scale(img_convert_ctx,
                          (uint8_t const * const *) swFrame->data,
                          swFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                          pFrameRGB->linesize);
                static int index = 0;
                qDebug() << "frame" << index++;
            }
        }

        av_packet_unref(&packet);
        msleep(30); //停一停  不然放的太快了
    }
    av_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
  • hw_decoder_init
static enum AVPixelFormat hw_pix_fmt;
static AVBufferRef *hw_device_ctx = NULL;
static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type)
{
    int err = 0;

    if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type,
                                      NULL, NULL, 0)) < 0) {
        fprintf(stderr, "Failed to create specified HW device.\n");
        return err;
    }
    ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);

    return err;
}
  • get_hw_format
static enum AVPixelFormat get_hw_format(AVCodecContext *ctx,
                                        const enum AVPixelFormat *pix_fmts)
{
    const enum AVPixelFormat *p;

    for (p = pix_fmts; *p != -1; p++) {
        if (*p == hw_pix_fmt)
            return *p;
    }

    fprintf(stderr, "Failed to get HW surface format.\n");
    return AV_PIX_FMT_NONE;
}

http://www.kler.cn/a/308718.html

相关文章:

  • 黄色校正电容102j100
  • Springboot整合Prometheus+grafana实现系统监控
  • 沃飞长空郭亮博士荣获中国航空航天月桂奖
  • python数据写入excel文件
  • 扫雷游戏代码分享(c基础)
  • 吴恩达机器学习笔记(3)
  • 关于RabbitMQ重复消费的解决方案
  • 大数据新视界 --大数据大厂之数据挖掘入门:用 R 语言开启数据宝藏的探索之旅
  • 图数据库的力量:深入理解与应用 Neo4j
  • Vue2知识点
  • makefile 的语法(7):函数 word wordlist words firstword lastword ;
  • SurrealDB:现代应用的端到端云原生数据库解决方案
  • Golang | Leetcode Golang题解之第401题二进制手表
  • 【图像拼接】基于SIFT/SURF特征算法的图像拼接,matlab实现
  • 【重学 MySQL】三十三、流程控制函数
  • 探索未来游戏边界:AI驱动的开放世界RPG引擎与UGC平台
  • 【每日一题】LeetCode 2332.坐上公交的最晚时间(数组、双指针、二分查找、排序)
  • 大数据新视界 --大数据大厂之Kafka消息队列实战:实现高吞吐量数据传输
  • Wophp靶场漏洞挖掘
  • 如何在webots中搭建一个履带机器人
  • RISC-V交叉编译器下载
  • 誉龙视音频综合管理平台 RelMedia/FindById SQL注入漏洞复现
  • 如何为聊天机器人添加检索功能:增强响应能力
  • 已开源!无限场景生成和高效数据迁移:3D金字塔扩散模型斩获ECCV24 Oral
  • 错误: 找不到或无法加载主类 org.apache.zookeeper.server.quorum.QuorumPeerMain
  • 设计模式 桥接模式(Bridge Pattern)