497 lines
16 KiB
C++
497 lines
16 KiB
C++
// FFmpegDecoder.cpp
|
|
//#include "FFmpegDecoder.h"
|
|
//
|
|
//FFmpegDecoder::FFmpegDecoder(QObject* parent) :
|
|
// QThread(parent),
|
|
// videoLabel(nullptr),
|
|
// abort(false),
|
|
// restart(false),
|
|
// formatContext(nullptr),
|
|
// codecContext(nullptr),
|
|
// frame(nullptr),
|
|
// packet(nullptr),
|
|
// swsContext(nullptr),
|
|
// videoStreamIndex(-1) // 初始化成员变量
|
|
//{
|
|
// av_log_set_level(AV_LOG_QUIET); // 设置日志级别为安静模式
|
|
// avformat_network_init(); // 初始化网络
|
|
//}
|
|
//
|
|
//FFmpegDecoder::~FFmpegDecoder()
|
|
//{
|
|
// mutex.lock();
|
|
// abort = true;
|
|
// condition.wakeOne();
|
|
// mutex.unlock();
|
|
// wait();
|
|
// if (codecContext) {
|
|
// avcodec_free_context(&codecContext);
|
|
// }
|
|
// if (frame) {
|
|
// av_frame_free(&frame);
|
|
// }
|
|
// if (packet) {
|
|
// av_packet_free(&packet);
|
|
// }
|
|
// if (swsContext) {
|
|
// sws_freeContext(swsContext);
|
|
// }
|
|
// if (formatContext) {
|
|
// avformat_close_input(&formatContext);
|
|
// }
|
|
// avformat_network_deinit(); // 反初始化网络
|
|
//}
|
|
//
|
|
//void FFmpegDecoder::initialize()
|
|
//{
|
|
// // 初始化FFmpeg库
|
|
// avformat_network_init();
|
|
//}
|
|
//
|
|
//void FFmpegDecoder::decodeFile(const QString& filePath, QLabel* videoLabel)
|
|
//{
|
|
// QMutexLocker locker(&mutex);
|
|
// this->filePath = filePath;
|
|
// this->videoLabel = videoLabel;
|
|
// if (!isRunning()) {
|
|
// //start(LowPriority);
|
|
// start(NormalPriority);
|
|
// }
|
|
// restart = true;
|
|
// condition.wakeOne();
|
|
//}
|
|
//
|
|
//void FFmpegDecoder::run()
|
|
//{
|
|
// QFile file(filePath);
|
|
// qint64 fileSize = 0;
|
|
//
|
|
// for (;;) {
|
|
// mutex.lock();
|
|
// while (!restart && !abort) {
|
|
// condition.wait(&mutex);
|
|
// }
|
|
// if (abort) {
|
|
// mutex.unlock();
|
|
// break;
|
|
// }
|
|
// restart = false;
|
|
// QLabel* currentVideoLabel = videoLabel;
|
|
// QSize labelSize = currentVideoLabel->size();
|
|
// mutex.unlock();
|
|
// qDebug() << "Video label size: Width =" << labelSize.width() << ", Height =" << labelSize.height();
|
|
//
|
|
// if (!file.open(QIODevice::ReadOnly)) {
|
|
// qWarning() << "Failed to open file:" << filePath;
|
|
// continue;
|
|
// }
|
|
// // 从上次处理的位置开始读取
|
|
// file.seek(fileSize);
|
|
//
|
|
// formatContext = nullptr;
|
|
// codecContext = nullptr;
|
|
// frame = nullptr;
|
|
// packet = nullptr;
|
|
//
|
|
// // 通过 FFmpeg 初始化格式上下文和解码器
|
|
// if (avformat_open_input(&formatContext, filePath.toStdString().c_str(), nullptr, nullptr) != 0) {
|
|
// qWarning() << "Failed to open file with FFmpeg:" << filePath;
|
|
// file.close();
|
|
// continue;
|
|
// }
|
|
//
|
|
// if (avformat_find_stream_info(formatContext, nullptr) < 0) {
|
|
// qWarning() << "Failed to retrieve stream info";
|
|
// avformat_close_input(&formatContext);
|
|
// file.close();
|
|
// continue;
|
|
// }
|
|
//
|
|
// videoStreamIndex = -1;
|
|
// for (unsigned int i = 0; i < formatContext->nb_streams; ++i) {
|
|
// if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
|
// videoStreamIndex = i;
|
|
// break;
|
|
// }
|
|
// }
|
|
// if (videoStreamIndex == -1) {
|
|
// qWarning() << "No video stream found";
|
|
// avformat_close_input(&formatContext);
|
|
// file.close();
|
|
// continue;
|
|
// }
|
|
//
|
|
// AVCodecParameters* codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
|
|
// const AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id);
|
|
// if (!codec) {
|
|
// qWarning() << "Unsupported codec";
|
|
// avformat_close_input(&formatContext);
|
|
// file.close();
|
|
// continue;
|
|
// }
|
|
//
|
|
// codecContext = avcodec_alloc_context3(codec);
|
|
// if (!codecContext) {
|
|
// qWarning() << "Failed to allocate codec context";
|
|
// avformat_close_input(&formatContext);
|
|
// file.close();
|
|
// continue;
|
|
// }
|
|
//
|
|
// if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
|
|
// qWarning() << "Failed to copy codec parameters to context";
|
|
// avcodec_free_context(&codecContext);
|
|
// avformat_close_input(&formatContext);
|
|
// file.close();
|
|
// continue;
|
|
// }
|
|
//
|
|
// if (avcodec_open2(codecContext, codec, nullptr) < 0) {
|
|
// qWarning() << "Failed to open codec";
|
|
// avcodec_free_context(&codecContext);
|
|
// avformat_close_input(&formatContext);
|
|
// file.close();
|
|
// continue;
|
|
// }
|
|
//
|
|
// frame = av_frame_alloc();
|
|
// packet = av_packet_alloc();
|
|
//
|
|
// // 主解码循环
|
|
// while (!abort) {
|
|
// qint64 currentFileSize = file.size();
|
|
// if (currentFileSize > fileSize) {
|
|
// fileSize = currentFileSize;
|
|
// file.seek(fileSize); // 设置文件读取位置到末尾
|
|
//
|
|
// // 读取并处理数据包
|
|
// while (av_read_frame(formatContext, packet) >= 0) {
|
|
// if (packet->stream_index == videoStreamIndex) {
|
|
// int ret = avcodec_send_packet(codecContext, packet);
|
|
// if (ret < 0) {
|
|
// qWarning() << "Error sending packet for decoding";
|
|
// av_packet_unref(packet);
|
|
// continue;
|
|
// }
|
|
// while (ret >= 0) {
|
|
// ret = avcodec_receive_frame(codecContext, frame);
|
|
// if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
|
// qWarning() << "----------- break";
|
|
// av_packet_unref(packet);
|
|
// continue;
|
|
// //break;
|
|
// }
|
|
// else if (ret < 0) {
|
|
// qWarning() << "Error during decoding";
|
|
// break;
|
|
// }
|
|
//
|
|
// /*mutex.lock();
|
|
// QSize labelSize = currentVideoLabel->size();
|
|
// mutex.unlock();*/
|
|
// //qDebug() << "Video label size: Width =" << labelSize.width() << ", Height =" << labelSize.height();
|
|
// QImage img = avFrameToQImage(frame);
|
|
// QImage scaledImage = img.scaled(labelSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
// currentVideoLabel->setPixmap(QPixmap::fromImage(scaledImage));
|
|
// //currentVideoLabel->setPixmap(QPixmap::fromImage(img));
|
|
// QThread::msleep(10); // Simulate 25 FPS frame rate
|
|
// }
|
|
// }
|
|
// av_packet_unref(packet);
|
|
// }
|
|
// }
|
|
//
|
|
// mutex.lock();
|
|
// if (restart) {
|
|
// restart = false;
|
|
// mutex.unlock();
|
|
// break;
|
|
// }
|
|
// mutex.unlock();
|
|
// }
|
|
//
|
|
// avcodec_free_context(&codecContext);
|
|
// avformat_close_input(&formatContext);
|
|
// av_frame_free(&frame);
|
|
// av_packet_free(&packet);
|
|
// file.close();
|
|
// sws_freeContext(swsContext);
|
|
//
|
|
// mutex.lock();
|
|
// if (!restart) {
|
|
// condition.wait(&mutex);
|
|
// }
|
|
// mutex.unlock();
|
|
// }
|
|
//}
|
|
//
|
|
//
|
|
//QImage FFmpegDecoder::avFrameToQImage(AVFrame* frame)
|
|
//{
|
|
// int width = frame->width;
|
|
// int height = frame->height;
|
|
// AVPixelFormat pixFmt = (AVPixelFormat)frame->format;
|
|
//
|
|
// SwsContext* swsCtx = sws_getContext(width, height, pixFmt, width, height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr);
|
|
// if (!swsCtx) {
|
|
// qWarning() << "Failed to initialize the conversion context";
|
|
// return QImage();
|
|
// }
|
|
//
|
|
// QImage img(width, height, QImage::Format_RGB888);
|
|
// uint8_t* dest[4] = { img.bits(), nullptr, nullptr, nullptr };
|
|
// int destLinesize[4] = { img.bytesPerLine(), 0, 0, 0 };
|
|
//
|
|
// sws_scale(swsCtx, frame->data, frame->linesize, 0, height, dest, destLinesize);
|
|
// sws_freeContext(swsCtx);
|
|
//
|
|
// return img;
|
|
//}
|
|
//
|
|
//
|
|
|
|
// FFmpegDecoder.cpp
|
|
#include "FFmpegDecoder.h"
|
|
|
|
FFmpegDecoder::FFmpegDecoder(QObject* parent) :
|
|
QThread(parent),
|
|
videoLabel(nullptr),
|
|
abort(false),
|
|
restart(false),
|
|
formatContext(nullptr),
|
|
codecContext(nullptr),
|
|
frame(nullptr),
|
|
packet(nullptr),
|
|
swsContext(nullptr),
|
|
videoStreamIndex(-1) // 初始化成员变量
|
|
{
|
|
av_log_set_level(AV_LOG_QUIET); // 设置日志级别为安静模式
|
|
avformat_network_init(); // 初始化网络
|
|
qDebug() << "FFmpegDecoder created";
|
|
}
|
|
|
|
FFmpegDecoder::~FFmpegDecoder()
|
|
{
|
|
qDebug() << "Destroying FFmpegDecoder";
|
|
mutex.lock();
|
|
abort = true;
|
|
condition.wakeOne();
|
|
mutex.unlock();
|
|
wait();
|
|
cleanup();
|
|
avformat_network_deinit(); // 反初始化网络
|
|
qDebug() << "FFmpegDecoder destroyed";
|
|
}
|
|
|
|
void FFmpegDecoder::initialize()
|
|
{
|
|
qDebug() << "Initializing FFmpeg library";
|
|
// 初始化FFmpeg库
|
|
avformat_network_init();
|
|
}
|
|
|
|
void FFmpegDecoder::decodeFile(const QString& filePath, QLabel* videoLabel)
|
|
{
|
|
QMutexLocker locker(&mutex);
|
|
this->filePath = filePath;
|
|
this->videoLabel = videoLabel;
|
|
if (!isRunning()) {
|
|
qDebug() << "Starting decoder thread";
|
|
start(NormalPriority);
|
|
}
|
|
restart = true;
|
|
condition.wakeOne();
|
|
}
|
|
|
|
void FFmpegDecoder::run()
|
|
{
|
|
QFile file(filePath);
|
|
qint64 fileSize = 0;
|
|
|
|
while (true) {
|
|
mutex.lock();
|
|
while (!restart && !abort) {
|
|
condition.wait(&mutex);
|
|
}
|
|
if (abort) {
|
|
mutex.unlock();
|
|
qDebug() << "Decoder thread aborting";
|
|
break;
|
|
}
|
|
restart = false;
|
|
QLabel* currentVideoLabel = videoLabel;
|
|
QSize labelSize = currentVideoLabel->size();
|
|
mutex.unlock();
|
|
qDebug() << "Video label size: Width =" << labelSize.width() << ", Height =" << labelSize.height();
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
qWarning() << "Failed to open file:" << filePath;
|
|
continue;
|
|
}
|
|
|
|
if (!initializeFFmpeg(filePath)) {
|
|
qDebug() << "Failed to initialize FFmpeg for file:" << filePath;
|
|
cleanup();
|
|
file.close();
|
|
continue;
|
|
}
|
|
|
|
// 主解码循环
|
|
while (!abort) {
|
|
qint64 currentFileSize = file.size();
|
|
if (currentFileSize > fileSize) {
|
|
fileSize = currentFileSize;
|
|
file.seek(fileSize); // 设置文件读取位置到末尾
|
|
|
|
// 读取并处理数据包
|
|
while (av_read_frame(formatContext, packet) >= 0) {
|
|
if (packet->stream_index == videoStreamIndex) {
|
|
int ret = avcodec_send_packet(codecContext, packet);
|
|
if (ret < 0) {
|
|
qWarning() << "Error sending packet for decoding";
|
|
av_packet_unref(packet);
|
|
continue;
|
|
}
|
|
while (ret >= 0) {
|
|
ret = avcodec_receive_frame(codecContext, frame);
|
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
|
av_packet_unref(packet);
|
|
continue;
|
|
}
|
|
else if (ret < 0) {
|
|
qWarning() << "Error during decoding";
|
|
break;
|
|
}
|
|
|
|
QImage img = avFrameToQImage(frame);
|
|
QImage scaledImage = img.scaled(labelSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
currentVideoLabel->setPixmap(QPixmap::fromImage(scaledImage));
|
|
QThread::msleep(10); // Simulate 25 FPS frame rate
|
|
}
|
|
}
|
|
av_packet_unref(packet);
|
|
}
|
|
}
|
|
|
|
mutex.lock();
|
|
if (restart) {
|
|
restart = false;
|
|
mutex.unlock();
|
|
break;
|
|
}
|
|
mutex.unlock();
|
|
}
|
|
|
|
cleanup();
|
|
file.close();
|
|
|
|
mutex.lock();
|
|
if (!restart) {
|
|
condition.wait(&mutex);
|
|
}
|
|
mutex.unlock();
|
|
}
|
|
}
|
|
|
|
bool FFmpegDecoder::initializeFFmpeg(const QString& filePath)
|
|
{
|
|
if (avformat_open_input(&formatContext, filePath.toStdString().c_str(), nullptr, nullptr) != 0) {
|
|
qWarning() << "Failed to open file with FFmpeg:" << filePath;
|
|
return false;
|
|
}
|
|
|
|
if (avformat_find_stream_info(formatContext, nullptr) < 0) {
|
|
qWarning() << "Failed to retrieve stream info";
|
|
return false;
|
|
}
|
|
|
|
videoStreamIndex = -1;
|
|
for (unsigned int i = 0; i < formatContext->nb_streams; ++i) {
|
|
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
|
videoStreamIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (videoStreamIndex == -1) {
|
|
qWarning() << "No video stream found";
|
|
return false;
|
|
}
|
|
|
|
AVCodecParameters* codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
|
|
const AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id);
|
|
if (!codec) {
|
|
qWarning() << "Unsupported codec";
|
|
return false;
|
|
}
|
|
|
|
codecContext = avcodec_alloc_context3(codec);
|
|
if (!codecContext) {
|
|
qWarning() << "Failed to allocate codec context";
|
|
return false;
|
|
}
|
|
|
|
if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
|
|
qWarning() << "Failed to copy codec parameters to context";
|
|
return false;
|
|
}
|
|
|
|
if (avcodec_open2(codecContext, codec, nullptr) < 0) {
|
|
qWarning() << "Failed to open codec";
|
|
return false;
|
|
}
|
|
|
|
frame = av_frame_alloc();
|
|
packet = av_packet_alloc();
|
|
|
|
return true;
|
|
}
|
|
|
|
void FFmpegDecoder::cleanup()
|
|
{
|
|
if (codecContext) {
|
|
avcodec_free_context(&codecContext);
|
|
codecContext = nullptr;
|
|
}
|
|
if (frame) {
|
|
av_frame_free(&frame);
|
|
frame = nullptr;
|
|
}
|
|
if (packet) {
|
|
av_packet_free(&packet);
|
|
packet = nullptr;
|
|
}
|
|
if (swsContext) {
|
|
sws_freeContext(swsContext);
|
|
swsContext = nullptr;
|
|
}
|
|
if (formatContext) {
|
|
avformat_close_input(&formatContext);
|
|
formatContext = nullptr;
|
|
}
|
|
}
|
|
|
|
QImage FFmpegDecoder::avFrameToQImage(AVFrame* frame)
|
|
{
|
|
int width = frame->width;
|
|
int height = frame->height;
|
|
AVPixelFormat pixFmt = (AVPixelFormat)frame->format;
|
|
|
|
if (!swsContext) {
|
|
swsContext = sws_getContext(width, height, pixFmt, width, height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr);
|
|
if (!swsContext) {
|
|
qWarning() << "Failed to initialize the conversion context";
|
|
return QImage();
|
|
}
|
|
}
|
|
|
|
QImage img(width, height, QImage::Format_RGB888);
|
|
uint8_t* dest[4] = { img.bits(), nullptr, nullptr, nullptr };
|
|
int destLinesize[4] = { img.bytesPerLine(), 0, 0, 0 };
|
|
|
|
sws_scale(swsContext, frame->data, frame->linesize, 0, height, dest, destLinesize);
|
|
|
|
return img;
|
|
}
|
|
|
|
|