Qt调用ffmpeg库录屏并进行UDP组播推流

news2025/1/27 10:16:50

基于以下参考链接,采用其界面和程序框架,实现实时推送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/90316849

主要代码
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 "libavdevice/avdevice.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* m_fmt_ctx = NULL;
    AVInputFormat* m_input_fmt = NULL;
    int video_stream = -1;
    avdevice_register_all();
    avcodec_register_all();
    //const char* deviceName =  "desktop";
    //const char* inputformat = "gdigrab";

    const char* deviceName =  ":0.0+0,00";
    const char* inputformat = "x11grab";

    int FPS = 23;  //15
    m_fmt_ctx = avformat_alloc_context();
    m_input_fmt = av_find_input_format(inputformat);
    AVDictionary* deoptions = NULL;
    av_dict_set_int(&deoptions, "framerate", FPS, AV_DICT_MATCH_CASE);
    av_dict_set_int(&deoptions, "rtbufsize", 3041280 * 100 * 5, 0);

    av_dict_set(&deoptions,"video_size","1920*1080",0); //

    //如果不设置的话,在输入源是直播流的时候,会花屏。单位bytes
    //av_dict_set(&deoptions, "buffer_size", "10485760", 0);
    //av_dict_set(&deoptions, "reuse", "1", 0);

    int ret = avformat_open_input(&m_fmt_ctx, deviceName, m_input_fmt, &deoptions);
    if (ret != 0) {
        qDebug() << "1. " << ret << "\n"; return;
    }
    av_dict_free(&deoptions);
    ret = avformat_find_stream_info(m_fmt_ctx, NULL);
    if (ret < 0) {
        qDebug() << "2. " << ret << "\n"; return;
    }
    av_dump_format(m_fmt_ctx, 0, deviceName, 0);
    video_stream = av_find_best_stream(m_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (video_stream < 0) {
        qDebug() << "3. " << -1 << "\n"; return;
    }

    AVCodecContext * _codec_ctx = m_fmt_ctx->streams[video_stream]->codec;
    AVCodec* _codec = avcodec_find_decoder(_codec_ctx->codec_id);
    if (_codec == NULL) {
        qDebug() << "4. " << -1 << "\n"; return;
    }
    ret = avcodec_open2(_codec_ctx, _codec, NULL);
    if (ret != 0) {
        qDebug() << "5. " << -1 << "\n"; return;
    }
    int width = m_fmt_ctx->streams[video_stream]->codec->width;
    int height = m_fmt_ctx->streams[video_stream]->codec->height;
    int fps = m_fmt_ctx->streams[video_stream]->codec->framerate.num > 0 ? m_fmt_ctx->streams[video_stream]->codec->framerate.num : 25;
    AVPixelFormat videoType = m_fmt_ctx->streams[video_stream]->codec->pix_fmt;
    std::cout << "avstream timebase : " << m_fmt_ctx->streams[video_stream]->time_base.num << " / " << m_fmt_ctx->streams[video_stream]->time_base.den << endl;
        AVDictionary* enoptions = 0;
    //av_dict_set(&enoptions, "preset", "superfast", 0);
    //av_dict_set(&enoptions, "tune", "zerolatency", 0);
    av_dict_set(&enoptions, "preset", "ultrafast", 0);
    av_dict_set(&enoptions, "tune", "zerolatency", 0);

    //TODO
    //av_dict_set(&enoptions, "pkt_size", "1316", 0);    //Maximum UDP packet size
    av_dict_set(&dic, "fifo_size", "18800", 0);
    av_dict_set(&enoptions, "buffer_size", "0", 1);
    av_dict_set(&dic, "bitrate", "11000000", 0);
    av_dict_set(&dic, "buffer_size", "1000000", 0);//1316
    //av_dict_set(&enoptions, "reuse", "1", 0);

    AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec)
    {
        std::cout << "avcodec_find_encoder failed!" << endl;
        qDebug() << "6. " << "NULL" << "\n"; return;
    }
    AVCodecContext* vc = avcodec_alloc_context3(codec);
    if (!vc)
    {
        std::cout << "avcodec_alloc_context3 failed!" << endl;
        qDebug() << "7. " << "NULL" << "\n"; return;
    }
    std::cout << "avcodec_alloc_context3 success!" << endl;
    vc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    vc->codec_id = AV_CODEC_ID_H264;
    vc->codec_type = AVMEDIA_TYPE_VIDEO;
    vc->pix_fmt = AV_PIX_FMT_YUV420P;
    vc->width = width;
    vc->height = height;
    vc->time_base.num = 1;
    vc->time_base.den = FPS;
    vc->framerate = { FPS,1 };
    vc->bit_rate = 10241000;
    vc->gop_size = 120;
    vc->qmin = 10;
    vc->qmax = 51;
    vc->max_b_frames = 0;
    vc->profile = FF_PROFILE_H264_MAIN;
    ret = avcodec_open2(vc, codec, &enoptions);
    if (ret != 0)
    {
        qDebug() << "8. " << ret << "\n"; return;
    }
    std::cout << "avcodec_open2 success!" << endl;
    av_dict_free(&enoptions);
    SwsContext *vsc = nullptr;
    vsc = sws_getCachedContext(vsc,
        width, height, (AVPixelFormat)videoType, //源宽、高、像素格式
        width, height, AV_PIX_FMT_YUV420P,//目标宽、高、像素格式
        SWS_BICUBIC, // 尺寸变化使用算法
        0, 0, 0
    );
    if (!vsc)
    {
        cout << "sws_getCachedContext failed!";
        qDebug() << "9. " << "false" << "\n"; return;
    }
    AVFrame* yuv = av_frame_alloc();
    yuv->format = AV_PIX_FMT_YUV420P;
    yuv->width = width;
    yuv->height = height;
    yuv->pts = 0;
    ret = av_frame_get_buffer(yuv, 32);
    if (ret != 0)
    {
        qDebug() << "10. " << ret << "\n"; return;
    }
    //const char* rtmpurl = "rtmp://192.168.0.105:1935/live/desktop";
    //const char* rtmpurl = "rtmp://172.17.53.33:1935/live/desktop";
    const char* rtmpurl = "udp://224.1.1.1:5001";
    AVFormatContext * ic = NULL;
    //ret = avformat_alloc_output_context2(&ic, 0, "flv", rtmpurl);
    ret = avformat_alloc_output_context2(&ic, NULL, "mpegts", rtmpurl);//UDP
    if (ret < 0)
    {
        qDebug() << "11. " << ret << "\n"; return;
    }
    AVStream* st = avformat_new_stream(ic, NULL);
    if (!st)
    {
        qDebug() << "12. " << -1 << "\n"; return;
    }
    st->codecpar->codec_tag = 0;
    avcodec_parameters_from_context(st->codecpar, vc);
    av_dump_format(ic, 0, rtmpurl, 1);
    ret = avio_open(&ic->pb, rtmpurl, AVIO_FLAG_WRITE);
    if (ret != 0)
    {
        qDebug() << "13. " << ret << "\n"; return;
    }
    ret = avformat_write_header(ic, NULL);
    if (ret != 0)
    {
        qDebug() << "14. " << ret << "\n"; return;
    }
    AVPacket* packet = av_packet_alloc();
    AVPacket* Encodepacket = av_packet_alloc();
    int frameIndex = 0;
    int EncodeIndex = 0;
    AVFrame* rgb = av_frame_alloc();
    AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
    long long startpts = m_fmt_ctx->start_time;
    long long lastpts = 0;
    long long duration = av_rescale_q(1, { 1,FPS }, { 1,AV_TIME_BASE });
    int got_picture = 0;
    while (frameIndex < 2000000)
    {
        ret = av_read_frame(m_fmt_ctx, packet);
        if (ret < 0) {
            break;
        }
        if (packet->stream_index == video_stream)
        {
            ret = avcodec_decode_video2(_codec_ctx, rgb, &got_picture, packet);
            if (ret < 0) {
                printf("Decode Error.\n");
                qDebug() << "15. " << ret << "\n"; return;
            }
            if (got_picture) {
                int h = sws_scale(vsc, rgb->data, rgb->linesize, 0, height, //源数据
                yuv->data, yuv->linesize);
                int guesspts = frameIndex * duration;
                yuv->pts = guesspts;
                frameIndex++;
                ret = avcodec_encode_video2(vc, Encodepacket, yuv, &got_picture);
                if (ret < 0) {
                    printf("Failed to encode!\n");
                    break;
                }
                if (got_picture == 1) {
                    Encodepacket->pts = av_rescale_q(EncodeIndex, vc->time_base, st->time_base);
                    Encodepacket->dts = Encodepacket->pts;
                    std::cout << "frameindex : " << EncodeIndex << " pts : " << Encodepacket->pts << " dts: " << Encodepacket->dts << " encodeSize:" << Encodepacket->size << " curtime - lasttime " << Encodepacket->pts - lastpts << endl;
                        lastpts = Encodepacket->pts;
                    ret = av_interleaved_write_frame(ic, Encodepacket);
                    EncodeIndex++;
                    av_packet_unref(Encodepacket);
                }
            }
        }
        av_packet_unref(packet);
    }
    ret = avcodec_send_frame(vc, NULL);
    while (ret >= 0) {
        ret = avcodec_receive_packet(vc, Encodepacket);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            break;
        }
        if (ret < 0) {
            break;
        }
        ret = av_interleaved_write_frame(ic, Encodepacket);
        EncodeIndex++;
    }
    av_write_trailer(ic);
    av_packet_free(&packet);
    av_packet_free(&Encodepacket);
    av_frame_free(&rgb);
    av_frame_free(&yuv);
    av_bitstream_filter_close(h264bsfc);
    h264bsfc = NULL;
    if (vsc){
        sws_freeContext(vsc);
        vsc = NULL;
    }
    if (_codec_ctx){
        avcodec_close(_codec_ctx);
    }
    _codec_ctx = NULL;
    _codec = NULL;
    if (vc){
        avcodec_free_context(&vc);
    }
    if (m_fmt_ctx){
        avformat_close_input(&m_fmt_ctx);
    }
    if (ic && !(ic->flags & AVFMT_NOFILE)){
        avio_closep(&ic->pb);
    }
    if (ic) {
        avformat_free_context(ic);
        ic = NULL;
    }
    m_input_fmt = NULL;

}
 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS:创建应用静态快捷方式

