QT播放器插件内嵌浏览器配合流媒体实现国标摄像头网页端播放

一、浏览器内嵌C++播放器 

        基本原理在浏览器⽹页中的指定播放窗口的位置和⼤⼩,实现⼀个内嵌到⽹页中显⽰的播放窗⼝,前端还必须可对这个内嵌播放窗⼝进⾏控制,⽽且播放窗⼝必须跟随浏览器窗⼝的移动和缩放、⽹页滚动、标签页切换、关闭等操作进⾏⾃动联动。播放器可以通过QT实现。这个播放窗⼝同时提供Web Socket的服务端和JSON打包命令的解析执⾏模块,前端就可以通过Web Socket连接后发送JSON打包的控制命令实现控制播放窗⼝。

二、浏览器内嵌播放器实现

        实现功能,通过插件在网页端播放H264和H265视频流,并能实现抓图功能。

下面是部分JavaScript代码

        /*实现显示*/
        show: function () {
            var that = this
            this.setting.stopRefresh = false
            this.setting.show = true
            this.send({
                method: 'window.show',
                info: {
                    show: true
                }
            },


        /**
         * @method playReal 实时预览
         * @param { String } option.devId 设备ID
         * @param { Number } option.winIndex 选择的窗口号,从0开始
         * @param { String } option.url rtsp地址
         * @param { Number } option.decodeType 软/硬解码 0-软解 1-硬解
         * @param { Number } option.connectType 连接方式 0-TCP 1-UDP 
         */
        playReal: function (option) {
            this.stopVideo(option.winIndex)
            this.send({
                method: 'media.playReal',
                info: {
                	devId: option.devId,
                    winIndex: option.winIndex,
                    url: option.url,
                    decodeType: option.decodeType,
                    connectType: option.connectType
                }
            })
        },

   C++部分通过调用ffmpeg来实现拉流,部分实现如下

    MY_DEBUG << "ctFFmpeg::Init sUrl:" << sUrl << " start.";

    //AVCodec *pvAVCodec = nullptr; // ffmpeg low than 5.1
    const AVCodec *pvAVCodec = nullptr; //ffmpeg 5.1

    m_bNeedToRecon = false;
    m_bQuit = false;
    if(sUrl.contains("rtsp://") || sUrl.contains("http://")) //实时流
        m_nPlayWay = MEDIA_PLAY_STREAM;
    else
        m_nPlayWay = MEDIA_PLAY_FILE;

    avformat_network_init();//初始化网络流

    //参数设置
    AVDictionary* pOptDict = NULL;
    if(nProtocolType == MEDIA_PROTOCOL_UDP)
    {
        MY_DEBUG << "UDP";
        av_dict_set(&pOptDict, "rtsp_transport", "udp", 0);
    }
    else
    {
        MY_DEBUG << "TCP";
        av_dict_set(&pOptDict, "rtsp_transport", "tcp", 0);
    }

    m_nProtocolType = nProtocolType;
    m_nDecodeType = nDecodeType;
    m_nShowType = nShowType;
    m_bEnablePush = bEnablePush;
    m_pShareQueue = pQueue;
    m_sRtmpServerAddr = sServerAddr;

    av_dict_set(&pOptDict, "stimeout", "5000000", 0);
    av_dict_set(&pOptDict, "buffer_size", "8192000", 0);
    av_dict_set(&pOptDict, "recv_buffer_size", "4096000", 0);     // 防止花屏, max 4M.
    av_dict_set(&pOptDict, "tune", "stillimage,fastdecode,zerolatency", 0);//快速解码和低延时

    if(nullptr == m_pAVFmtCxt)
        m_pAVFmtCxt = avformat_alloc_context();
    if(nullptr == m_pYuvFrame)
        m_pYuvFrame = av_frame_alloc();
    if(nullptr == m_pDstVideoFrame)
        m_pDstVideoFrame = av_frame_alloc();
    if(nullptr == m_pPcmFrame)
        m_pPcmFrame = av_frame_alloc();
    if(nullptr == m_pFilterFrame)
        m_pFilterFrame = av_frame_alloc();
    if(nullptr == m_pHwFrame)
        m_pHwFrame = av_frame_alloc();

    //加入中断处理
    m_nLastReadPacktTime = av_gettime();
    m_pAVFmtCxt->interrupt_callback.opaque = this;
    m_pAVFmtCxt->interrupt_callback.callback = [](void* ctx)
    {
        ctFFmpeg* pThis = (ctFFmpeg*)ctx;
        int nTimeout = 15;
        if (av_gettime() - pThis->m_nLastReadPacktTime > nTimeout * 1000 * 1000)
        {
            return -1;
        }
        return 0;
    };

    //打开码流
    MY_DEBUG << "Open Stream.";
    int nRet;
    if(m_nPlayWay == MEDIA_PLAY_FILE)
    {
        nRet = avformat_open_input(&m_pAVFmtCxt, sUrl.toStdString().c_str(), nullptr, nullptr);
        if(m_pAVFmtCxt)
        {
            m_FileTotalTimes = m_pAVFmtCxt->duration/1000000.0;//获取总时长,转化为秒
            MY_DEBUG << "m_FileTimes:" << m_FileTotalTimes;
            emit sig_showTotalTime(static_cast<int>(m_FileTotalTimes));
        }
    }
    else{
        nRet = avformat_open_input(&m_pAVFmtCxt, sUrl.toStdString().c_str(), nullptr, &pOptDict);
        if(nRet < 0)
        {
            MY_DEBUG << "avformat_open_input failed nRet:" << nRet;
            av_dict_free(&pOptDict);
            return nRet;
        }
    }

    av_dict_free(&pOptDict);

    //设置探测时间,获取码流信息
    MY_DEBUG << "Get Stream Info.";
    m_pAVFmtCxt->probesize = MAX_PROBE_SIZE * 15;
    m_pAVFmtCxt->max_analyze_duration = MAX_ANALYZE_DURATION * 15;
    nRet = avformat_find_stream_info(m_pAVFmtCxt, nullptr);
    if(nRet < 0)
    {
        MY_DEBUG << "avformat_find_stream_info failed nRet:" << nRet;
        return nRet;
    }

    //打印码流信息
    av_dump_format(m_pAVFmtCxt, 0, sUrl.toStdString().c_str(), 0);

    //查找码流
    MY_DEBUG << "av_find_best_stream.";
    m_nVideoIndex = av_find_best_stream(m_pAVFmtCxt, AVMEDIA_TYPE_VIDEO, -1, -1, &pvAVCodec, 0);
    if(m_nVideoIndex < 0)
    {
        MY_DEBUG << "av not find best stream";
        return -1;
    }
    m_nAudioIndex = av_find_best_stream(m_pAVFmtCxt, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

    if(nullptr == pvAVCodec)
    {
        MY_DEBUG << "nullptr == pvAVCodec error.";
        return -1;
    }

    //初始化视频
    MY_DEBUG << "Init Video.";
    if(InitVideo(pvAVCodec) < 0)
    {
        MY_DEBUG << "InitVideo() error";
        return -1;
    }

    MY_DEBUG << "Init ffmpeg version"<<av_version_info();
#if FILTER_ENABLE
    if(m_nPlayWay == MEDIA_PLAY_FILE)
    {
        m_bSupportFilter = InitOutFile();
        if(!m_bSupportFilter)
        {
            MY_DEBUG << "InitOutFile() error.";
        }
        else
            MY_DEBUG << "InitOutFile() success.";
    }
#endif

    //初始化音频
    if(m_nAudioIndex >= 0)
    {
        if(InitAudio() < 0)
        {
            MY_DEBUG << "InitAudio() error";
            m_bSupportAudioPlay = false;
        }
        else
            m_bSupportAudioPlay = true;
    }

    m_nShowWidgetW = 0;
    m_nShowWidgetH = 0;

    if(m_nDecodeType == MEDIA_DECODE_HARD)
    {
        if(m_bSupportHw)
        {
            if(nullptr != m_pHwDevCtx)
                av_buffer_unref(&m_pHwDevCtx);
        }
    }
    else
    {
        m_bSupportPush = initPushStream();
    }

    MY_DEBUG << "ctFFmpeg::Init sUrl:" << sUrl << " success.";

    return 0;

三、实现效果如下所示

1、可以实现多路视频播放

播放三路H265(2688*1520)所占用的cpu和内存如下

2、支持H264和H265的视频抓拍功能,图片保存在本地

四、工程代码地址

https://github.com/streamingServer/webplayer.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值