FFmpeg 解码一帧压缩Packet
  sdKhIpc8YPFM 2023年11月05日 31 0


头文件

#ifndef FFMPEGDECODER_H
#define FFMPEGDECODER_H

#include <iostream>
#include <memory>
#include <QImage>
#include <QMutexLocker>

extern "C"{
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libavutil/parseutils.h>
#include <libavutil/mem.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
}

class FFmpegDecoder
{
public:
  FFmpegDecoder(AVCodecID codecID=AV_CODEC_ID_H264);
  ~FFmpegDecoder();

public:
  bool init(AVCodecID codecID);
  void parsePkt(uint8_t* data, int len);
  void parsePkt(AVPacket &packet);
  QImage getImage() const;
  void clear();

private:
  void decode();
  void setImage(const QImage &newImage);

private:
  const AVCodec                 *videoCodec   = nullptr;
  AVCodecContext                *videoCodecCtx= nullptr;
  AVCodecParserContext          *parser       = nullptr;
  AVPacket                      *pkt          = nullptr;
  AVFrame                       *yuvFrame     = nullptr;
  AVFrame                       *rgbFrame     = nullptr;
  SwsContext                    *img_ctx      = nullptr;
  unsigned char                 *out_buffer   = nullptr;
  AVCodecID                     id            = AV_CODEC_ID_H264;
  QMutex                        mutex;
  QImage                        image;
  bool                          openCodec     = false;
};

using FFmpegDecoderPtr = std::shared_ptr<FFmpegDecoder>;

#endif // FFMPEGDECODER_H

源文件

#include "FFmpegDecoder.h"
#include <QDebug>

FFmpegDecoder::FFmpegDecoder(AVCodecID codecID)
{
  id = codecID;
  pkt = av_packet_alloc();
  yuvFrame = av_frame_alloc();
  rgbFrame = av_frame_alloc();
}

FFmpegDecoder::~FFmpegDecoder()
{
  if(!parser) av_parser_close(parser);
  if(!pkt) av_packet_free(&pkt);
  if(!yuvFrame) av_frame_free(&yuvFrame);
  if(!rgbFrame) av_frame_free(&rgbFrame);
  if(!videoCodecCtx) avcodec_free_context(&videoCodecCtx);
  if(!videoCodecCtx) avcodec_close(videoCodecCtx);
}

bool FFmpegDecoder::init(AVCodecID codecID)
{
  id = codecID;
  if(!videoCodec){
    if(!(videoCodec = avcodec_find_decoder(id))){
      printf("Cannot find valid decode codec.\n");
      return false;
    }
  }
  if(!parser){
    if (!(parser = av_parser_init(id))) {
      printf("parser not found\n");
      return false;
    }
  }
  if(!videoCodecCtx){
    if(!(videoCodecCtx = avcodec_alloc_context3(videoCodec))){
      printf("Cannot find valid decode codec context.\n");
      return false;
    }
  }

  if(!openCodec){
    if (avcodec_open2(videoCodecCtx, videoCodec, NULL) < 0) {
      printf("Cannot open codec.\n");
      return false;
    }
    openCodec = true;
  }

  return true;
}

void FFmpegDecoder::parsePkt(uint8_t *data, int len)
{
  int eof = !len;
  while (len > 0 || eof) {
    int ret = av_parser_parse2(parser, videoCodecCtx, &pkt->data, &pkt->size,
                               data, 4096, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
    if (ret < 0) {
      fprintf(stderr, "Error while parsing\n");
      continue;
    }

    data += ret;
    len -= ret;

    if (pkt->size) {
      decode();
    }
  }
}

void FFmpegDecoder::decode()
{
  int ret = avcodec_send_packet(videoCodecCtx, pkt);
  if (ret >= 0) {
    while ((ret = avcodec_receive_frame(videoCodecCtx, yuvFrame)) >= 0) {
      if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        break;
      else if (ret < 0) {
        fprintf(stderr, "Error during decoding\n");
        break;
      }
      if(!img_ctx){
        img_ctx = sws_getContext(videoCodecCtx->width,
                                 videoCodecCtx->height,
                                 videoCodecCtx->pix_fmt,
                                 videoCodecCtx->width,
                                 videoCodecCtx->height,
                                 AV_PIX_FMT_RGB32,
                                 SWS_BICUBIC, NULL, NULL, NULL);
      }
      if(!out_buffer){
        int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32,videoCodecCtx->width,videoCodecCtx->height,1);
        out_buffer = (unsigned char *)av_malloc(numBytes*sizeof(unsigned char));

        int res = av_image_fill_arrays(
              rgbFrame->data,rgbFrame->linesize,
              out_buffer,AV_PIX_FMT_RGB32,
              videoCodecCtx->width,videoCodecCtx->height,1);
        if(res<0){
          qDebug()<<"Fill arrays failed.";
          break;
        }
      }

      sws_scale(img_ctx,
                yuvFrame->data, yuvFrame->linesize,
                0, videoCodecCtx->height,
                rgbFrame->data, rgbFrame->linesize);

      QImage img(out_buffer,
                 videoCodecCtx->width, videoCodecCtx->height,
                 QImage::Format_RGB32);
      setImage(img);
      av_packet_unref(pkt);
    }
  }
  av_packet_unref(pkt);
}

QImage FFmpegDecoder::getImage() const
{
  return image;
}

void FFmpegDecoder::parsePkt(AVPacket& packet)
{
  pkt->data = packet.data;
  pkt->size = packet.size;
  decode();
}

void FFmpegDecoder::clear()
{
  avcodec_flush_buffers(videoCodecCtx);
}

void FFmpegDecoder::setImage(const QImage &newImage)
{
  QMutexLocker locker(&mutex);
  image = newImage;
}


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

上一篇: Qt开发笔记 下一篇: H264转MP4
  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  8Tw5Riv1mGFK   2024年05月01日   80   0   0 C++
  BYaHC1OPAeY4   2024年05月08日   56   0   0 C++
  yZdUbUDB8h5t   2024年05月05日   43   0   0 C++
  oXKBKZoQY2lx   2024年05月17日   57   0   0 C++
sdKhIpc8YPFM