#include "ffmpeg.h"
// FFmpeg视频处理线程类的构造函数
FFmpegThread::FFmpegThread(QObject *parent) : QThread(parent)
{
// 设置对象名称,用于调试
setObjectName("FFmpegThread");
// 初始化线程控制标志
stopped = false;
isPlay = false;
// 初始化视频相关参数
frameFinish = false;
videoWidth = 0;
videoHeight = 0;
oldWidth = 0;
oldHeight = 0;
videoStreamIndex = -1;
audioStreamIndex = -1;
// 设置默认RTSP地址
url = "rtsp://admin:
[email protected]:554/ch1/main/av_stream";
// 初始化FFmpeg相关指针
buffer = NULL;
avPacket = NULL;
avFrame = NULL;
avFrame2 = NULL;
avFrame3 = NULL;
avFormatContext = NULL;
videoCodec = NULL;
audioCodec = NULL;
swsContext = NULL;
options = NULL;
videoDecoder = NULL;
audioDecoder = NULL;
// 初始化FFmpeg库
FFmpegThread::initlib();
}
// 初始化FFmpeg库,整个程序只需要初始化一次
void FFmpegThread::initlib()
{
// 使用互斥锁确保线程安全
static QMutex mutex;
QMutexLocker locker(&mutex);
static bool isInit = false;
if (!isInit) {
// 注册所有可用的编解码器和文件格式
av_register_all();
// 注册所有设备(用于本地摄像头)
#ifdef ffmpegdevice
avdevice_register_all();
#endif
// 初始化网络流格式(使用网络流时必须先执行)
avformat_network_init();
isInit = true;
qDebug() << TIMEMS << "init ffmpeg lib ok" << " version:" << FFMPEG_VERSION;
}
}
// 初始化视频流
bool FFmpegThread::init()
{
// 设置FFmpeg参数
// 设置缓存大小(1080p视频可以适当调大)
av_dict_set(&options, "buffer_size", "8192000", 0);
// 使用TCP方式打开RTSP流(更稳定)
av_dict_set(&options, "rtsp_transport", "tcp", 0);
// 设置超时时间(3秒)
av_dict_set(&options, "stimeout", "3000000", 0);
// 设置最大延迟(1秒)
av_dict_set(&options, "max_delay", "1000000", 0);
// 自动设置线程数
av_dict_set(&options, "threads", "auto", 0);
// 分配格式上下文
avFormatContext = avformat_alloc_context();
// 打开输入流
int result = avformat_open_input(&avFormatContext, url.toStdString().data(), NULL, &options);
if (result < 0) {
qDebug() << TIMEMS << "open input error" << url;
return false;
}
// 释放参数设置
if (options != NULL) {
av_dict_free(&options);
}
// 获取流信息
result = avformat_find_stream_info(avFormatContext, NULL);
if (result < 0) {
qDebug() << TIMEMS << "find stream info error";
return false;
}
//----------视频流处理部分----------
if (1) {
// 查找最佳视频流
videoStreamIndex = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, &videoDecoder, 0);
if (videoStreamIndex < 0) {
qDebug() << TIMEMS << "find video stream index error";
return false;
}
// 获取视频流
AVStream *videoStream = avFormatContext->streams[videoStreamIndex];
videoCodec = videoStream->codec;
// 查找视频解码器
videoDecoder = avcodec_find_decoder(videoCodec->codec_id);
if (videoDecoder == NULL) {
qDebug() << TIMEMS << "video decoder not found";
return false;
}
// 设置解码器参数
videoCodec->lowres = videoDecoder->max_lowres;
videoCodec->flags2 |= AV_CODEC_FLAG2_FAST;
// 打开视频解码器
result = avcodec_open2(videoCodec, videoDecoder, NULL);
if (result < 0) {
qDebug() << TIMEMS << "open video codec error";
return false;
}
// 获取视频分辨率
videoWidth = videoStream->codec->width;
videoHeight = videoStream->codec->height;
if (videoWidth == 0 || videoHeight == 0) {
qDebug() << TIMEMS << "find width height error";
return false;
}
// 输出视频流信息
QString videoInfo = QString("视频流信息 -> 索引: %1 解码: %2 格式: %3 时长: %4 秒 分辨率: %5*%6")
.arg(videoStreamIndex).arg(videoDecoder->name).arg(avFormatContext->iformat->name)
.arg((avFormatContext->duration) / 1000000).arg(videoWidth).arg(videoHeight);
qDebug() << TIMEMS << videoInfo;
}
//----------音频流处理部分----------
if (1) {
// 查找音频流
audioStreamIndex = -1;
for (uint i = 0; i < avFormatContext->nb_streams; i++) {
if (avFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
audioStreamIndex = i;
break;
}
}
// 处理音频流(如果有)
if (audioStreamIndex == -1) {
qDebug() << TIMEMS << "find audio stream index error";
} else {
// 获取音频流和解码器
AVStream *audioStream = avFormatContext->streams[audioStreamIndex];
audioCodec = audioStream->codec;
audioDecoder = avcodec_find_decoder(audioCodec->codec_id);
if (audioDecoder == NULL) {
qDebug() << TIMEMS << "audio codec not found";
return false;
}
// 打开音频解码器
result = avcodec_open2(audioCodec, audioDecoder, NULL);
if (result < 0) {
qDebug() << TIMEMS << "open audio codec error";
return false;
}
// 输出音频流信息
QString audioInfo = QString("音频流信息 -> 索引: %1 解码: %2 比特率: %3 声道数: %4 采样: %5")
.arg(audioStreamIndex).arg(audioDecoder->name).arg(avFormatContext->bit_rate)
.arg(audioCodec->channels).arg(audioCodec->sample_rate);
qDebug() << TIMEMS << audioInfo;
}
}
// 分配内存
avPacket = av_packet_alloc();
avFrame = av_frame_alloc();
avFrame2 = av_frame_alloc();
avFrame3 = av_frame_alloc();
// 根据视频分辨率分配缓冲区
if (oldWidth != videoWidth || oldHeight != videoHeight) {
int byte = avpicture_get_size(AV_PIX_FMT_RGB32, videoWidth, videoHeight);
buffer = (uint8_t *)av_malloc(byte * sizeof(uint8_t));
oldWidth = videoWidth;
oldHeight = videoHeight;
}
// 设置图像格式转换参数
AVPixelFormat srcFormat = videoCodec->pix_fmt;
AVPixelFormat dstFormat = AV_PIX_FMT_RGB32;
int flags = SWS_FAST_BILINEAR;
// 填充图像数据
av_image_fill_arrays(avFrame3->data, avFrame3->linesize, buffer, dstFormat, videoWidth, videoHeight, 1);
// 创建图像转换上下文
swsContext = sws_getContext(videoWidth, videoHeight, srcFormat, videoWidth, videoHeight, dstFormat, flags, NULL, NULL, NULL);
return true;
}
// 线程运行函数
void FFmpegThread::run()
{
while (!stopped) {
// 根据标志位执行初始化
if (isPlay) {
this->init();
isPlay = false;
continue;
}
// 读取一帧数据
if (av_read_frame(avFormatContext, avPacket) >= 0) {
int index = avPacket->stream_index;
// 处理视频帧
if (index == videoStreamIndex) {
// 发送数据包到解码器
frameFinish = avcodec_send_packet(videoCodec, avPacket);
if (frameFinish < 0) {
continue;
}
// 接收解码后的帧
frameFinish = avcodec_receive_frame(videoCodec, avFrame2);
if (frameFinish < 0) {
continue;
}
if (frameFinish >=