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