Qt调用FFmpeg库实时播放UDP组播视频流

基于以下参考链接,通过改进实现实时播放UDP组播视频流

https://blog.csdn.net/u012532263/article/details/102736700

源码在windows(qt-opensource-windows-x86-5.12.9.exe)、ubuntu20.04.6(x64)(qt-opensource-linux-x64-5.12.12.run)、以及针对arm64的ubuntu20.04.6(x64)交叉编译环境下编译成功(QT5.12.8, 5.15.13), 可执行程序在windows,ubuntu(x64)、arm64上均可运行。

工程代码见:

https://download.csdn.net/download/daqinzl/90315016

主要代码
videoplayer.cpp

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

#include <stdio.h>
#include<iostream>
using namespace std;
VideoPlayer::VideoPlayer()
{

}

VideoPlayer::~VideoPlayer()
{

}

void VideoPlayer::startPlay()
{
    ///调用 QThread 的start函数 将会自动执行下面的run函数 run函数是一个新的线程
    this->start();

}

void VideoPlayer::run()
{
    /*
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame;
    AVFrame *pFrameRGB;
    AVPacket *packet;
    uint8_t *out_buffer;

    static struct SwsContext *img_convert_ctx;

    int videoStream, i, numBytes;
    int ret, got_picture;

    avformat_network_init();
    av_register_all();


    //Allocate an AVFormatContext.
    pFormatCtx = avformat_alloc_context();

    // ffmpeg取rtsp流时av_read_frame阻塞的解决办法 设置参数优化
    AVDictionary* avdic = NULL;
    //rtsp
    //av_dict_set(&avdic, "buffer_size", "102400", 0); //设置缓存大小,1080p可将值调大
    //av_dict_set(&avdic, "rtsp_transport", "udp", 0); //以udp方式打开,如果以tcp方式打开将udp替换为tcp

    //rtmp
    //av_dict_set(&avdic, "buffer_size", "8192000", 0); //设置缓存大小,1080p可将值调大
    //av_dict_set(&avdic, "rtsp_transport", "tcp", 0); //以udp方式打开,如果以tcp方式打开将udp替换为tcp

    //udp
    av_dict_set(&avdic, "buffer_size", "8192000", 0); //设置缓存大小,1080p可将值调大
    av_dict_set(&avdic, "rtsp_transport", "udp", 0); //以udp方式打开,如果以tcp方式打开将udp替换为tcp
    //av_dict_set(&avdic, "fflags", "nobuffer", 0); // 设置实时选项
    //av_dict_set(&avdic, "flags", "low_delay", 0); // 设置低延迟选项
    av_dict_set(&avdic, "max_interleave_delta", "40000", 0);


    //av_dict_set(&avdic, "stimeout", "2000000", 0);   //设置超时断开连接时间,单位微秒
    //av_dict_set(&avdic, "max_delay", "500000", 0);   //设置最大时延

    ///rtsp地址,可根据实际情况修改
    //char url[]="rtsp://admin:admin@192.168.1.18:554/h264/ch1/main/av_stream";
    //char url[]="rtsp://192.168.17.112/test2.264";
    //char url[]="rtsp://admin:admin@192.168.43.1/stream/main";
    //char url[]="rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp";

    char url[]="udp://224.1.1.1:5001";

    //char url[]="rtmp://192.168.1.100:1935/live/desktop";

    if (avformat_open_input(&pFormatCtx, url, NULL, &avdic) != 0) {
        qDebug("can't open the file. \n");
        return;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        qDebug("Could't find stream infomation.\n");
        return;
    }

    videoStream = -1;

    ///循环查找视频中包含的流信息,直到找到视频类型的流
    ///便将其记录下来 保存到videoStream变量中
    ///这里我们现在只处理视频流  音频流先不管他
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    ///如果videoStream为-1 说明没有找到视频流
    if (videoStream == -1) {
        qDebug("Didn't find a video stream.\n");
        return;
    }

    ///查找解码器
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    ///2017.8.9---lizhen
    //pCodecCtx->bit_rate = 0;   //初始化为0
    //pCodecCtx->time_base.num = 1;  //下面两行:一秒钟25帧
    //pCodecCtx->time_base.den = 10;
    //pCodecCtx->frame_number = 1;  //每包一个视频帧

    if (pCodec == NULL) {
        qDebug("Codec not found.\n");
        return;
    }

    ///打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        qDebug("Could not open codec.\n");
        return;
    }

    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();

    ///这里我们改成了 将解码后的YUV数据转换成RGB32
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
            pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
            AV_PIX_FMT_RGB32, SWS_FAST_BILINEAR, NULL, NULL, NULL);

    numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);

    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB32,
            pCodecCtx->width, pCodecCtx->height);

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
    av_new_packet(packet, y_size); //分配packet的数据
    //*/

    //bool CanRun = true;

    //ffmpeg 初始化
    // 初始化注册ffmpeg相关的编码器
    av_register_all();
    avcodec_register_all();
    avformat_network_init();

    //qDebug()<<"1 FFmpeg version info: {  av_version_info() }";
    qDebug()<<"2 FFmpeg version info: { " << av_version_info() << "}";
    qDebug()<<"3 FFmpeg version info:";
    qDebug()<< av_version_info() ;

    char url[]="udp://224.1.1.1:5001";

    // ffmpeg 转码
    // 分配音视频格式上下文
    AVFormatContext *pFormatCtx = avformat_alloc_context();

    AVDictionary* avdic = NULL;
    av_dict_set(&avdic, "buffer_size", "8192000", 0);
    av_dict_set(&avdic, "max_interleave_delta", "40000", 0);

    av_dict_set(&avdic, "analyzeduration", "100000000", 0);
    av_dict_set(&avdic, "probesize", "100000000", 0);

    int ret;
    //打开流
    ret = avformat_open_input(&pFormatCtx, url, NULL, &avdic);
    if (ret != 0){ qDebug("can't open the url. \n"); return; }

    // 读取媒体流信息
    ret = avformat_find_stream_info(pFormatCtx, NULL);
    if (ret != 0) { qDebug("Could't find stream infomation.\n"); return; }

    // 这里只是为了打印些视频参数
    AVDictionaryEntry* tag = NULL;
    while ((tag = av_dict_get(pFormatCtx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)) != NULL)
    {
        char * key = tag->key;
        char * value = tag->value;
        qDebug()<< *key  << *value << "\n";
    }

    // 从格式化上下文获取流索引
    AVStream* pStream = NULL;
    //AVStream* aStream = NULL;
    int videoStream = -1;
    for (int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            pStream = pFormatCtx->streams[i];
            videoStream = i;
        }
        //else if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
        //{
        //    aStream = pFormatCtx->streams[i];
        //}
    }
    if (pStream == NULL) { qDebug("Didn't find a video stream.\n"); return; }

    // 获取流的编码器上下文
    AVCodecContext codecContext = *pStream->codec;

    qDebug() << "codec name: {" << avcodec_get_name(codecContext.codec_id) << "}\n";
    // 获取图像的宽、高及像素格式
    int width = codecContext.width;
    int height = codecContext.height;
    AVPixelFormat sourcePixFmt = codecContext.pix_fmt;

    // 得到编码器ID
    AVCodecID codecId = codecContext.codec_id;
    // 目标像素格式
    AVPixelFormat destinationPixFmt = AV_PIX_FMT_RGB32;

    // 某些264格式codecContext.pix_fmt获取到的格式是AV_PIX_FMT_NONE 统一都认为是YUV420P
    if (sourcePixFmt == AV_PIX_FMT_NONE && codecId == AV_CODEC_ID_MPEG2TS)
    {
        sourcePixFmt = AV_PIX_FMT_YUV420P;
    }

    static struct SwsContext *img_convert_ctx;

    // 得到SwsContext对象:用于图像的缩放和转换操作
    img_convert_ctx = sws_getContext(width, height, sourcePixFmt,
        width, height, destinationPixFmt,
        SWS_FAST_BILINEAR, NULL, NULL, NULL);
    if (img_convert_ctx == NULL) { qDebug() << "Could not initialize the conversion context.\n" ; return; }

    //分配一个默认的帧对象:AVFrame
    //AVFrame* pConvertedFrame = av_frame_alloc();
    // 目标媒体格式需要的字节长度
    //numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
    int numBytes = avpicture_get_size(destinationPixFmt, width, height);
    // 分配目标媒体格式内存使用
    //out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    uint8_t * out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    //var dstData = new byte_ptrArray4();
    //var dstLinesize = new int_array4();
    AVFrame *pFrameRGB = av_frame_alloc();
    // 设置图像填充参数
    //av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, convertedFrameBufferPtr, destinationPixFmt, width, height, 1);
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, destinationPixFmt,width, height);

    // ffmpeg 解码
    // 根据编码器ID获取对应的解码器
    AVCodec* pCodec = avcodec_find_decoder(codecId);
    if (pCodec == NULL) { qDebug() << "Unsupported codec.\n"; }

    AVCodecContext* pCodecCtx = &codecContext;

    if ((pCodec->capabilities & AV_CODEC_CAP_TRUNCATED) == AV_CODEC_CAP_TRUNCATED)
        pCodecCtx->flags |= AV_CODEC_FLAG_TRUNCATED;

    // 通过解码器打开解码器上下文:AVCodecContext pCodecContext
    ret = avcodec_open2(pCodecCtx, pCodec, NULL);
    if (ret < 0) { qDebug() << ret << "\n"; return; }

    // 分配解码帧对象:AVFrame pDecodedFrame
    AVFrame* pFrame = av_frame_alloc();

    // 初始化媒体数据包
    AVPacket* packet = new AVPacket();
    AVPacket** pPacket = &packet;
    av_init_packet(packet);

    while (1)
    {
        /*
        if (av_read_frame(pFormatCtx, packet) < 0)
        {
            qDebug() << "av_read_frame < 0";
            break; //这里认为视频读取完了
        }

        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);

            if (ret < 0) {
                qDebug("decode error.\n");
                return;
            }

            if (got_picture) {
                sws_scale(img_convert_ctx,
                        (uint8_t const * const *) pFrame->data,
                        pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                        pFrameRGB->linesize);

                //把这个RGB数据 用QImage加载
                QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
                QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
                emit sig_GetOneFrame(image);  //发送信号
                //emit sig_GetOneFrame(tmpImg);


                //提取出图像中的R数据
                //for(int i=0;i<pCodecCtx->width;i++)
                //{
                //    for(int j=0;j<pCodecCtx->height;j++)
                //    {
                //        QRgb rgb=image.pixel(i,j);
                //        int r=qRed(rgb);
                //        image.setPixel(i,j,qRgb(r,0,0));
                //    }
                //}
                //emit sig_GetRFrame(image);


            }else qDebug() << "got_picture < 0";
        }else qDebug() << "packet->stream_index not video stream";
        av_free_packet(packet); //释放资源,否则内存会一直上升
        //msleep(0.02); //停一停  不然放的太快了
        //*/

        //*
        try
        {
            do
            {
                // 读取一帧未解码数据
                ret = av_read_frame(pFormatCtx, packet);
               // Console.WriteLine(pPacket->dts);
                if (ret == AVERROR_EOF) break;
                if (ret < 0) { qDebug() << "got error "; return; }

                if (packet->stream_index != videoStream) continue;

                // 解码
                ret = avcodec_send_packet(pCodecCtx, packet);
                if (ret < 0) { qDebug() << "got error 2 "; return; }
                // 解码输出解码数据
                ret = avcodec_receive_frame(pCodecCtx, pFrame);
            } while (ret == AVERROR(EAGAIN) && 1);
            if (ret == AVERROR_EOF) break;
            if (ret < 0) { qDebug() << "got error 3 "; return; }

            if (packet->stream_index != videoStream) continue;

            //Console.WriteLine($@"frame: {frameNumber}");
            // YUV->RGB
            sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

            //把这个RGB数据 用QImage加载
            QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
            QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
            emit sig_GetOneFrame(image);  //发送信号
            //emit sig_GetOneFrame(tmpImg);

        }  catch (exception e) {

        }
        {
            av_packet_unref(packet);//释放数据包对象引用
            av_frame_unref(pFrame);//释放解码帧对象引用
        }
        //*/
    }
    av_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10871.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

