| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- #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
|