2 Star 0 Fork 0

顾金龙/LPMSQtUi

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
videortmpobject.cpp 6.83 KB
一键复制 编辑 原始数据 按行查看 历史
顾金龙 提交于 2024-03-22 17:41 . 添加打开nginx服务器的代码
#include "videortmpobject.h"
#include <QDebug>
#include <QImage>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libswresample/swresample.h"
#include "libavutil/imgutils.h"
}
VideoRtmpObject::VideoRtmpObject(QObject *parent)
: QThread{parent}
{
this->runFlag = true;
}
void VideoRtmpObject::run()
{
this->runFlag = true;
qDebug()<<"will start the rtmp pull thread";
this->ffmpeg_rtmp_client();
}
int VideoRtmpObject::ffmpeg_rtmp_client()
{
int video_width = 0;
int video_height = 0;
unsigned char *yuv420p_data;
unsigned char *rgb24_data;
// Allocate an AVFormatContext
AVFormatContext *format_ctx = avformat_alloc_context();
AVCodecContext *codecContext;
AVCodec *video_pCodec;
// 打开rtsp:打开输入流并读取标题。 编解码器未打开
std::string str = this->rtmpUrl.toStdString();
const char *url = str.c_str();
qDebug() << "拉流地址:" << url;
int ret = -1;
ret = avformat_open_input(&format_ctx, url, nullptr, nullptr);
if (ret != 0)
{
qDebug() << "无法打开网址: " << url << " return value:" << ret;
return -1;
}
// 读取媒体文件的数据包以获取流信息
ret = avformat_find_stream_info(format_ctx, nullptr);
if (ret < 0)
{
qDebug() << "无法获取流信息:" << ret;
return -1;
}
// video stream index
int video_stream_index = -1;
qDebug() << "视频中流的数量:" << format_ctx->nb_streams;
for (int i = 0; i < format_ctx->nb_streams; ++i)
{
const AVStream *stream = format_ctx->streams[i];
qDebug() << "编码数据的类型:" << stream->codecpar->codec_id;
if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
// 判断视频流是否是H264格式
if (stream->codecpar->codec_id != AV_CODEC_ID_H264)
{
qDebug() << "当前视频编码格式暂时不支持. 目前只支持:H264";
return 0;
}
// 查找解码器
video_pCodec = (AVCodec *)avcodec_find_decoder(AV_CODEC_ID_H264);
// 打开解码器
codecContext = avcodec_alloc_context3(video_pCodec);
if (!codecContext)
{
// 处理错误
}
// 将 AVCodecParameters 设置到 AVCodecContext 中
if (avcodec_parameters_to_context(codecContext, stream->codecpar) < 0)
{
// 处理错误
}
// 打开编解码器
int err = avcodec_open2(codecContext, video_pCodec, NULL);
if (err != 0)
{
qDebug() << "H264解码器打开失败";
return 0;
}
video_stream_index = i;
// 得到视频帧的宽高
video_width = stream->codecpar->width;
video_height = stream->codecpar->height;
qDebug() << "视频帧的尺寸(以像素为单位): (宽X高)" << stream->codecpar->width << "X" << stream->codecpar->height << QString("像素格式:") << stream->codecpar->format;
}
}
if (video_stream_index == -1)
{
qDebug() << "没有检测到视频流.";
return -1;
}
// 初始化解码相关的参数
AVFrame *yuv420p_pFrame = nullptr;
yuv420p_pFrame = av_frame_alloc(); // 存放解码后YUV数据的缓冲区
// 打开转码器
struct SwrContext *convert_ctx = swr_alloc();
// 初始化转码器
swr_init(convert_ctx);
// 申请存放yuv420p数据的空间
yuv420p_data = new unsigned char[video_width * video_height * 3 / 2];
// 申请存放rgb24数据的空间
rgb24_data = new unsigned char[video_width * video_height * 3];
int y_size = video_width * video_height;
AVPacket pkt;
int re;
while (this->runFlag)
{
// 读取一帧数据
ret = av_read_frame(format_ctx, &pkt);
if (ret < 0)
{
usleep(10);
continue;
}
// 得到视频包
if (pkt.stream_index == video_stream_index)
{
// 解码视频 frame
re = avcodec_send_packet(codecContext, &pkt); // 发送视频帧
if (re != 0)
{
av_packet_unref(&pkt); // 不成功就释放这个pkt
usleep(10);
continue;
}
re = avcodec_receive_frame(codecContext, yuv420p_pFrame); // 接受后对视频帧进行解码
if (re != 0)
{
av_packet_unref(&pkt); // 不成功就释放这个pkt
usleep(10);
continue;
}
// 将YUV数据拷贝到缓冲区
memcpy(yuv420p_data, (const void *)yuv420p_pFrame->data[0], y_size);
memcpy(yuv420p_data + y_size, (const void *)yuv420p_pFrame->data[1], y_size / 4);
memcpy(yuv420p_data + y_size + y_size / 4, (const void *)yuv420p_pFrame->data[2], y_size / 4);
// 将yuv420p转为RGB24格式
YUV420P_to_RGB24(yuv420p_data, rgb24_data, video_width, video_height);
// 加载图片数据
QImage image(rgb24_data, video_width, video_height, QImage::Format_RGB888);
emit sendImageToUI(image); // 发送信号
usleep(10);
}
// 释放pkt
av_packet_unref(&pkt);
}
qDebug()<<"thread stop,will close the pull client";
if(format_ctx)
{
avformat_close_input(&format_ctx); // 释放解封装器的空间,以防空间被快速消耗完
avformat_free_context(format_ctx);
format_ctx = nullptr;
}
if(codecContext)
{
avcodec_free_context(&codecContext); // 最后记得释放资源
codecContext = nullptr;
}
return 0;
}
// 图像颜色转换
void VideoRtmpObject::YUV420P_to_RGB24(unsigned char *data, unsigned char *rgb, int width, int height)
{
int index = 0;
unsigned char *ybase = data;
unsigned char *ubase = &data[width * height];
unsigned char *vbase = &data[width * height * 5 / 4];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// YYYYYYYYUUVV
unsigned char Y = ybase[x + y * width];
unsigned char U = ubase[y / 2 * width / 2 + (x / 2)];
unsigned char V = vbase[y / 2 * width / 2 + (x / 2)];
rgb[index++] = Y + 1.402 * (V - 128); // R
rgb[index++] = Y - 0.34413 * (U - 128) - 0.71414 * (V - 128); // G
rgb[index++] = Y + 1.772 * (U - 128); // B
}
}
}
void VideoRtmpObject::getRtmpUrlHandle(QString url)
{
this->rtmpUrl = url;
qDebug()<<"get url: "<<url;
}
void VideoRtmpObject::stop()
{
this->runFlag = false;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/gu_jin_long/lpmsqt-ui.git
git@gitee.com:gu_jin_long/lpmsqt-ui.git
gu_jin_long
lpmsqt-ui
LPMSQtUi
master

搜索帮助

D67c1975 1850385 1daf7b77 1850385