three.js+WebGL踩坑经验合集(6.2):负缩放,负定矩阵和行列式的关系(3D版本)

本篇将紧接上篇的2D版本对3D版的负缩放矩阵进行解读。 (6.1):负缩放&#xff0c;负定矩阵和行列式的关系&#xff08;2D版本&#xff09; 既然three.js对3D版的负缩放也使用行列式进行判断&#xff0c;那么&#xff0c;2D版的结论用到3D上其实是没毛病的&#xff0c;THREE.Li…

反向代理模块jmh

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当…

软件工程经济学-日常作业+大作业

目录 一、作业1 作业内容 解答 二、作业2 作业内容 解答 三、作业3 作业内容 解答 四、大作业 作业内容 解答 1.建立层次结构模型 (1)目标层 (2)准则层 (3)方案层 2.构造判断矩阵 (1)准则层判断矩阵 (2)方案层判断矩阵 3.层次单排序及其一致性检验 代码 …

【回溯】目标和 字母大小全排列

文章目录 494. 目标和解题思路&#xff1a;回溯784. 字母大小写全排列解题思路&#xff1a;回溯 494. 目标和 494. 目标和 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式…

告别复杂,拥抱简洁:用plusDays(7)代替plus(7, ChronoUnit.DAYS)

前言 你知道吗?有时候代码里的一些小细节看起来很简单,却可能成为你调试时的大麻烦。在 Java 中,我们用 LocalDateTime 进行日期和时间的操作时,发现一个小小的替代方法可以让代码更简洁,功能更强大。这不,今天我们就来探讨如何用 LocalDateTime.now().plusDays(7) 替代…