一、前言 静态快捷方式是一种在系统中创建的可以快速访问应用程序或特定功能的链接。它通常可以在长按应用图标&#xff0c;以图标和相应的文字出现在应用图标的上方&#xff0c;用户可以迅速启动对应应用程序的组件。使用快捷方式&#xff0c;可以提高效率&#xff0c;节省了查…

mysql 学习6 DQL语句,对数据库中的表进行 查询 操作

前期准备数据 重新create 一张表 create table emp(id int comment 编号,workno varchar(10) comment 工号,name varchar(10) comment 姓名,gender char comment 性别,ager tinyint unsigned comment 年龄,idcard char(18) comment 身份证号,workaddress varchar(10) c…

【ES实战】治理项之索引模板相关治理

索引模板治理 文章目录 索引模板治理问题现象分析思路操作步骤问题程序化方案索引与索引模板增加分片数校验管理 彩蛋如何查询Flink on Yarn 模式下的Task Manager日志相关配置查询已停止的Flink任务查询未停止的Flink任务 问题现象 在集群索引新建时&#xff0c;索引的分片比…

springboot3 集成 knife4j(接口文档)

提示&#xff1a;文章是集成 knife4j&#xff0c;而非 swagger2 或者 swagger3&#xff0c;效果如图 文章目录 前言一、添加依赖二、如何集成1.配置文件2.注解部分1.Tag2.Operation3.Parameter4.Schema 3.使用 总结 前言 提示&#xff1a;&#xff1a;大家在开发阶段&#xff…

51单片机开发:独立键盘实验

实验目的&#xff1a;按下键盘1时&#xff0c;点亮LED灯1。 键盘原理图如下图所示&#xff0c;可见&#xff0c;由于接GND&#xff0c;当键盘按下时&#xff0c;P3相应的端口为低电平。 键盘按下时会出现抖动&#xff0c;时间通常为5-10ms&#xff0c;代码中通过延时函数delay…

Flutter_学习记录_Tab的简单Demo~真的很简单

1. Tab的简单使用了解 要实现tab(选项卡或者标签视图)需要用到三个组件&#xff1a; TabBarTabBarViewTabController 这一块&#xff0c;我也不知道怎么整理了&#xff0c;直接提供代码吧&#xff1a; import package:flutter/material.dart;void main() {runApp(MyApp());…

GESP2024年3月认证C++六级( 第三部分编程题(1)游戏)

参考程序&#xff1a; #include <cstdio> using namespace std; const int N 2e5 5; const int mod 1e9 7; int n, a, b, c; int f[N << 1]; int ans; int main() {scanf("%d%d%d%d", &n, &a, &b, &c);f[N n] 1;for (int i n; i…

数据结构测试题2

一、单选题&#xff08;每题 2 分&#xff0c;共20分&#xff09; 1. 栈和队列的共同特点是( A )。 A.只允许在端点处插入和删除元素 B.都是先进后出 C.都是先进先出 D.没有共同点 2. 用链接方式存储的队列&#xff0c;在进行插入运算时( C ) A. 仅修改头指针 B. 头…

项目概述与规划 (I)

项目概述与规划 (I) JavaScript的学习已经接近尾声了&#xff0c;最后我们将通过一个项目来讲我们在JavaScript中学习到的所有都在这个项目中展现出来&#xff0c;这个项目的DEMO来自于Udemy中的课程&#xff0c;作者是Jonas Schmedtmann&#xff1b; 项目规划 项目步骤 用户…

【WebRTC - STUN/TURN服务 - COTURN配置】

在WebRTC中&#xff0c;对于通信的两端不在同一个局域网的情况下&#xff0c;通信两端往往无法P2P直接连接&#xff0c;需要一个TURN中继服务&#xff0c;而中继服务可以选用 COTURN 构建。 注&#xff1a;COTURN 是一个开源的 TURN&#xff08;Traversal Using Relays around…

【HuggingFace项目】:Open-R1 - DeepSeek-R1 大模型开源复现计划

项目链接&#xff1a;https://github.com/huggingface/open-r1 概述 Open-R1 是由 HuggingFace 发布的一个完全开放的项目&#xff0c;旨在通过三个主要步骤复现 DeepSeek-R1 的完整训练流程。这个项目的目标是让更多人能够理解和使用 DeepSeek-R1 的技术方案&#xff0c;从而…

On to OpenGL and 3D computer graphics

2. On to OpenGL and 3D computer graphics 声明&#xff1a;该代码来自&#xff1a;Computer Graphics Through OpenGL From Theory to Experiments&#xff0c;仅用作学习参考 2.1 First Program Square.cpp完整代码 /// // square.cpp // // OpenGL program to draw a squ…

【数据分享】2014-2025年我国水系数据(免费获取/全国/分省)

水系数据是我们在各项研究中经常使用的数据&#xff01;水系数据虽然很常用&#xff0c;但是却基本没有能下载最近年份水系数据的网站&#xff0c;所以很多人不知道如何获得水系数据。 本次我们给大家分享的是2014-2025年的全国范围的水系数据&#xff0c;包括水系线数据和水系…

学习数据结构(2)算法复杂度+顺序表

1.空间复杂度 &#xff08;1&#xff09;概念 空间复杂度也是一个数学表达式&#xff0c;表示一个算法在运行过程中根据算法的需要额外临时开辟的空间。 空间复杂度不是指程序占用了多少bytes的空间&#xff0c;因为常规情况每个对象大小差异不会很大&#xff0c;所以空间复杂…

MyBatis和JPA区别详解

文章目录 MyBatis和JPA区别详解一、引言二、设计理念与使用方式1、MyBatis&#xff1a;半自动化的ORM框架1.1、代码示例 2、JPA&#xff1a;全自动的ORM框架2.1、代码示例 三、性能优化与适用场景1、MyBatis&#xff1a;灵活的SQL控制1.1、适用场景 2、JPA&#xff1a;开发效率…

DeepSeek明确学术研究方向效果如何?

明确学术研究方向 在学术写作中&#xff0c;选择一个出色的研究主题至关重要&#xff0c;因为它直接关系到论文是否能登上高级别的学术期刊。不少学者在这个过程中走入了误区&#xff0c;他们往往将大把的时间花在写作本身&#xff0c;而忽略了对选题的深入思考&#xff0c;这…

牛客周赛round78 B,C

B.一起做很甜的梦 题意&#xff1a;就是输出n个数&#xff08;1-n&#xff09;&#xff0c;使输出的序列中任意选连续的小序列&#xff08;小序列长度>2&&<n-1&#xff09;不符合排列&#xff08;例如如果所选长度为2&#xff0c;在所有长度为2 的小序列里不能出…

基于语义-拓扑-度量表征引导的大语言模型推理的空中视觉语言导航

1. 摘要翻译及主要贡献点 摘要&#xff1a; 空中视觉语言导航&#xff08;VLN&#xff09;是一项新兴任务&#xff0c;它使无人机能够通过自然语言指令和视觉线索在户外环境中导航。由于户外空中场景中复杂的空间关系&#xff0c;这项任务仍然具有挑战性。本文提出了一种端到…

React 前端框架实战教程

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 引言 React 是由 Facebook 开发的前端 JavaScript 库&#xff0c;旨在构建高效、灵活的用户界面&#xff0c;尤其适用于单页应用…

c语言中的数组(上)

数组的概念 数组是⼀组相同类型元素的集合&#xff1b; 数组中存放的是1个或者多个数据&#xff0c;但是数组元素个数不能为0。 数组中存放的多个数据&#xff0c;类型是相同的。 数组分为⼀维数组和多维数组&#xff0c;多维数组⼀般⽐较多⻅的是⼆维数组。 数组创建 在C语言…