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;

}

相关推荐
码农客栈2 小时前
qt QNetworkRequest详解
qt
Say-hai3 小时前
QT6 + CMAKE编译OPENCV3.9
qt·opencv
威桑6 小时前
关于QLineEdit 添加的QAction图标的交互问题
qt
island131414 小时前
【QT】 控件 -- 显示类
开发语言·数据库·qt
FancySuMMer1114 小时前
关于av_get_channel_layout_nb_channels函数
qt·ffmpeg
weixin_4043702914 小时前
ffmpeg的AVOption用法
ffmpeg
行十万里人生16 小时前
Qt 控件与布局管理
数据库·qt·microsoft·华为od·华为·华为云·harmonyos
XuanRanDev17 小时前
【音视频处理】FFmpeg for Windows 安装教程
windows·ffmpeg·音视频
daqinzl17 小时前
Qt调用FFmpeg库实时播放UDP组播视频流
qt·ffmpeg·udp组播流