《苍穹外卖》项目学习记录-Day10订单状态定时处理

利用Cron表达式生成器生成Cron表达式 1.处理超时订单 查询订单表把超时的订单查询出来&#xff0c;也就是订单的状态为待付款&#xff0c;下单的时间已经超过了15分钟。 //select * from orders where status ? and order_time < (当前时间 - 15分钟) 遍历集合把数据库…

【深度分析】微软全球裁员计划不影响印度地区,将继续增加当地就业机会

当微软的裁员刀锋掠过全球办公室时&#xff0c;班加罗尔的键盘声却愈发密集——这场资本迁徙背后&#xff0c;藏着数字殖民时代最锋利的生存法则。 表面是跨国公司的区域战略调整&#xff0c;实则是全球人才市场的地壳运动。微软一边在硅谷裁撤年薪20万美金的高级工程师&#x…

Linux中 端口被占用如何解决

lsof命令查找 查找被占用端口 lsof -i :端口号 #示例 lsof -i :8080 lsof -i :3306 netstat命令查找 查找被占用端口 netstat -tuln | grep 端口号 #示例 netstat -tuln | grep 3306 netstat -tuln | grep 6379 ss命令查找 查找被占用端口 ss -tunlp | grep 端口号 #示例…

qt-Quick3D笔记之官方例程Runtimeloader Example运行笔记

