MythTV master
videoout_d3d.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2
3#include <windows.h>
4#include <mmsystem.h>
5
6#include <map>
7#include <iostream>
8#include <algorithm>
9
10#include <QtGlobal>
11#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
12#include <QtEnvironmentVariables>
13#endif
14
15#include "libmythbase/mythconfig.h"
18
19#include "fourcc.h"
20#include "mythavutil.h"
21#include "mythplayer.h"
22#include "mythvideoprofile.h"
23#include "osd.h"
24#include "tv.h"
25#include "videoout_d3d.h"
26
27extern "C" {
28#include "libavutil/imgutils.h"
29}
30
31#undef UNICODE
32
33const int kNumBuffers = 31;
34const int kNeedFreeFrames = 1;
37
38#define LOC QString("VideoOutputD3D: ")
39
41{
42 Options.renderers->append("direct3d");
43 (*Options.safe_renderers)["dummy"].append("direct3d");
44 (*Options.safe_renderers)["nuppel"].append("direct3d");
45 if (Options.decoders->contains("ffmpeg"))
46 (*Options.safe_renderers)["ffmpeg"].append("direct3d");
47 Options.priorities->insert("direct3d", 70);
48
49#if CONFIG_DXVA2
50 if (Options.decoders->contains("dxva2"))
51 (*Options.safe_renderers)["dxva2"].append("direct3d");
52#endif
53}
54
56 MythD3D9Painter* Painter, MythDisplay* Display,
57 const MythVideoProfilePtr& VideoProfile, QString& Profile)
58 : MythVideoOutputGPU(MainWindow, Render, Painter, Display, VideoProfile, Profile)
59{
60 m_pauseFrame.m_buffer = nullptr;
61}
62
64{
65 TearDown();
66}
67
69{
70 QMutexLocker locker(&m_lock);
73 m_videoBuffers.DeleteBuffers();
75 {
76 delete [] m_pauseFrame.m_buffer;
77 m_pauseFrame.m_buffer = nullptr;
78 }
79
82}
83
85{
86 QMutexLocker locker(&m_lock);
87 m_renderValid = false;
88 m_renderReset = false;
89
90 if (m_video)
91 {
92 delete m_video;
93 m_video = nullptr;
94 }
95
96 if (m_render)
97 {
99 m_render = nullptr;
100 }
101}
102
103bool VideoOutputD3D::InputChanged(QSize video_dim_buf,
104 QSize video_dim_disp,
105 float video_aspect,
106 MythCodecID av_codec_id,
107 bool &aspect_only,
108 int [[maybe_unused]] reference_frames,
109 bool force_change)
110{
111 QMutexLocker locker(&m_lock);
112
113 QSize cursize = GetVideoDim();
114
115 LOG(VB_PLAYBACK, LOG_INFO, LOC +
116 QString("InputChanged from %1: %2x%3 aspect %4 to %5: %6x%7 aspect %9")
117 .arg(toString(m_videoCodecID)).arg(cursize.width())
118 .arg(cursize.height()).arg(GetVideoAspect())
119 .arg(toString(av_codec_id)).arg(video_dim_disp.width())
120 .arg(video_dim_disp.height()).arg(video_aspect));
121
122
123 bool cid_changed = (m_videoCodecID != av_codec_id);
124 bool res_changed = video_dim_disp != cursize;
125 bool asp_changed = video_aspect != GetVideoAspect();
126
127 if (!res_changed && !cid_changed && !force_change)
128 {
129 if (asp_changed)
130 {
131 aspect_only = true;
132 VideoAspectRatioChanged(video_aspect);
133 MoveResize();
134 }
135 return true;
136 }
137
138 TearDown();
139 QRect disp = GetDisplayVisibleRect();
140 if (Init(video_dim_buf, video_dim_disp,
141 video_aspect, disp, av_codec_id))
142 {
143 return true;
144 }
145
146 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to re-initialise video output.");
148
149 return false;
150}
151
153{
154 QMutexLocker locker(&m_lock);
156 QSize size = GetVideoDim();
157 m_render = new MythRenderD3D9();
159 m_hWnd)))
160 return false;
161
162 m_video = new D3D9Image(m_render, size, true);
163 if (!(m_video && m_video->IsValid()))
164 return false;
165
166 LOG(VB_PLAYBACK, LOG_INFO, LOC +
167 "Direct3D device successfully initialized.");
168 m_renderValid = true;
169 return true;
170}
171
172bool VideoOutputD3D::Init(QSize video_dim_buf,
173 QSize video_dim_disp,
174 float video_aspect,
175 QRect win_rect,MythCodecID codec_id)
176{
177 MythPainter *painter = GetMythPainter();
178 if (painter)
179 painter->FreeResources();
180
181 QMutexLocker locker(&m_lock);
182 m_hWnd = (HWND)m_display->m_widget->winId();
183
184 MythVideoOutput::Init(video_dim_buf, video_dim_disp,
185 video_aspect, win_rect, codec_id);
186
187 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Init with codec: %1")
188 .arg(toString(codec_id)));
189 SetProfile();
190
191 bool success = true;
192 success &= SetupContext();
193
195 {
196 if (!CreateDecoder())
197 return false;
198 }
199
200 success &= CreateBuffers();
201 success &= InitBuffers();
202
203 MoveResize();
204
205 if (!success)
206 TearDown();
207
208 return success;
209}
210
212{
213 if (m_videoProfile)
214 m_videoProfile->SetVideoRenderer("direct3d");
215}
216
218{
220 {
222 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 empty DXVA2 buffers.")
224 return true;
225 }
226
229 return true;
230
231}
232
234{
235#if CONFIG_DXVA2
236 if ((codec_is_dxva2(m_videoCodecID)) && m_decoder)
237 {
238 QMutexLocker locker(&m_lock);
239 const QSize video_dim = GetVideoDim();
240 bool ok = true;
241 for (int i = 0; i < NUM_DXVA2_BUFS; i++)
242 {
243 ok &= vbuffers.CreateBuffer(video_dim.width(),
244 video_dim.height(), i,
245 m_decoder->GetSurface(i), FMT_DXVA2);
246 }
247 if (ok)
248 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Initialised DXVA2 buffers.");
249 return ok;
250 }
251#endif
253 GetVideoDim().width(),
254 GetVideoDim().height());
255}
256
259{
260 if (IsErrored())
261 {
262 LOG(VB_GENERAL, LOG_ERR, LOC +
263 "RenderFrame() called while IsErrored is true.");
264 return;
265 }
266
267 if (!buffer && codec_is_std(m_videoCodecID))
268 buffer = m_videoBuffers.GetScratchFrame();
269
270 bool dummy = false;
271 if (buffer)
272 {
273 dummy = buffer->m_dummy;
274 m_framesPlayed = buffer->m_frameNumber + 1;
275 }
276
277 if (!m_render || !m_video)
278 return;
279
281 if (m_renderValid)
282 {
283 QRect dvr = GetDisplayVideoRect();
284 bool ok = m_render->ClearBuffer();
285 if (ok && !dummy)
287 255, true);
288
289 if (ok)
290 {
291 ok = m_render->Begin();
292 if (ok)
293 {
294 if (!dummy)
295 m_video->Draw();
296 }
297 }
298
299 }
300}
301
303{
304 if (!m_renderValid)
305 return;
306 m_render->End();
307}
308
310{
311 if (IsErrored())
312 {
313 LOG(VB_GENERAL, LOG_ERR, LOC +
314 "Show() called while IsErrored is true.");
315 return;
316 }
317
318 if (!m_render)
319 return;
320
322 if (m_renderValid)
323 m_render->Present(IsEmbedding() ? m_hEmbedWnd : nullptr);
324}
325
326void VideoOutputD3D::UpdatePauseFrame(std::chrono::milliseconds &disp_timecode, FrameScanType)
327{
328 QMutexLocker locker(&m_lock);
330
332 {
333 if (!used_frame)
334 used_frame = m_videoBuffers.GetScratchFrame();
335 CopyFrame(&m_pauseFrame, used_frame);
336 disp_timecode = m_pauseFrame.m_displayTimecode;
337 }
339 {
340 if (used_frame)
341 {
342 m_pauseSurface = used_frame->m_buffer;
343 disp_timecode = used_frame->m_displayTimecode;
344 }
345 else
346 LOG(VB_PLAYBACK, LOG_WARNING, LOC + "Failed to update pause frame");
347 }
348}
349
351{
353 return;
354
355 // TODO - add a size check
356 bool hardware_conv = false;
357 uint pitch = 0;
358 uint8_t *buf = img->GetBuffer(hardware_conv, pitch);
359 if (buf && hardware_conv)
360 {
361 copybuffer(buf, frame, pitch);
362 }
363 else if (buf && !hardware_conv)
364 {
365 AVFrame image_out;
366 av_image_fill_arrays(image_out.data, image_out.linesize,
367 (uint8_t*)buf,
368 AV_PIX_FMT_RGB32, frame->m_width, frame->m_height, IMAGE_ALIGN);
369 image_out.linesize[0] = pitch;
370 m_copyFrame.Copy(&image_out, frame,(uint8_t*)buf, AV_PIX_FMT_RGB32);
371 }
372 img->ReleaseBuffer();
373}
374
376{
377 if (!m_render || !m_video)
378 return;
379
380 QMutexLocker locker(&m_lock);
381 if (IsErrored())
382 {
383 LOG(VB_GENERAL, LOG_ERR, LOC +
384 "ProcessFrame() called while IsErrored is true.");
385 return;
386 }
387
388 bool gpu = codec_is_dxva2(m_videoCodecID);
389
390 if (gpu && frame && frame->m_type != FMT_DXVA2)
391 {
392 LOG(VB_GENERAL, LOG_ERR, LOC + "Wrong frame format");
393 return;
394 }
395
396 bool dummy = false;
397 bool pauseframe = false;
398 if (!frame)
399 {
400 if (!gpu)
401 {
402 frame = m_videoBuffers.GetScratchFrame();
403 CopyFrame(m_videoBuffers.GetScratchFrame(), &m_pauseFrame);
404 }
405 pauseframe = true;
406 }
407
408 if (frame)
409 dummy = frame->m_dummy;
410
411 // Test the device
413 if (m_renderReset)
414 SetupContext();
415
416 // Update a software decoded frame
417 if (m_renderValid && !gpu && !dummy)
418 UpdateFrame(frame, m_video);
419
420 // Update a GPU decoded frame
421 if (m_renderValid && gpu && !dummy)
422 {
424 if (m_renderReset)
426
427 if (m_renderValid && frame)
428 {
430 }
431 else if (m_renderValid && pauseframe)
432 {
434 }
435 }
436}
437
439 MythCodecID myth_codec_id, const QSize &video_dim)
440{
441 QStringList list;
442 if (codec_is_std(myth_codec_id) || (codec_is_dxva2_hw(myth_codec_id) &&
443 !qEnvironmentVariableIsSet("NO_DXVA2")))
444 {
445 list += "direct3d";
446 }
447 return list;
448}
449
451 AVCodecContext **Context, const AVCodec ** Codec,
452 const QString &decoder, uint stream_type)
453{
454#if CONFIG_DXVA2
455 MythCodecID test_cid = (MythCodecID)(kCodec_MPEG1_DXVA2 + (stream_type - 1));
456 bool use_cpu = !codec_is_dxva2_hw(test_cid);
457 if ((decoder == "dxva2") && !qEnvironmentVariableIsSet("NO_DXVA2") && !use_cpu)
458 return test_cid;
459#endif
460 return (MythCodecID)(kCodec_MPEG1 + (stream_type - 1));
461}
462
464{
465#if CONFIG_DXVA2
466 QMutexLocker locker(&m_lock);
467 if (m_decoder)
469 QSize video_dim = GetVideoDim();
470 m_decoder = new DXVA2Decoder(NUM_DXVA2_BUFS, m_videoCodecID,
471 video_dim.width(), video_dim.height());
472 return (m_decoder && m_decoder->Init(m_render));
473#else
474 return false;
475#endif
476}
477
479{
480#if CONFIG_DXVA2
481 QMutexLocker locker(&m_lock);
482 delete m_decoder;
483 m_decoder = nullptr;
484#endif
485}
486
487/* vim: set expandtab tabstop=4 shiftwidth=4: */
AVFrame AVFrame
uint8_t * GetBuffer(bool &hardware_conv, uint &pitch)
bool UpdateVertices(const QRect &dvr, const QRect &vr, int alpha=255, bool video=false)
void ReleaseBuffer(void)
bool IsValid(void) const
bool Draw(void)
int Copy(AVFrame *To, const MythVideoFrame *From, unsigned char *Buffer, AVPixelFormat Fmt=AV_PIX_FMT_YUV420P)
Initialise AVFrame and copy contents of VideoFrame frame into it, performing any required conversion.
Definition: mythavutil.cpp:271
QWidget * m_widget
Definition: mythdisplay.h:95
virtual void FreeResources(void)
Definition: mythpainter.h:50
bool ClearBuffer(void)
void CopyFrame(void *surface, D3D9Image *img)
bool Test(bool &reset)
bool Create(QSize size, HWND window)
bool Present(HWND win)
QRect GetDisplayVideoRect(void) const
bool IsEmbedding(void) const
QSize GetVideoDim(void) const
void MoveResize(void)
performs all the calculations for video framing and any resizing.
QRect GetVideoRect(void) const
MythDisplay * m_display
QRect GetDisplayVisibleRect(void) const
float GetVideoAspect(void) const
void VideoAspectRatioChanged(float Aspect)
Calls SetVideoAspectRatio(float aspect), then calls MoveResize() to apply changes.
long long m_frameNumber
Definition: mythframe.h:128
std::chrono::milliseconds m_displayTimecode
Definition: mythframe.h:131
VideoFrameType m_type
Definition: mythframe.h:118
uint8_t * m_buffer
Definition: mythframe.h:119
Common code shared between GPU accelerated sub-classes (e.g. OpenGL)
MythCodecID m_videoCodecID
Definition: mythvideoout.h:98
VideoErrorState m_errorState
Definition: mythvideoout.h:102
long long m_framesPlayed
Definition: mythvideoout.h:103
bool IsErrored() const
MythAVCopy m_copyFrame
Definition: mythvideoout.h:104
virtual bool Init(QSize VideoDim, QSize VideoDispDim, float VideoAspect, QRect WindowRect, MythCodecID CodecID)
MythVideoProfilePtr m_videoProfile
Definition: mythvideoout.h:100
VideoBuffers m_videoBuffers
Definition: mythvideoout.h:101
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
static uint GetNumBuffers(int PixelFormat, int MaxReferenceFrames=16, bool Decoder=false)
bool CreateBuffers(VideoFrameType Type, const VideoFrameTypes *RenderFormats, QSize Size, uint NeedFree, uint NeedprebufferNormal, uint NeedPrebufferSmall, int MaxReferenceFrames=16)
void Reset(void)
Resets the class so that Init may be called again.
void DiscardFrames(bool NextFrameIsKeyFrame)
Mark all used frames as ready to be reused, this is for seek.
void Init(uint NumDecode, uint NeedFree, uint NeedprebufferNormal, uint NeedPrebufferSmall)
Creates buffers and sets various buffer management parameters.
MythVideoFrame * Head(BufferType Type)
void UpdateFrame(MythVideoFrame *frame, D3D9Image *img)
bool Init(QSize video_dim_buf, QSize video_dim_disp, float video_aspect, QRect win_rect, MythCodecID codec_id) override
D3D9Image * m_video
Definition: videoout_d3d.h:61
static void GetRenderOptions(RenderOptions &Options)
bool CreateDecoder(void)
void SetProfile(void)
MythRenderD3D9 * m_render
Definition: videoout_d3d.h:60
MythVideoFrame m_pauseFrame
Definition: videoout_d3d.h:57
static MythCodecID GetSupportedCodec(AVCodecContext **Context, const AVCodec **Codec, const QString &decoder, uint stream_type)
void PrepareFrame(MythVideoFrame *frame, FrameScanType scan) override
static QStringList GetAllowedRenderers(MythCodecID myth_codec_id, const QSize &video_dim)
void DestroyContext(void)
void EndFrame() override
void UpdatePauseFrame(std::chrono::milliseconds &disp_timecode, FrameScanType Scan=kScan_Progressive) override
bool InitBuffers(void)
bool SetupContext(void)
void RenderEnd() override
bool CreateBuffers(void)
VideoOutputD3D(MythMainWindow *MainWindow, MythRenderD3D9 *Render, MythD3D9Painter *Painter, MythDisplay *Display, const MythVideoProfilePtr &VideoProfile, QString &Profile)
void DeleteDecoder(void)
void TearDown(void)
bool InputChanged(QSize video_dim_buf, QSize video_dim_disp, float video_aspect, MythCodecID av_codec_id, bool &aspect_only, int reference_frames, bool force_change) override
Tells video output to discard decoded frames and wait for new ones.
void RenderFrame(MythVideoFrame *buffer, FrameScanType) override
void * m_pauseSurface
Definition: videoout_d3d.h:70
QRecursiveMutex m_lock
Definition: videoout_d3d.h:58
unsigned int uint
Definition: compat.h:60
static bool codec_is_std(MythCodecID id)
Definition: mythcodecid.h:296
MythCodecID
Definition: mythcodecid.h:14
@ kCodec_MPEG1_DXVA2
Definition: mythcodecid.h:104
@ kCodec_MPEG1
Definition: mythcodecid.h:24
static bool codec_is_dxva2_hw(MythCodecID id)
Definition: mythcodecid.h:331
static bool codec_is_dxva2(MythCodecID id)
Definition: mythcodecid.h:328
@ FMT_YV12
Definition: mythframe.h:23
@ FMT_DXVA2
Definition: mythframe.h:58
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythPainter * GetMythPainter(void)
std::shared_ptr< MythVideoProfile > MythVideoProfilePtr
Definition: mythvideogpu.h:18
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
def scan(profile, smoonURL, gate)
Definition: scan.py:54
QStringList * decoders
QStringList * renderers
QMap< QString, QStringList > * safe_renderers
QMap< QString, uint > * priorities
@ kVideoBuffer_used
Definition: videobuffers.h:30
#define LOC
const int kPrebufferFramesSmall
const int kPrebufferFramesNormal
const int kNumBuffers
const int kNeedFreeFrames
@ kError_Unknown
FrameScanType
Definition: videoouttypes.h:95