依据 QVideoWidget原理,实现一个最简单的类 QVideoWidget 播放d3d视频
时间: 2023-12-06 07:39:36 浏览: 36
首先,需要了解一下 QVideoWidget 的原理。QVideoWidget 是一个继承自 QWidget 的类,用于在 QWidget 上显示视频。它是通过使用 QAbstractVideoSurface 作为后端实现的,可以接收来自 QMediaPlayer 或其他支持 QAbstractVideoSurface 的视频源的数据,并将其渲染到 QWidget 上。
要播放 D3D 视频,需要使用 Direct3D 渲染器来将视频渲染到 QVideoWidget 上。这可以通过创建一个 D3D 纹理并将其与 QAbstractVideoSurface 关联来完成。下面是一个最简单的实现示例:
```cpp
#include <QWidget>
#include <QAbstractVideoSurface>
#include <d3d9.h>
class D3DVideoSurface : public QAbstractVideoSurface
{
public:
D3DVideoSurface(QWidget* widget, IDirect3DDevice9* device)
: m_widget(widget), m_device(device)
{
HRESULT hr = device->CreateTexture(640, 480, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &m_texture, NULL);
Q_ASSERT(SUCCEEDED(hr));
}
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const override
{
Q_UNUSED(handleType);
return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_RGB32;
}
bool present(const QVideoFrame& frame) override
{
Q_UNUSED(frame);
IDirect3DSurface9* surface = nullptr;
HRESULT hr = m_texture->GetSurfaceLevel(0, &surface);
Q_ASSERT(SUCCEEDED(hr));
D3DLOCKED_RECT rect = { 0 };
hr = surface->LockRect(&rect, NULL, D3DLOCK_DISCARD);
Q_ASSERT(SUCCEEDED(hr));
const int stride = rect.Pitch / 4;
const uchar* src = frame.bits();
uchar* dst = reinterpret_cast<uchar*>(rect.pBits);
for (int y = 0; y < frame.height(); ++y)
{
memcpy(dst + y * rect.Pitch, src + y * frame.bytesPerLine(), frame.width() * 4);
}
hr = surface->UnlockRect();
Q_ASSERT(SUCCEEDED(hr));
hr = m_device->BeginScene();
Q_ASSERT(SUCCEEDED(hr));
hr = m_device->StretchRect(surface, NULL, m_widget->winId(), NULL, D3DTEXF_LINEAR);
Q_ASSERT(SUCCEEDED(hr));
hr = m_device->EndScene();
Q_ASSERT(SUCCEEDED(hr));
surface->Release();
return true;
}
private:
QWidget* m_widget;
IDirect3DDevice9* m_device;
IDirect3DTexture9* m_texture;
};
class D3DVideoWidget : public QWidget
{
public:
D3DVideoWidget(QWidget* parent = nullptr)
: QWidget(parent)
{
HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_d3d);
Q_ASSERT(SUCCEEDED(hr));
D3DPRESENT_PARAMETERS params = { 0 };
params.Windowed = TRUE;
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
params.BackBufferFormat = D3DFMT_UNKNOWN;
params.BackBufferWidth = width();
params.BackBufferHeight = height();
params.EnableAutoDepthStencil = TRUE;
params.AutoDepthStencilFormat = D3DFMT_D16;
hr = m_d3d->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, winId(), D3DCREATE_HARDWARE_VERTEXPROCESSING, ¶ms, NULL, &m_device);
Q_ASSERT(SUCCEEDED(hr));
m_surface = new D3DVideoSurface(this, m_device);
}
virtual QPaintEngine* paintEngine() const override
{
return nullptr;
}
virtual QAbstractVideoSurface* videoSurface() const override
{
return m_surface;
}
private:
IDirect3D9Ex* m_d3d;
IDirect3DDevice9Ex* m_device;
D3DVideoSurface* m_surface;
};
```
在这个示例中,我们创建了一个名为 D3DVideoWidget 的 QWidget 子类。它使用 Direct3D 9 渲染器将视频渲染到 QWidget 上。我们还创建了一个名为 D3DVideoSurface 的 QAbstractVideoSurface 子类,它将视频帧渲染到 Direct3D 纹理上,并在每一帧结束后将纹理渲染到 QWidget 上。
最后,我们可以使用 QMediaPlayer 或其他支持 QAbstractVideoSurface 的视频源来播放 D3D 视频。例如:
```cpp
#include <QApplication>
#include <QMediaPlayer>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
D3DVideoWidget* videoWidget = new D3DVideoWidget;
videoWidget->show();
QMediaPlayer* player = new QMediaPlayer;
player->setVideoOutput(videoWidget->videoSurface());
player->setMedia(QUrl("file:///C:/test.avi"));
player->play();
return app.exec();
}
```
这个示例展示了如何使用 QMediaPlayer 和 D3DVideoWidget 播放 D3D 视频。我们将 QMediaPlayer 的视频输出设置为 D3DVideoSurface,这将自动将视频帧传递给 D3DVideoSurface 的 present() 方法进行渲染。然后,我们调用 play() 方法开始播放视频。