#include "video_renderer.h" #include "../base/logger.h" #include #include #include #include #include extern "C" { #include #include } namespace av { namespace player { VideoRenderer::VideoRenderer(QWidget* parent) : QLabel(parent) , m_videoWidth(0) , m_videoHeight(0) , m_inputFormat(AV_PIX_FMT_NONE) , m_swsContext(nullptr) , m_rgbBuffer(nullptr) , m_rgbBufferSize(0) , m_backgroundColor(Qt::black) , m_keepAspectRatio(true) , m_initialized(false) , m_updateTimer(new QTimer(this)) { // 设置基本属性 setAlignment(Qt::AlignCenter); setStyleSheet("background-color: black;"); setMinimumSize(320, 240); // 连接更新定时器 connect(m_updateTimer, &QTimer::timeout, this, &VideoRenderer::updateDisplay); m_updateTimer->setSingleShot(true); } VideoRenderer::~VideoRenderer() { cleanupConverter(); } bool VideoRenderer::initialize(int width, int height, AVPixelFormat pixelFormat) { QMutexLocker locker(&m_mutex); if (m_initialized) { av::Logger::instance().warning("Video renderer already initialized"); return true; } if (width <= 0 || height <= 0) { av::Logger::instance().error("Invalid video dimensions"); return false; } m_videoWidth = width; m_videoHeight = height; m_inputFormat = pixelFormat; // 初始化图像转换器 if (!initConverter()) { av::Logger::instance().error("Failed to initialize video converter"); return false; } // 设置控件大小提示 setMinimumSize(m_videoWidth / 4, m_videoHeight / 4); m_initialized = true; av::Logger::instance().info( QString("Video renderer initialized: %1x%2").arg(width).arg(height).toStdString()); return true; } bool VideoRenderer::renderFrame(const AVFramePtr& frame) { if (!frame || !m_initialized) { return false; } // 转换帧为QImage QImage image = convertFrameToImage(frame); if (image.isNull()) { return false; } // 缩放图像 QPixmap pixmap = scaleImage(image); { QMutexLocker locker(&m_mutex); m_currentPixmap = pixmap; } // 触发更新显示 if (!m_updateTimer->isActive()) { m_updateTimer->start(16); // �?0fps } return true; } void VideoRenderer::clear() { QMutexLocker locker(&m_mutex); m_currentPixmap = QPixmap(); QLabel::clear(); update(); } void VideoRenderer::setKeepAspectRatio(bool keepAspectRatio) { if (m_keepAspectRatio != keepAspectRatio) { m_keepAspectRatio = keepAspectRatio; update(); } } bool VideoRenderer::getKeepAspectRatio() const { return m_keepAspectRatio; } void VideoRenderer::setBackgroundColor(const QColor& color) { if (m_backgroundColor != color) { m_backgroundColor = color; setStyleSheet(QString("background-color: %1;").arg(color.name())); update(); } } QSize VideoRenderer::getVideoSize() const { return QSize(m_videoWidth, m_videoHeight); } QSize VideoRenderer::getDisplaySize() const { return size(); } bool VideoRenderer::isInitialized() const { return m_initialized; } void VideoRenderer::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.fillRect(rect(), m_backgroundColor); QMutexLocker locker(&m_mutex); if (!m_currentPixmap.isNull()) { QRect displayRect = calculateDisplayRect(); painter.drawPixmap(displayRect, m_currentPixmap); } QLabel::paintEvent(event); } void VideoRenderer::resizeEvent(QResizeEvent* event) { QLabel::resizeEvent(event); update(); } void VideoRenderer::updateDisplay() { update(); } bool VideoRenderer::initConverter() { // 创建图像转换上下文 m_swsContext = sws_getContext( m_videoWidth, m_videoHeight, m_inputFormat, m_videoWidth, m_videoHeight, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr ); if (!m_swsContext) { av::Logger::instance().error("Failed to create image converter context"); return false; } // 分配RGB缓冲区 m_rgbBufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, m_videoWidth, m_videoHeight, 1); m_rgbBuffer = static_cast(av_malloc(m_rgbBufferSize)); if (!m_rgbBuffer) { av::Logger::instance().error("Failed to allocate RGB buffer"); sws_freeContext(m_swsContext); m_swsContext = nullptr; return false; } av::Logger::instance().info("Video converter initialized successfully"); return true; } void VideoRenderer::cleanupConverter() { if (m_swsContext) { sws_freeContext(m_swsContext); m_swsContext = nullptr; } if (m_rgbBuffer) { av_free(m_rgbBuffer); m_rgbBuffer = nullptr; m_rgbBufferSize = 0; } } QImage VideoRenderer::convertFrameToImage(const AVFramePtr& frame) { if (!frame || !m_swsContext || !m_rgbBuffer) { return QImage(); } // 设置输出缓冲区 uint8_t* dstData[4] = { m_rgbBuffer, nullptr, nullptr, nullptr }; int dstLinesize[4] = { m_videoWidth * 3, 0, 0, 0 }; // 执行图像转换 int result = sws_scale( m_swsContext, frame->data, frame->linesize, 0, m_videoHeight, dstData, dstLinesize ); if (result != m_videoHeight) { av::Logger::instance().error("Image conversion failed"); return QImage(); } // 创建QImage QImage image(m_rgbBuffer, m_videoWidth, m_videoHeight, QImage::Format_RGB888); return image.copy(); // 复制数据,避免缓冲区被覆盖 } QRect VideoRenderer::calculateDisplayRect() const { if (m_videoWidth <= 0 || m_videoHeight <= 0) { return rect(); } QSize widgetSize = size(); QSize videoSize(m_videoWidth, m_videoHeight); if (!m_keepAspectRatio) { return rect(); } // 计算保持宽高比的显示矩形 QSize scaledSize = videoSize.scaled(widgetSize, Qt::KeepAspectRatio); int x = (widgetSize.width() - scaledSize.width()) / 2; int y = (widgetSize.height() - scaledSize.height()) / 2; return QRect(x, y, scaledSize.width(), scaledSize.height()); } QPixmap VideoRenderer::scaleImage(const QImage& image) const { if (image.isNull()) { return QPixmap(); } QSize displaySize = size(); if (displaySize.isEmpty()) { return QPixmap::fromImage(image); } QSize targetSize; if (m_keepAspectRatio) { targetSize = image.size().scaled(displaySize, Qt::KeepAspectRatio); } else { targetSize = displaySize; } if (targetSize == image.size()) { return QPixmap::fromImage(image); } QImage scaledImage = image.scaled(targetSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); return QPixmap::fromImage(scaledImage); } } // namespace player } // namespace av #include "video_renderer.moc"