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");
    }

  2. 通用过程

    avformat_open_input
    avformat_find_stream_info
    av_find_best_stream

  3. 获取硬件编码器

    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;
    }
    }

  4. 查找解码器

    if (!(pCodecCtx = avcodec_alloc_context3(pCodec)))
    return;

  5. 复制参数

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

     pCodecCtx->get_format = get_hw_format;
    
  6. 初始化硬件解码器与打开解码器

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

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

  7. 读取视频包,发送视频包到解码器,解码

  8. 将显存中的内容复制内存中

    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;
    

    }

相关推荐
Watermelo61723 分钟前
通过MongoDB Atlas 实现语义搜索与 RAG——迈向AI的搜索机制
人工智能·深度学习·神经网络·mongodb·机器学习·自然语言处理·数据挖掘
AI算法-图哥35 分钟前
pytorch量化训练
人工智能·pytorch·深度学习·文生图·模型压缩·量化
大山同学37 分钟前
DPGO:异步和并行分布式位姿图优化 2020 RA-L best paper
人工智能·分布式·语言模型·去中心化·slam·感知定位
机器学习之心38 分钟前
时序预测 | 改进图卷积+informer时间序列预测,pytorch架构
人工智能·pytorch·python·时间序列预测·informer·改进图卷积
醉颜凉1 小时前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
hunandede1 小时前
FFmpeg 4.3 音视频-多路H265监控录放C++开发十三.2:avpacket中包含多个 NALU如何解析头部分析
c++·ffmpeg·音视频
天飓1 小时前
基于OpenCV的自制Python访客识别程序
人工智能·python·opencv
檀越剑指大厂1 小时前
开源AI大模型工作流神器Flowise本地部署与远程访问
人工智能·开源
声网1 小时前
「人眼视觉不再是视频消费的唯一形式」丨智能编解码和 AI 视频生成专场回顾@RTE2024
人工智能·音视频
newxtc1 小时前
【AiPPT-注册/登录安全分析报告-无验证方式导致安全隐患】
人工智能·安全·ai写作·极验·行为验证