|
|
@@ -1,304 +0,0 @@
|
|
|
-#include "video_renderer.h"
|
|
|
-#include "../base/logger.h"
|
|
|
-#include <QPainter>
|
|
|
-#include <QResizeEvent>
|
|
|
-#include <QApplication>
|
|
|
-#include <QDebug>
|
|
|
-#include <algorithm>
|
|
|
-
|
|
|
-extern "C" {
|
|
|
-#include <libavutil/imgutils.h>
|
|
|
-#include <libswscale/swscale.h>
|
|
|
-}
|
|
|
-
|
|
|
-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, double fps)
|
|
|
-{
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- if (fps <= 0) {
|
|
|
- fps = 25.0; // 默认帧率
|
|
|
- }
|
|
|
-
|
|
|
- m_videoWidth = width;
|
|
|
- m_videoHeight = height;
|
|
|
- m_inputFormat = pixelFormat;
|
|
|
- m_fps = fps;
|
|
|
-
|
|
|
- // 初始化图像转换器
|
|
|
- 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 @ %3fps").arg(width).arg(height).arg(fps).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()) {
|
|
|
- int interval = static_cast<int>(1000.0 / m_fps); // 根据帧率计算间隔
|
|
|
- m_updateTimer->start(interval);
|
|
|
- }
|
|
|
-
|
|
|
- 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<uint8_t*>(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
|
|
|
-
|