qt-Quick3D笔记之官方例程Runtimeloader Example运行笔记 文章目录 qt-Quick3D笔记之官方例程Runtimeloader Example运行笔记1.例程运行效果2.例程缩略图3.项目文件列表4.main.qml5.main.cpp6.CMakeLists.txt 1.例程运行效果 运行该项目需要自己准备一个模型文件 2.例程缩略图…

高性能消息队列Disruptor

定义一个事件模型 之后创建一个java类来使用这个数据模型。 /* <h1>事件模型工程类&#xff0c;用于生产事件消息</h1> */ no usages public class EventMessageFactory implements EventFactory<EventMessage> { Overridepublic EventMessage newInstance(…

Spring Boot项目如何使用MyBatis实现分页查询

写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油&#xff0c;冲鸭&#x…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.27 线性代数王国:矩阵分解实战指南

1.27 线性代数王国&#xff1a;矩阵分解实战指南 #mermaid-svg-JWrp2JAP9qkdS2A7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JWrp2JAP9qkdS2A7 .error-icon{fill:#552222;}#mermaid-svg-JWrp2JAP9qkdS2A7 .erro…

EasyExcel使用详解

文章目录 EasyExcel使用详解一、引言二、环境准备与基础配置1、添加依赖2、定义实体类 三、Excel 读取详解1、基础读取2、自定义监听器3、多 Sheet 处理 四、Excel 写入详解1、基础写入2、动态列与复杂表头3、样式与模板填充 五、总结 EasyExcel使用详解 一、引言 EasyExcel 是…

FIDL:Flutter与原生通讯的新姿势,不局限于基础数据类型

void initUser(User user); } 2、执行命令./gradlew assembleDebug&#xff0c;生成IUserServiceStub类和fidl.json文件 3、打开通道&#xff0c;向Flutter公开方法 FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() { Override void…

DIFY源码解析

偶然发现Github上某位大佬开源的DIFY源码注释和解析&#xff0c;目前还处于陆续不断更新地更新过程中&#xff0c;为大佬的专业和开源贡献精神点赞。先收藏链接&#xff0c;后续慢慢学习。 相关链接如下&#xff1a; DIFY源码解析

87.(3)攻防世界 web simple_php

之前做过&#xff0c;回顾 12&#xff0c;攻防世界simple_php-CSDN博客 进入靶场 <?php // 显示当前 PHP 文件的源代码&#xff0c;方便调试或查看代码结构 // __FILE__ 是 PHP 的一个魔术常量&#xff0c;代表当前文件的完整路径和文件名 show_source(__FILE__);// 包含…

x86-64数据传输指令

关于汇编语言一些基础概念的更详细的介绍&#xff0c;可移步MIPS指令集&#xff08;一&#xff09;基本操作_mips指令 sw-CSDN博客 该指令集中一个字2字节。 该架构有16个64位寄存器&#xff0c;名字都以%r开头&#xff0c;每个寄存器的最低位字节&#xff0c;低1~2位字节&…

网络工程师 (8)存储管理

一、页式存储基本原理 &#xff08;一&#xff09;内存划分 页式存储首先将内存物理空间划分成大小相等的存储块&#xff0c;这些块通常被称为“页帧”或“物理页”。每个页帧的大小是固定的&#xff0c;例如常见的页帧大小有4KB、8KB等&#xff0c;这个大小由操作系统决定。同…

全程Kali linux---CTFshow misc入门(25-37)

第二十五题&#xff1a; 提示&#xff1a;flag在图片下面。 直接检查CRC&#xff0c;检测到错误&#xff0c;就直接暴力破解。 暴力破解CRC的python代码。 import binascii import struct def brute_force_ihdr_crc(filename): # 读取文件二进制数据 with open(filen…

MySQL数据库(二)- SQL

目录 ​编辑 一 DDL (一 数据库操作 1 查询-数据库&#xff08;所有/当前&#xff09; 2 创建-数据库 3 删除-数据库 4 使用-数据库 (二 表操作 1 创建-表结构 2 查询-所有表结构名称 3 查询-表结构内容 4 查询-建表语句 5 添加-字段名数据类型 6 修改-字段数据类…