[FFmpeg] Qt + FFmpeg解码RTSP视频流代码

8 篇文章 0 订阅
2 篇文章 0 订阅

完整QML视频教程:http://i.xue.taobao.com/detail.htm?courseId=113958

Qt 5.12.8

VideoBuffer 头文件:

#ifndef VIDEOBUFFER_H
#define VIDEOBUFFER_H

#include <QAbstractVideoBuffer>

class VideoBuffer : public QAbstractVideoBuffer
{
public:
    VideoBuffer(uchar *data, int numBytes, int bytesPerLine);

    virtual uchar *map(QAbstractVideoBuffer::MapMode mode, int *numBytes,
                       int *bytesPerLine) override;

    virtual QAbstractVideoBuffer::MapMode mapMode() const override;

    virtual void unmap() override;

    void setMap(uchar *data, int numBytes, int bytesPerLine) {
        m_data = data;
        m_numBytes = numBytes;
        m_bytesPerLine = bytesPerLine;
    }

private:
    uchar *m_data;
    int m_numBytes;
    int m_bytesPerLine;
};

#endif // VIDEOBUFFER_H

VideoBuffer 源文件:

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

VideoBuffer::VideoBuffer(uchar *data, int numBytes, int bytesPerLine)
    : QAbstractVideoBuffer(QAbstractVideoBuffer::NoHandle)
    , m_data(data)
    , m_numBytes(numBytes)
    , m_bytesPerLine(bytesPerLine)
{

}

uchar *VideoBuffer::map(QAbstractVideoBuffer::MapMode mode, int *numBytes,
                        int *bytesPerLine)
{
    Q_UNUSED(mode)

    *numBytes = m_numBytes;
    *bytesPerLine = m_bytesPerLine;

    return m_data;
}

QAbstractVideoBuffer::MapMode VideoBuffer::mapMode() const
{
    return QAbstractVideoBuffer::ReadOnly;
}

void VideoBuffer::unmap()
{
    //delete m_data;
}

RtspDecoder 头文件:

#ifndef RTSPDECODER_H
#define RTSPDECODER_H

#include <QThread>
#include <QVideoFrame>
#include <QUrl>

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

class RtspDecoder : public QThread
{
    Q_OBJECT

public:
    explicit RtspDecoder(QObject *parent = nullptr);
    ~RtspDecoder();

    inline int width() const;
    inline int height() const;
    inline QSize frameSize() const;

    inline void setUrl(const QByteArray &url) { m_url = url; }

public slots:
    void play();
    void stop();

signals:
    void frameSizeChanged(int width, int height);
    void newVideoFrame(const QVideoFrame &frame);

protected:
    void run();

private:
    bool open();
    void free();

    QByteArray m_url;
    bool                m_isOpen = false;
    bool                m_playing = false;
    AVFormatContext*    m_fmtCtx = nullptr;
    AVCodecContext*     m_videoCtx = nullptr;
    AVDictionary*       m_opts = nullptr;
    int                 m_streamIndex = -1;
    AVFrame*            m_frame = nullptr;
    uint8_t*            m_videoData[4] = {nullptr};
    int                 m_videoLineSize[4];
    int                 m_videoSize;
};

int RtspDecoder::width() const
{
    return m_videoCtx ? m_videoCtx->width : 0;
}

int RtspDecoder::height() const
{
    return m_videoCtx ? m_videoCtx->height : 0;
}

QSize RtspDecoder::frameSize() const
{
    return QSize(width(), height());
}

#endif // RTSPDECODER_H

源文件:

#include "RtspDecoder.h"
#include "VideoBuffer.h"
#include <QDateTime>
#include <QDebug>

RtspDecoder::RtspDecoder(QObject *parent)
    : QThread(parent)
{
    avformat_network_init();
}

RtspDecoder::~RtspDecoder()
{
    stop();
    free();
}

