• ffmpeg解码RTSP/TCP视频流H.264(QT界面显示视频画面)


    源码下载地址: http://download.csdn.net/detail/liukang325/9489952

    我用的ffmpeg版本为 ffmpeg-2.1.8.tar.bz2 
    版本低了恐怕有些头文件和API找不到。 
    在Linux下解压后编译,Linux下编译很简单,我这里生成的动态库: 
    ./configure –enable-shared 
    make 
    就能找到各个so动态库文件。 
    移动位置后,记得手动链接 一下:

    ln -s libavcodec.so.55 libavcodec.so
    ln -s libavdevice.so.55 libavdevice.so
    ln -s libavfilter.so.3 libavfilter.so
    ln -s libavformat.so.55 libavformat.so
    ln -s libavutil.so.52 libavutil.so
    ln -s libswscale.so.2 libswscale.so

    QT pro文件中记得加入: 
    INCLUDEPATH += ffmpeg/include 
    // windows下用这几个 
    win32: LIBS += ffmpeg/lib/libavcodec.dll.a ffmpeg/lib/libavfilter.dll.a ffmpeg/lib/libavformat.dll.a ffmpeg/lib/libswscale.dll.a ffmpeg/lib/libavutil.dll.a 
    // Linux下用这几个 
    LIBS += -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lswscale -L./ffmpeg/so

    我这里对外提供三个接口:

    void VideoStream::setUrl(QString url)
    {
        m_str_url = url;
    }
    
    void VideoStream::startStream()
    {
        videoStreamIndex=-1;
        av_register_all();//注册库中所有可用的文件格式和解码器
        avformat_network_init();//初始化网络流格式,使用RTSP网络流时必须先执行
        pAVFormatContext = avformat_alloc_context();//申请一个AVFormatContext结构的内存,并进行简单初始化
        pAVFrame=av_frame_alloc();
        if (this->Init())
        {
            m_timerPlay->start();
        }
    }
    void VideoStream::stopStream()
    {
        m_timerPlay->stop();
    
        avformat_free_context(pAVFormatContext);
        av_frame_free(&pAVFrame);
        sws_freeContext(pSwsContext);
    }

    里面与ffmpeg解码相关的私有变量:

        QMutex mutex;
        AVPicture  pAVPicture;
        AVFormatContext *pAVFormatContext;
        AVCodecContext *pAVCodecContext;
        AVFrame *pAVFrame;
        SwsContext * pSwsContext;
        AVPacket pAVPacket;

    在QT里,用一个QLabel的对象来显示解码后的视频画面:

    connect(this,SIGNAL(GetImage(QImage)),this,SLOT(SetImageSlots(QImage)));
    
    ...
    void VideoStream::SetImageSlots(const QImage &image)
    {
        if (image.height()>0){
            QPixmap pix = QPixmap::fromImage(image.scaled(m_i_w,m_i_h));
            m_label->setPixmap(pix);
        }
    }

    这里用的QTimer来进行一帧帧数据的解码,也可以用一个线程比如QThread来进行解码:

        m_timerPlay = new QTimer;
        m_timerPlay->setInterval(10);
        connect(m_timerPlay,SIGNAL(timeout()),this,SLOT(playSlots()));
    
        m_i_frameFinished = 0;
    
    ... 
    bool VideoStream::Init()
    {
        if(m_str_url.isEmpty())
            return false;
        //打开视频流
        int result=avformat_open_input(&pAVFormatContext, m_str_url.toStdString().c_str(),NULL,NULL);
        if (result<0){
            qDebug()<<"打开视频流失败";
            return false;
        }
    
        //获取视频流信息
        result=avformat_find_stream_info(pAVFormatContext,NULL);
        if (result<0){
            qDebug()<<"获取视频流信息失败";
            return false;
        }
    
        //获取视频流索引
        videoStreamIndex = -1;
        for (uint i = 0; i < pAVFormatContext->nb_streams; i++) {
            if (pAVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                videoStreamIndex = i;
                break;
            }
        }
    
        if (videoStreamIndex==-1){
            qDebug()<<"获取视频流索引失败";
            return false;
        }
    
        //获取视频流的分辨率大小
        pAVCodecContext = pAVFormatContext->streams[videoStreamIndex]->codec;
        videoWidth=pAVCodecContext->width;
        videoHeight=pAVCodecContext->height;
    
        avpicture_alloc(&pAVPicture,PIX_FMT_RGB24,videoWidth,videoHeight);
    
        AVCodec *pAVCodec;
    
        //获取视频流解码器
        pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id);
        pSwsContext = sws_getContext(videoWidth,videoHeight,PIX_FMT_YUV420P,videoWidth,videoHeight,PIX_FMT_RGB24,SWS_BICUBIC,0,0,0);
    
        //打开对应解码器
        result=avcodec_open2(pAVCodecContext,pAVCodec,NULL);
        if (result<0){
            qDebug()<<"打开解码器失败";
            return false;
        }
    
        qDebug()<<"初始化视频流成功";
        return true;
    }
    
    void VideoStream::playSlots()
    {
        //一帧一帧读取视频
        if (av_read_frame(pAVFormatContext, &pAVPacket) >= 0){
            if(pAVPacket.stream_index==videoStreamIndex){
                qDebug()<<"开始解码"<<QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
                avcodec_decode_video2(pAVCodecContext, pAVFrame, &m_i_frameFinished, &pAVPacket);
                if (m_i_frameFinished){
                    mutex.lock();
                    sws_scale(pSwsContext,(const uint8_t* const *)pAVFrame->data,pAVFrame->linesize,0,videoHeight,pAVPicture.data,pAVPicture.linesize);
                    //发送获取一帧图像信号
                    QImage image(pAVPicture.data[0],videoWidth,videoHeight,QImage::Format_RGB888);
                    emit GetImage(image);
                    mutex.unlock();
                }
            }
        }
        av_free_packet(&pAVPacket);//释放资源,否则内存会一直上升
    }

    备注: 
    头文件包含及注意事项

    //必须加以下内容,否则编译不能通过,为了兼容C和C99标准
    
    #ifndef INT64_C
    #define INT64_C
    #define UINT64_C
    #endif
    
    //引入ffmpeg头文件
    extern "C"
    {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libswscale/swscale.h>
    #include <libavutil/frame.h>
    }

    启发: 
    setUrl(QString url); 
    这里的url 一般情况下是一个RTSP流的播放地址,如rtsp://192.168.1.123:554/stream1 
    但也可以是一个TCP流。 
    我这边测试的是一个本地的socket流,设url地址为 http://127.0.0.1:5858 
    可直接解码播放。

  • 相关阅读:
    I/O工作机制
    Apache和Tomcat区别
    jenkins学习和使用
    小程序富文本转化插件
    一个正则表达式的用法
    contenteditable="true"让div可编辑
    JS实现品字布局
    扯扯小程序。
    (canvas)两小球碰撞后的速度问题研究
    canvas画多边形
  • 原文地址:https://www.cnblogs.com/lidabo/p/6404551.html
Copyright © 2020-2023  润新知