Qt使用FFmpeg解码H.264视频流转成QImage

参考: Qt使用FFmpeg解码H.264视频流转成QImage - 简书

AnnexB流的ffmpeg硬解、截图成JPEG、OpenGL渲染、编码成MP4 - 掘金

FFmpeg的H264解码实战 - 掘金

在做一个解码H264播放转QImage,使用定时器刷新QLabel,效果有点卡顿

软编码:

#ifndef H264_H
#define H264_H

#include <QObject>
#include <QByteArray>
#include <QMutex>
#include <QByteArray>
#include <QThread>
#include <QImage>

extern "C"{
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
    #include <libavutil/imgutils.h>
}

class H264 : public QThread
{
    Q_OBJECT
public:
    explicit H264(QObject *parent = nullptr);
    ~H264();
    void appendData(const QByteArray& data);
    void appendData(std::vector<uint8_t>& data);
    void close() { m_isRun = false; wait(); }
    void setTopicName(std::string topicName){this->topicName = topicName;};
    std::string getTopicName(){return topicName;};

private:
    bool init();
    bool genImageObj();
    void decode();

signals:
    void imageData(QImage image);

protected:
    void run() override;

private:
    AVPacket *m_avPacket = nullptr;      //  音视频数据包
    AVFrame *m_avFrameInput = nullptr;   //  解码的帧数据
    AVFrame *m_avFramePicture = nullptr;  // 转换的帧图片
    AVCodec *m_pCodec = nullptr;          //编码器
    AVCodecContext *m_avCodecCtx = nullptr; //编码器上下文
    AVCodecParserContext *m_pCodecParserCtx = nullptr; //裸流解析器上下文
    SwsContext *m_pSwsContext = nullptr;   //格式转换上下文

    int m_nVideoWidth;
    int m_nVideoHeight;
    uint8_t *m_pPicBuffer = nullptr;

    QByteArray m_buffer;
    QMutex m_mutex;
    bool m_isFirst;
    bool m_isRun;
    std::string topicName;
};

#endif // H264_H
#include "H264.h"
#include <QMutexLocker>
#include <QDebug>

H264::H264(QObject *parent)
    : QThread(parent), m_isFirst(true)
{
    init();
}
H264::~H264(){
    if(m_avPacket)
        av_packet_free(&m_avPacket);
    if(m_avFrameInput)
        av_frame_free(&m_avFrameInput);
    if(m_avFramePicture)
        av_frame_free(&m_avFramePicture);
    if(m_avCodecCtx)
        avcodec_free_context(&m_avCodecCtx);
    if(m_pPicBuffer){
        av_free(m_pPicBuffer);
        m_pPicBuffer = nullptr;
    }
}

bool H264::init()
{
    m_avPacket = av_packet_alloc(); //创建AVPacket
    av_init_packet(m_avPacket);  //初始化
    m_avFrameInput = av_frame_alloc();  //创建解码的帧
    m_avFramePicture = av_frame_alloc(); //创建图片帧
    //获取解码器: h264
    m_pCodec = (AVCodec*)avcodec_find_decoder(AVCodecID::AV_CODEC_ID_H264);
    if (!m_pCodec) return false;
    //创建上下文
    m_avCodecCtx = avcodec_alloc_context3(m_pCodec);
    if (!m_avCodecCtx) return false;
    //初始化裸流解析器
    m_pCodecParserCtx = av_parser_init(AVCodecID::AV_CODEC_ID_H264);
    if (!m_pCodecParserCtx) return false;
    将解码器和解码器上下文进行关联
    if (avcodec_open2(m_avCodecCtx, m_pCodec, nullptr) < 0) {
        return false;
    }
    return true;
}

bool H264::genImageObj()
{
    AVPixelFormat srcFmt = AV_PIX_FMT_YUV420P;
    AVPixelFormat dstFmt = AV_PIX_FMT_RGB32;

    srcFmt = m_avCodecCtx->pix_fmt;
    //获取存储RGB32的存储所需空间大小
    int bytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, m_nVideoWidth, m_nVideoHeight, 1);
    //分配图片缓存空间
    m_pPicBuffer = (uint8_t*)av_malloc(bytes * sizeof(uint8_t));
    av_image_fill_arrays(m_avFramePicture->data, m_avFramePicture->linesize, m_pPicBuffer, dstFmt, m_nVideoWidth, m_nVideoHeight, 1);
    //由于解码出来的帧格式不是RGB的,在编码之前需要进行格式转换,获取转换上下文
    m_pSwsContext = sws_getContext(m_nVideoWidth, m_nVideoHeight, srcFmt, m_nVideoWidth, m_nVideoHeight, dstFmt, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
    return true;
}