bool RtspDecoder::open()
{
    /* open input file, and allocate format context */
    av_dict_set(&m_opts, "stimeout", "10000000", 0);
    av_dict_set(&m_opts, "rtsp_transport", "tcp", 0);
    if (avformat_open_input(&m_fmtCtx, m_url.constData(), nullptr, &m_opts) < 0) {
        qWarning("Could not open source file %s.", m_url.constData());
        return false;
    }

    /* retrieve stream information */
    if (avformat_find_stream_info(m_fmtCtx, nullptr) < 0) {
        qWarning("Could not find stream information.");
        return false;
    }

    m_streamIndex = av_find_best_stream(m_fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (m_streamIndex < 0) {
        qWarning("Could not find stream in input file.");
        return false;
    }

    AVStream *videoStream = m_fmtCtx->streams[m_streamIndex];

    /* find decoder for the stream */
    AVCodec *dec = avcodec_find_decoder(videoStream->codecpar->codec_id);
    if (!dec) {
        qWarning("Failed to find codec.");
        return false;
    }

    /* Allocate a codec context for the decoder */
    m_videoCtx = avcodec_alloc_context3(dec);
    if (!m_videoCtx) {
        qWarning("Failed to allocate the codec context.");
        return false;
    }

    /* Copy codec parameters from input stream to output codec context */
    if (avcodec_parameters_to_context(m_videoCtx, videoStream->codecpar) < 0) {
        qWarning("Failed to copy codec parameters to decoder context.");
        free();
        return false;
    }

    /* Init the decoders, with or without reference counting */
    if (avcodec_open2(m_videoCtx, dec, nullptr) < 0) {
        qWarning("Failed to open codec.");
        free();
        return false;
    }

    m_frame = av_frame_alloc();
    if (!m_frame) {
        qWarning("Could not allocate frame.");
        free();
        return false;
    }

    m_videoSize = av_image_alloc(m_videoData, m_videoLineSize,
                                width(), height(), m_videoCtx->pix_fmt, 1);
    if (m_videoSize < 0) {
        qWarning("Could not allocate raw video buffer.");
        free();
        return false;
    }

    emit frameSizeChanged(width(), height());
    m_isOpen = true;

    return true;
}

void RtspDecoder::play()
{
    if (isRunning())
        return ;

    m_playing = true;
    start();
}

void RtspDecoder::stop()
{
    m_playing = false;
    wait(); // wait thread exit
}

void RtspDecoder::run()
{
    if (!m_isOpen)
    {
        if (!open())
            return ;
    }

    AVPacket pkt;

    /* initialize packet, set data to NULL, let the demuxer fill it */
    av_init_packet(&pkt);
    pkt.data = nullptr;
    pkt.size = 0;

    while (av_read_frame(m_fmtCtx, &pkt) >= 0)
    {
        if (!m_playing)
            break;

        if(pkt.stream_index != m_streamIndex)
            continue;

        if (avcodec_send_packet(m_videoCtx, &pkt) < 0) {
            qWarning("Error sending a packet for decoding.");
            continue;
        }

        if (avcodec_receive_frame(m_videoCtx, m_frame) >= 0)
        {
            /* copy decoded frame to destination buffer:
             * this is required since rawvideo expects non aligned data */
            av_image_copy(m_videoData, m_videoLineSize,
                          const_cast<const uint8_t **>(m_frame->data), m_frame->linesize,
                          m_videoCtx->pix_fmt, width(), height());

            VideoBuffer *buffer = new VideoBuffer(m_videoData[0], m_videoSize, m_videoLineSize[0]);

            emit newVideoFrame(QVideoFrame(buffer, frameSize(), QVideoFrame::Format_YUV420P));
        }

        av_packet_unref(&pkt);
    }
}

void RtspDecoder::free()
{
    avcodec_close(m_videoCtx);
    avformat_close_input(&m_fmtCtx);

    if (m_videoCtx)
        avcodec_free_context(&m_videoCtx);

    if (m_frame)
        av_frame_free(&m_frame);

    if (m_opts)
        av_dict_free(&m_opts);
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值