Decoding h264 frames from RTP stream

前端 未结 2 1653
情书的邮戳
情书的邮戳 2021-02-04 21:46

I am using live555 and ffmpeg libraries to get and decode RTP H264 stream from server; Video stream was encoded by ffmpeg, using Baseline profile and

x264_param         


        
2条回答
  •  慢半拍i
    慢半拍i (楼主)
    2021-02-04 21:57

    Ok. This my simple example, which decode H264 stream, which received from liveMedia library. It can decode frames, which are truncated not only at boundary of frames.

       class H264Decoder : public AbstractDecoder
        {
            Q_OBJECT
        public:
            H264Decoder( QObject* parent = nullptr );
            virtual ~H264Decoder();
    
        public slots:
    
            virtual bool decodeFrame(SharedEncodedFrame orig_frame) override;
    
        signals:
            //sended when we have new decoded frame
            void newDecodedFrame( QImage img );
    
        protected:
            void storePicture(std::vector& res);
    
            AVCodec* m_decoder;
            AVCodecContext* m_decoderContext;
            int m_got_picture;
            AVFrame* m_picture;
            AVPacket m_packet;
        };
    

    And this is realization:

    #include "H264Decoder.hpp"
    #include "ImgConverting.hpp"
    #include 
    extern "C"
    {
    #include 
    }
    
    using namespace std;
    
    H264Decoder::H264Decoder( QObject *parent)
        : AbstractDecoder(parent), m_decoder(nullptr), m_decoderContext(nullptr),
          m_got_picture(0), m_picture(nullptr)
    {
        avcodec_register_all();
        av_init_packet(&m_packet);
        m_decoder = avcodec_find_decoder(CODEC_ID_H264);
        if (!m_decoder)
        {
            QString str = QString("Can't find H264 decoder!");
            emit criticalError(str);
        }
        m_decoderContext = avcodec_alloc_context3(m_decoder);
    
        if (m_decoder->capabilities & CODEC_CAP_TRUNCATED)
            m_decoderContext->flags |= CODEC_FLAG_TRUNCATED;
    
    
        //we can receive truncated frames
        m_decoderContext->flags2 |= CODEC_FLAG2_CHUNKS;
        m_decoderContext->thread_count = 4;//TODO: random value. May be changing can make decoding faster
    
        AVDictionary* dictionary = nullptr;
        if (avcodec_open2(m_decoderContext, m_decoder, &dictionary) < 0)
        {
            QString str = QString("Failed to open decoder!");
            emit criticalError(str);
        }
        qDebug() << "H264 Decoder successfully opened";
        m_picture = avcodec_alloc_frame();
    }
    
    H264Decoder::~H264Decoder()
    {
        qDebug() << "ACHTUNG!!! H264Decoder deleted!!!\r\n\r\n";
        if (m_decoderContext)
        {
            avcodec_close(m_decoderContext);
            delete m_decoderContext;
        }
    }
    
    
    bool H264Decoder::decodeFrame(SharedEncodedFrame orig_frame)
    {
        Frame_t enc_frame;
        orig_frame >> enc_frame;
        m_packet.size = enc_frame.size();
        m_packet.data = enc_frame.data();
    
        qDebug() << "H264Decoder: received encoded frame with framesize " << enc_frame.size();
    
        while(m_packet.size > 0)
        {
            int got_picture;
            int len = avcodec_decode_video2(m_decoderContext, m_picture, &got_picture, &m_packet);
            if (len < 0)
            {
                QString err("Decoding error");
                qDebug() << err;
                return false;
            }
            if (got_picture)
            {
                qDebug() << "H264Decoder: frame decoded!";
                std::vector result;
                this->storePicture(result);
    
                if ( m_picture->format == PIX_FMT_YUV420P )
                {
                    static SwsContext *m_swsCtx = NULL;
                    QImage frame_img = QImage(m_picture->width, m_picture->height, QImage::Format_RGB888);
                    m_swsCtx = sws_getCachedContext ( m_swsCtx, m_picture->width,
                        m_picture->height, PIX_FMT_YUV420P,
                        m_picture->width, m_picture->height,
                        PIX_FMT_RGB24, SWS_GAUSS,
                        NULL, NULL, NULL );
                    uint8_t *dstSlice[] = { frame_img.bits() };
                    int dstStride = frame_img.width() * 3;
                    if (sws_scale ( m_swsCtx, m_picture->data, m_picture->linesize,
                        0, m_picture->height, dstSlice, &dstStride ) != m_picture->height )
                    {
                        qDebug() << "PIZDETS!!!";
                        exit(-5);
                    }
                    qDebug() << "New decoded image!";
                    emit newDecodedFrame(frame_img);
                }
                else if (m_picture->format == PIX_FMT_RGB32)
                {
                    QImage img = QImage(result.data(), m_picture->width, m_picture->height, QImage::Format_RGB32);
                    qDebug() << "New decoded image!";
                    emit newDecodedFrame(img);
                }
                else if (m_picture->format == PIX_FMT_RGB24)
                {
                    QImage img = QImage(result.data(), m_picture->width, m_picture->height, QImage::Format_RGB888);
                    qDebug() << "New decoded image!";
                    emit newDecodedFrame(img);
                }
                else
                {
                    QString err = QString( "Unsupported pixel format! Can't create QImage!");
                    qDebug() << err;
                    emit criticalError( err );
                    return false;
                }
            }
            m_packet.size -= len;
            m_packet.data += len;
        }
    
        return true;
    }
    
    void H264Decoder::storePicture(std::vector& res)
    {
        for (size_t i = 0; i < AV_NUM_DATA_POINTERS; i++)
        {
            std::copy(m_picture->data[i], m_picture->data[i] + 
                m_picture->linesize[i]*m_picture->height, std::back_inserter(res));
        }
    }
    

    I send newDecodedFrame to GUI thread, where this QImage will be drawn at some widget.

    P.S: This comment is very long for posting as comment's comment

提交回复
热议问题