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