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 "libmythbase/mythconfig.h"
13
14#include "fourcc.h"
15#include "mythavutil.h"
16#include "mythplayer.h"
17#include "mythvideoprofile.h"
18#include "osd.h"
19#include "tv.h"
20#include "videoout_d3d.h"
21
22extern "C" {
23#include "libavutil/imgutils.h"
24}
25
26#undef UNICODE
27
28const int kNumBuffers = 31;
29const int kNeedFreeFrames = 1;
32
33#define LOC QString("VideoOutputD3D: ")
34
36{
37 Options.renderers->append("direct3d");
38 (*Options.safe_renderers)["dummy"].append("direct3d");
39 (*Options.safe_renderers)["nuppel"].append("direct3d");
40 if (Options.decoders->contains("ffmpeg"))
41 (*Options.safe_renderers)["ffmpeg"].append("direct3d");
42 Options.priorities->insert("direct3d", 70);
43
44#if CONFIG_DXVA2
45 if (Options.decoders->contains("dxva2"))
46 (*Options.safe_renderers)["dxva2"].append("direct3d");
47#endif
48}
49
51 MythD3D9Painter* Painter, MythDisplay* Display,
52 const MythVideoProfilePtr& VideoProfile, QString& Profile)
53 : MythVideoOutputGPU(MainWindow, Render, Painter, Display, VideoProfile, Profile)
54{
55 m_pauseFrame.m_buffer = nullptr;
56}
57
59{
60 TearDown();
61}
62
64{
65 QMutexLocker locker(&m_lock);
68 m_videoBuffers.DeleteBuffers();
70 {
71 delete [] m_pauseFrame.m_buffer;
72 m_pauseFrame.m_buffer = nullptr;
73 }
74
77}
78
80{
81 QMutexLocker locker(&m_lock);
82 m_renderValid = false;
83 m_renderReset = false;
84
85 if (m_video)
86 {
87 delete m_video;
88 m_video = nullptr;
89 }
90
91 if (m_render)
92 {
94 m_render = nullptr;
95 }
96}
97
98bool VideoOutputD3D::InputChanged(QSize video_dim_buf,
99 QSize video_dim_disp,
100 float video_aspect,
101 MythCodecID av_codec_id,
102 bool &aspect_only,
103 int [[maybe_unused]] reference_frames,
104 bool force_change)
105{
106 QMutexLocker locker(&m_lock);
107
108 QSize cursize = GetVideoDim();
109
110 LOG(VB_PLAYBACK, LOG_INFO, LOC +
111 QString("InputChanged from %1: %2x%3 aspect %4 to %5: %6x%7 aspect %9")
112 .arg(toString(m_videoCodecID)).arg(cursize.width())
113 .arg(cursize.height()).arg(GetVideoAspect())
114 .arg(toString(av_codec_id)).arg(video_dim_disp.width())
115 .arg(video_dim_disp.height()).arg(video_aspect));
116
117
118 bool cid_changed = (m_videoCodecID != av_codec_id);
119 bool res_changed = video_dim_disp != cursize;
120 bool asp_changed = video_aspect != GetVideoAspect();
121
122 if (!res_changed && !cid_changed && !force_change)
123 {
124 if (asp_changed)
125 {
126 aspect_only = true;
127 VideoAspectRatioChanged(video_aspect);
128 MoveResize();
129 }
130 return true;
131 }
132
133 TearDown();
134 QRect disp = GetDisplayVisibleRect();
135 if (Init(video_dim_buf, video_dim_disp,
136 video_aspect, disp, av_codec_id))
137 {
138 return true;
139 }
140
141 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to re-initialise video output.");
143
144 return false;
145}
146
148{
149 QMutexLocker locker(&m_lock);
151 QSize size = GetVideoDim();
152 m_render = new MythRenderD3D9();
154 m_hWnd)))
155 return false;
156
157 m_video = new D3D9Image(m_render, size, true);
158 if (!(m_video && m_video->IsValid()))
159 return false;
160
161 LOG(VB_PLAYBACK, LOG_INFO, LOC +
162 "Direct3D device successfully initialized.");
163 m_renderValid = true;
164 return true;
165}
166
167bool VideoOutputD3D::Init(QSize video_dim_buf,
168 QSize video_dim_disp,
169 float video_aspect,
170 QRect win_rect,MythCodecID codec_id)
171{
172 MythPainter *painter = GetMythPainter();
173 if (painter)
174 painter->FreeResources();
175
176 QMutexLocker locker(&m_lock);
177 m_hWnd = (HWND)m_display->m_widget->winId();
178
179 MythVideoOutput::Init(video_dim_buf, video_dim_disp,
180 video_aspect, win_rect, codec_id);
181
182 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Init with codec: %1")
183 .arg(toString(codec_id)));
184 SetProfile();
185
186 bool success = true;
187 success &= SetupContext();
188
190 {
191 if (!CreateDecoder())
192 return false;
193 }
194
195 success &= CreateBuffers();
196 success &= InitBuffers();
197
198 MoveResize();
199
200 if (!success)
201 TearDown();
202
203 return success;
204}
205
207{
208 if (m_videoProfile)
209 m_videoProfile->SetVideoRenderer("direct3d");
210}
211
213{
215 {
217 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 empty DXVA2 buffers.")
219 return true;
220 }
221
224 return true;
225
226}
227
229{
230#if CONFIG_DXVA2
231 if ((codec_is_dxva2(m_videoCodecID)) && m_decoder)
232 {
233 QMutexLocker locker(&m_lock);
234 const QSize video_dim = GetVideoDim();
235 bool ok = true;
236 for (int i = 0; i < NUM_DXVA2_BUFS; i++)
237 {
238 ok &= vbuffers.CreateBuffer(video_dim.width(),
239 video_dim.height(), i,
240 m_decoder->GetSurface(i), FMT_DXVA2);
241 }
242 if (ok)
243 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Initialised DXVA2 buffers.");
244 return ok;
245 }
246#endif
248 GetVideoDim().width(),
249 GetVideoDim().height());
250}
251
254{
255 if (IsErrored())
256 {
257 LOG(VB_GENERAL, LOG_ERR, LOC +
258 "RenderFrame() called while IsErrored is true.");
259 return;
260 }
261
262 if (!buffer && codec_is_std(m_videoCodecID))
263 buffer = m_videoBuffers.GetScratchFrame();
264
265 bool dummy = false;
266 if (buffer)
267 {
268 dummy = buffer->m_dummy;
269 m_framesPlayed = buffer->m_frameNumber + 1;
270 }
271
272 if (!m_render || !m_video)
273 return;
274
276 if (m_renderValid)
277 {
278 QRect dvr = GetDisplayVideoRect();
279 bool ok = m_render->ClearBuffer();
280 if (ok && !dummy)
282 255, true);
283
284 if (ok)
285 {
286 ok = m_render->Begin();
287 if (ok)
288 {
289 if (!dummy)
290 m_video->Draw();
291 }
292 }
293
294 }
295}
296
298{
299 if (!m_renderValid)
300 return;
301 m_render->End();
302}
303
305{
306 if (IsErrored())
307 {
308 LOG(VB_GENERAL, LOG_ERR, LOC +
309 "Show() called while IsErrored is true.");
310 return;
311 }
312
313 if (!m_render)
314 return;
315
317 if (m_renderValid)
318 m_render->Present(IsEmbedding() ? m_hEmbedWnd : nullptr);
319}
320
321void VideoOutputD3D::UpdatePauseFrame(std::chrono::milliseconds &disp_timecode, FrameScanType)
322{
323 QMutexLocker locker(&m_lock);
325
327 {
328 if (!used_frame)
329 used_frame = m_videoBuffers.GetScratchFrame();
330 CopyFrame(&m_pauseFrame, used_frame);
331 disp_timecode = m_pauseFrame.m_displayTimecode;
332 }
334 {
335 if (used_frame)
336 {
337 m_pauseSurface = used_frame->m_buffer;
338 disp_timecode = used_frame->m_displayTimecode;
339 }
340 else
341 LOG(VB_PLAYBACK, LOG_WARNING, LOC + "Failed to update pause frame");
342 }
343}
344
346{
348 return;
349
350 // TODO - add a size check
351 bool hardware_conv = false;
352 uint pitch = 0;
353 uint8_t *buf = img->GetBuffer(hardware_conv, pitch);
354 if (buf && hardware_conv)
355 {
356 copybuffer(buf, frame, pitch);
357 }
358 else if (buf && !hardware_conv)
359 {
360 AVFrame image_out;
361 av_image_fill_arrays(image_out.data, image_out.linesize,
362 (uint8_t*)buf,
363 AV_PIX_FMT_RGB32, frame->m_width, frame->m_height, IMAGE_ALIGN);
364 image_out.linesize[0] = pitch;
365 m_copyFrame.Copy(&image_out, frame,(uint8_t*)buf, AV_PIX_FMT_RGB32);
366 }
367 img->ReleaseBuffer();
368}
369
371{
372 if (!m_render || !m_video)
373 return;
374
375 QMutexLocker locker(&m_lock);
376 if (IsErrored())
377 {
378 LOG(VB_GENERAL, LOG_ERR, LOC +
379 "ProcessFrame() called while IsErrored is true.");
380 return;
381 }
382
383 bool gpu = codec_is_dxva2(m_videoCodecID);
384
385 if (gpu && frame && frame->m_type != FMT_DXVA2)
386 {
387 LOG(VB_GENERAL, LOG_ERR, LOC + "Wrong frame format");
388 return;
389 }
390
391 bool dummy = false;
392 bool pauseframe = false;
393 if (!frame)
394 {
395 if (!gpu)
396 {
397 frame = m_videoBuffers.GetScratchFrame();
398 CopyFrame(m_videoBuffers.GetScratchFrame(), &m_pauseFrame);
399 }
400 pauseframe = true;
401 }
402
403 if (frame)
404 dummy = frame->m_dummy;
405
406 // Test the device
408 if (m_renderReset)
409 SetupContext();
410
411 // Update a software decoded frame
412 if (m_renderValid && !gpu && !dummy)
413 UpdateFrame(frame, m_video);
414
415 // Update a GPU decoded frame
416 if (m_renderValid && gpu && !dummy)
417 {
419 if (m_renderReset)
421
422 if (m_renderValid && frame)
423 {
425 }
426 else if (m_renderValid && pauseframe)
427 {
429 }
430 }
431}
432
434 MythCodecID myth_codec_id, const QSize &video_dim)
435{
436 QStringList list;
437 if (codec_is_std(myth_codec_id) || (codec_is_dxva2_hw(myth_codec_id) &&
438 !getenv("NO_DXVA2")))
439 {
440 list += "direct3d";
441 }
442 return list;
443}
444
446 AVCodecContext **Context, const AVCodec ** Codec,
447 const QString &decoder, uint stream_type)
448{
449#if CONFIG_DXVA2
450 MythCodecID test_cid = (MythCodecID)(kCodec_MPEG1_DXVA2 + (stream_type - 1));
451 bool use_cpu = !codec_is_dxva2_hw(test_cid);
452 if ((decoder == "dxva2") && !getenv("NO_DXVA2") && !use_cpu)
453 return test_cid;
454#endif
455 return (MythCodecID)(kCodec_MPEG1 + (stream_type - 1));
456}
457
459{
460#if CONFIG_DXVA2
461 QMutexLocker locker(&m_lock);
462 if (m_decoder)
464 QSize video_dim = GetVideoDim();
465 m_decoder = new DXVA2Decoder(NUM_DXVA2_BUFS, m_videoCodecID,
466 video_dim.width(), video_dim.height());
467 return (m_decoder && m_decoder->Init(m_render));
468#else
469 return false;
470#endif
471}
472
474{
475#if CONFIG_DXVA2
476 QMutexLocker locker(&m_lock);
477 delete m_decoder;
478 m_decoder = nullptr;
479#endif
480}
481
482/* 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:270
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