void H264::appendData(const QByteArray &data)
{
    QMutexLocker _(&m_mutex);
    m_buffer += data;
}

void H264::appendData(std::vector<uint8_t>& data){
    QMutexLocker _(&m_mutex);
    m_buffer.append(reinterpret_cast<const char*>(data.data()), data.size());
}

void H264::decode()
{
    QMutexLocker _(&m_mutex);

    while (m_buffer.size() > 0) {
        const int nBufferSize = m_buffer.size() + FF_BUG_NO_PADDING;
        QByteArray buf;
        buf.fill(0, nBufferSize);
        memcpy(buf.data(), m_buffer.constData(), m_buffer.size());
        // 读取数据,av_parser_parse2函数用来解析出一个完整的Packet
        int nLength = av_parser_parse2(
          m_pCodecParserCtx,
          m_avCodecCtx,
          &m_avPacket->data,
          &m_avPacket->size,
          (const uint8_t*)buf.constData(),
          m_buffer.size(),
          AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);

        m_buffer.remove(0, nLength);

        if (m_avPacket->size == 0) {
            return;
        }

        switch (m_pCodecParserCtx->pict_type) {
            case AV_PICTURE_TYPE_I: break;
            case AV_PICTURE_TYPE_P: break;
            case AV_PICTURE_TYPE_B: break;
            default: break;
        }
        //avcodec_send_packet和avcodec_receive_frame是成双结对出现的,用于解码packet数据
        avcodec_send_packet(m_avCodecCtx, m_avPacket);
        //输出解码后的帧数据
        int ret = avcodec_receive_frame(m_avCodecCtx, m_avFrameInput);
        if (ret < 0) return;
        if (m_isFirst) {
            m_nVideoWidth = m_avCodecCtx->width;
            m_nVideoHeight = m_avCodecCtx->height;
            genImageObj(); //生成转换图片对象
            m_isFirst = false;
        }
        //格式转换,数据转换为RGB格式
        sws_scale(m_pSwsContext, m_avFrameInput->data, m_avFrameInput->linesize, 0, m_nVideoHeight, m_avFramePicture->data, m_avFramePicture->linesize);
        QImage image(m_avFramePicture->data[0], m_nVideoWidth, m_nVideoHeight, QImage::Format_ARGB32);
        emit imageData(image);
    }
}

void H264::run()
{
    m_isRun = true;
    while (m_isRun) {
        decode();
    }
}

硬编码:

方法一:可以提高2-3倍的解码速度

将上文初始化中的下面这句
m_pCodec = (AVCodec*)avcodec_find_decoder(AVCodecID::AV_CODEC_ID_H264);
修改为对应硬件支持的编码方法,比如NVIDIA cuda支持的h264解码器:
m_pCodec = (AVCodec*)avcodec_find_decoder_by_name("h264_cuvid");

查看支持的编码器:

root@jiack-VirtualBox# ffmpeg -decoders| grep 264
ffmpeg version 4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
  built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04)
  configuration: --prefix=/usr/local/ffmpeg --enable-cuda --enable-cuvid --enable-nvenc --enable-static --enable-shared --enable-gpl --enable-libass --enable-libfreetype --enable-libvorbis --enable-libx264 --enable-libx265 --enable-nonfree
  libavutil      56. 51.100 / 56. 51.100
  libavcodec     58. 91.100 / 58. 91.100
  libavformat    58. 45.100 / 58. 45.100
  libavdevice    58. 10.100 / 58. 10.100
  libavfilter     7. 85.100 /  7. 85.100
  libswscale      5.  7.100 /  5.  7.100
  libswresample   3.  7.100 /  3.  7.100
  libpostproc    55.  7.100 / 55.  7.100
 VFS..D h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
 V..... h264_v4l2m2m         V4L2 mem2mem H.264 decoder wrapper (codec h264)
 V..... h264_cuvid           Nvidia CUVID H264 decoder (codec h264)

相关链接:FFMPEG最简解码H264(NVIDIA硬解)_ffmpeg硬解码h264-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值