Недавно я обновляю Qt-версию аудио- и видеопроекта с 5.15.0 до 6.4.3 (то же самое относится и к 6.5). Помимо удаления некоторых элементов управления Qt Quick Controls 1 в некоторых QML, самое главное — это удаление некоторых элементов управления Qt Quick Controls 1 в некоторых QML. улучшение пользовательского рендеринга видео.
Qt5 Чжунцзай QML При рендеринге пользовательских видеокадров на C++ Layer реализует слой, полученный из QObject Подкласс, используемый внутри QAbstractVideoSurface
Приди и отдай VideoOutput Предоставьте данные. Конкретный метод здесь обсуждаться не будет. Вы можете обратиться к статье,которую я написал ранее. Qt QML VideoOutput Показать индивидуальные YUV420P поток данных существовать Qt6 середина,QAbstractVideoSurface одеяло QVideoSink альтернатива, обеспечивающая более простой способ доставки QVideoFrame, пример кода выглядит следующим образом:
class FrameProvider : public QObject {
Q_OBJECT
Q_PROPERTY(QVideoSink* videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged)
public:
explicit FrameProvider(QObject* parent = nullptr);
~FrameProvider();
QVideoSink* videoSink() const { return m_videoSink; }
void setVideoSink(QVideoSink* videoSink);
signals:
void videoSinkChanged();
public slots:
void deliverFrame(const QVideoFrame& frame);
private:
QPointer<QVideoSink> m_videoSink;
};
Класс объявляет функцию слота deliverFrame
Предоставить видеокадр. Предоставить данные привязки модуля и кадра доставки. существовать cpp В реализации только при наличии нового видеопотока он вызывается напрямую m_videoSink из setVideoFrame Метод будет делать:
void FrameProvider::deliverFrame(const QVideoFrame& frame) {
if (!m_videoSink)
return;
m_videoSink->setVideoFrame(frame);
}
Воля FrameProvider Следуйте тому же методу, который указан в статье выше, и зарегистрируйтесь, чтобы QML конец, с VideoOutput При совместном использовании есть некоторые небольшие изменения:
FrameProvider {
id: frameProvider
videoSink: videoContainer.videoSink
}
VideoOutput {
id: videoContainer
anchors.fill: parent
fillMode: VideoOutput.PreserveAspectFit
}
так VideoOutput с новымиз FrameProvider Это делается при совместном использовании. Давайте поговорим об этом дальше. QVideoFrame изизменять:
существовать Qt5 например, копирование YUV данные поступают QVideoFrame очень жестокий, по videoFrame.bits() Получите адрес, вычислите местоположение и скопируйте его, не задумываясь:
int frameSize = static_cast<int>(frame.width * frame.height * frame.count / 2);
QVideoFrame videoFrame(frameSize, QSize(static_cast<int>(rotationWidth), static_cast<int>(rotationHeight)), static_cast<int>(rotationWidth),
QVideoFrame::Format_YUV420P);
if (videoFrame.map(QAbstractVideoBuffer::WriteOnly)) {
auto src = reinterpret_cast<uint8_t*>(frame.data);
auto dest = reinterpret_cast<uint8_t*>(videoFrame.bits());
libyuv::I420Rotate(src + frame.offset[0], static_cast<int>(frame.stride[0]),
src + frame.offset[1], static_cast<int>(frame.stride[1]),
src + frame.offset[2], static_cast<int>(frame.stride[2]),
dest, static_cast<int>(rotationWidth),
dest + rotationWidth * rotationHeight, rotationWidth / 2,
dest + rotationWidth * rotationHeight + rotationWidth * rotationHeight / 4, rotationWidth / 2,
static_cast<int>(frame.width), static_cast<int>(frame.height), rotate_mode);
videoFrame.setStartTime(0);
videoFrame.unmap();
QSize size = QSize(static_cast<int>(rotationWidth), static_cast<int>(rotationHeight));
emit VideoManager::m_videoFrameDelegate->receivedVideoFrame(QString::fromStdString(accountId), videoFrame, size, bSub);
}
но Qt6 Серьезные изменения произошли, прежде всего, в bits Функция требует передачи целевых данныхиз самолет, например Y plane для 0,U и V В свою очередь для 1 и 2. Это похоже на Qt5 Нет большой разницы, но если нажать bits(0)、bits(1)、bits(1) Когда адрес скопирован в соответствии с логикой оригинала, будет обнаружено, что изображение с некоторым разрешением будет рендериться. Это в основном потому, что для оригинала. YUV Ширина данных не 16 из кратных. и QVideoFrame Однажды позвонили map функция, то каждая plane из stride(существовать Qt второе имя для bytesPerLine) Волявстречада 16 из кратных, если вы скопируете исходные данные по ширине, изображение будет испорчено. Правильный способ сделать это — через QVideoFrame Предоставить из bytesPerLine() метод расчета конкретного plane изширина,Копирование по требованию,Реализация следующая:
QVideoFrameFormat format(QSize(rotationWidth, rotationHeight), QVideoFrameFormat::Format_YUV420P);
format.setViewport(QRect(0, 0, rotationWidth, rotationHeight));
QVideoFrame videoFrame(format);
if (videoFrame.map(QVideoFrame::WriteOnly)) {
auto src = reinterpret_cast<uint8_t*>(frame.data);
// If the aspect ratio of the original data is not a multiple of 16,
// when mappedBytes(n) is called after frame mapping, the returned size will be expanded to the nearest multiple of 16.
// When copying the data, bytesPerLine(n) should be used to get the actual stride that needs to be copied.
libyuv::I420Rotate(src + frame.offset[0], frame.stride[0],
src + frame.offset[1], frame.stride[1],
src + frame.offset[2], frame.stride[2],
videoFrame.bits(0), videoFrame.bytesPerLine(0),
videoFrame.bits(1), videoFrame.bytesPerLine(1),
videoFrame.bits(2), videoFrame.bytesPerLine(2),
frame.width, frame.height, rotate_mode);
videoFrame.setStartTime(0);
videoFrame.unmap();
QSize size = QSize(static_cast<int>(rotationWidth), static_cast<int>(rotationHeight));
emit VideoManager::m_videoFrameDelegate->receivedVideoFrame(QString::fromStdString(accountId), videoFrame, size, bSub);
}
в frame.data да YUV из исходных данных. После прохождения изменений QVideoFrame API Мы видим, что Qt верновидеоиметь дело сданныеиз Требования ужесточаются,Хотя много времени было потрачено на решение проблемы,но Наконец-то подытожил ценный опыт.
Вышеупомянутый метод копирования следует использовать, когда Qt 6.x Если в версии по умолчанию используется механизм рендеринга (OpenGL), некоторые странные разрешения могут вызвать проблемы с размытием экрана. Исправлять Qt изрендеринг Engine для Каждый движок, специфичный для платформы, решает:
int main(int argc, char* argv[]) {
QGuiApplication app(argc, argv);
#if defined(Q_OS_MACX)
QQuickWindow::setGraphicsApi(QSGRendererInterface::Metal);
#else
QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11);
#endif
..... other code
}