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/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;

}

相关推荐
小小码农Come on1 小时前
Qt Creator常用设置
qt
wkm9563 小时前
在arm64 ubuntu系统安装Qt后编译时找不到Qt3DExtras头文件
开发语言·arm开发·qt
小小码农Come on5 小时前
QT开发环境安装
开发语言·qt
小小码农Come on5 小时前
QT内存管理
开发语言·qt
有理想的打工人6 小时前
QT的安装
qt
来鸟 鸣间6 小时前
linux下ffmpeg源码编译
linux·运维·ffmpeg
Echo_NGC22377 小时前
【FFmpeg使用指南】Part 2:滤镜图架构与信号处理
架构·ffmpeg·音视频·信号处理
SilentSlot7 小时前
【QT-QML】8. 输入元素
qt·qml
是店小二呀7 小时前
Visual Studio C++ 工程架构深度解析:从 .vcxproj 到 Qt MOC 的文件管理实录
c++·qt·visual studio
枫叶丹47 小时前
【Qt开发】Qt系统(十二)-> Qt视频
c语言·开发语言·c++·qt·音视频