MythTV  master
videoout_nullvdpau.cpp
Go to the documentation of this file.
1 #include "videoout_nullvdpau.h"
2 #define LOC QString("NullVDPAU: ")
3 
4 #define MIN_REFERENCE_FRAMES 2
5 #define MAX_REFERENCE_FRAMES 16
6 #define MIN_PROCESS_BUFFER 6
7 
9 {
10  opts.renderers->append("nullvdpau");
11  (*opts.osds)["nullvdpau"].append("dummy");
12  QStringList dummy(QString("dummy"));
13  opts.deints->insert("nullvdpau", dummy);
14  if (opts.decoders->contains("vdpau"))
15  (*opts.safe_renderers)["vdpau"].append("nullvdpau");
16  if (opts.decoders->contains("ffmpeg"))
17  (*opts.safe_renderers)["ffmpeg"].append("nullvdpau");
18  if (opts.decoders->contains("crystalhd"))
19  (*opts.safe_renderers)["crystalhd"].append("nullvdpau");
20  (*opts.safe_renderers)["dummy"].append("nullvdpau");
21  (*opts.safe_renderers)["nuppel"].append("nullvdpau");
22 
23  opts.priorities->insert("nullvdpau", 20);
24 }
25 
27  : m_render(nullptr), m_lock(QMutex::Recursive), m_decoder(0), m_pix_fmt(-1),
28  m_decoder_buffer_size(MAX_REFERENCE_FRAMES),
29  m_checked_surface_ownership(false), m_shadowBuffers(nullptr),
30  m_surfaceSize(QSize(0,0))
31 {
32  memset(&m_context, 0, sizeof(AVVDPAUContext));
33 }
34 
36 {
37  QMutexLocker locker(&m_lock);
38  TearDown();
39 }
40 
42 {
43  DeleteBuffers();
45  DeleteRender();
46 }
47 
48 bool VideoOutputNullVDPAU::Init(const QSize &video_dim_buf,
49  const QSize &video_dim_disp,
50  float aspect,
51  WId winid, const QRect &win_rect,
52  MythCodecID codec_id)
53 {
54  QMutexLocker locker(&m_lock);
55  bool ok = VideoOutput::Init(video_dim_buf, video_dim_disp,
56  aspect, winid, win_rect, codec_id);
58  return false;
59 
60  if (db_vdisp_profile)
61  db_vdisp_profile->SetVideoRenderer("nullvdpau");
62  if (ok) ok = InitRender();
63  if (ok) ok = InitBuffers();
64  if (ok) ok = InitShadowBuffers();
65  if (!ok)
66  return false;
67 
68  LOG(VB_PLAYBACK, LOG_INFO, LOC +
69  "Created VDPAU context with GPU decoding)");
70  return ok;
71 }
72 
74 {
75  QMutexLocker locker(&m_lock);
76 
77  m_render = new MythRenderVDPAU();
79  return true;
80  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise VDPAU");
81  return false;
82 }
83 
85 {
86  QMutexLocker locker(&m_lock);
87  if (m_render)
88  {
89  if (m_decoder)
91  m_render->DecrRef();
92  }
93 
94  m_decoder = 0;
95  m_render = nullptr;
96  m_pix_fmt = -1;
97 }
98 
100 {
101  QMutexLocker locker(&m_lock);
103  return false;
104 
106  const QSize video_dim = window.GetActualVideoDim();
107  vbuffers.Init(buffer_size, false, 2, 1, 4, 1);
108  bool ok = CreateVideoSurfaces(buffer_size);
109  if (ok)
110  {
111  for (int i = 0; i < m_video_surfaces.size(); i++)
112  ok &= vbuffers.CreateBuffer(video_dim.width(),
113  video_dim.height(), i,
115  FMT_VDPAU);
116  }
117 
118  if (!ok)
119  {
120  DeleteBuffers();
121  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to create VDPAU buffers");
122  return false;
123  }
124 
125  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Created VDPAU buffers");
126  return ok;
127 }
128 
130 {
131  QMutexLocker locker(&m_lock);
134  vbuffers.Reset();
137 }
138 
140 {
141  QMutexLocker locker(&m_lock);
143  return false;
144 
146 
148  if ((vbuffers.Size() != buffer_size) ||
149  (vbuffers.Size() != (uint)m_video_surfaces.size()))
150  {
151  LOG(VB_GENERAL, LOG_ERR, LOC + "Number of GPU buffers is wrong.");
152  return false;
153  }
154 
157  if (!m_shadowBuffers)
158  return false;
159 
160  m_shadowBuffers->Init(buffer_size, false, 2, 1, 4, 1);
162  m_surfaceSize.width(),
163  m_surfaceSize.height()))
164  {
165  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create shadow buffers.");
167  return false;
168  }
169 
170  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 CPU buffers (%2x%3)")
171  .arg(buffer_size).arg(m_surfaceSize.width())
172  .arg(m_surfaceSize.height()));
173  return true;
174 }
175 
177 {
178  QMutexLocker locker(&m_lock);
179  if (!m_shadowBuffers)
180  return;
181 
184  delete m_shadowBuffers;
185  m_shadowBuffers = nullptr;
186 }
187 
189 {
190  if (!m_render || num < 1)
191  return false;
192 
193  bool ret = true;
194  const QSize size = window.GetActualVideoDim();
195  for (uint i = 0; i < num; i++)
196  {
198  if (tmp)
199  {
200  m_video_surfaces.push_back(tmp);
202  }
203  else
204  {
205  ret = false;
206  break;
207  }
208  }
209  return ret;
210 }
211 
213 {
214  if (!m_render || m_video_surfaces.empty())
215  return;
216 
217  for (int i = 0; i < m_video_surfaces.size(); i++)
219  m_video_surfaces.clear();
220 }
221 
223 {
224  if (!m_render)
225  return;
226 
227  QMutexLocker locker(&m_lock);
228  QVector<uint>::iterator it;
229  for (it = m_video_surfaces.begin(); it != m_video_surfaces.end(); ++it)
232 }
233 
234 void VideoOutputNullVDPAU::DrawSlice(VideoFrame *frame, int x, int y, int w, int h)
235 {
236  (void)x;
237  (void)y;
238  (void)w;
239  (void)h;
240 
241  if (m_render && m_render->IsErrored())
243 
244  if (IsErrored())
245  {
246  LOG(VB_GENERAL, LOG_ERR, LOC + QString("IsErrored() in DrawSlice"));
247  return;
248  }
249 
251  return;
252 
255 
256  struct vdpau_render_state *render =
257  (struct vdpau_render_state *)frame->priv[0];
258  const VdpPictureInfo *info = (const VdpPictureInfo *)frame->priv[1];
259 
260  if (frame->pix_fmt != m_pix_fmt)
261  {
262  if (m_decoder)
263  {
264  LOG(VB_GENERAL, LOG_ERR, LOC + "Picture format has changed.");
266  return;
267  }
268 
269  uint max_refs = MIN_REFERENCE_FRAMES;
271  {
272  max_refs = ((VdpPictureInfoH264*)info)->num_ref_frames;
273  if (max_refs < 1 || max_refs > MAX_REFERENCE_FRAMES)
274  {
275  uint32_t round_width = (frame->width + 15) & ~15;
276  uint32_t round_height = (frame->height + 15) & ~15;
277  uint32_t surf_size = (round_width * round_height * 3) / 2;
278  max_refs = (12 * 1024 * 1024) / surf_size;
279  }
280  if (max_refs > MAX_REFERENCE_FRAMES)
281  max_refs = MAX_REFERENCE_FRAMES;
282 
283  // Add extra buffers as necessary
284  int needed = max_refs - m_decoder_buffer_size;
285  if (needed > 0)
286  {
287  QMutexLocker locker(&m_lock);
288  const QSize size = window.GetActualVideoDim();
289  uint created = 0;
290  for (int i = 0; i < needed; i++)
291  {
293  if (tmp)
294  {
295  m_video_surfaces.push_back(tmp);
297  if (vbuffers.AddBuffer(size.width(), size.height(),
299  FMT_VDPAU))
300  {
301  created++;
303  m_surfaceSize.height(),
304  nullptr,
305  FMT_YV12);
306  }
307  }
308  }
309  m_decoder_buffer_size += created;
310  LOG(VB_GENERAL, LOG_INFO, LOC +
311  QString("Added %1 new buffers. New buffer size %2 "
312  "(%3 decode and %4 process)")
313  .arg(created).arg(vbuffers.Size())
315  .arg(MIN_PROCESS_BUFFER));
316  }
317  }
318 
319  VdpDecoderProfile vdp_decoder_profile;
320  switch (video_codec_id)
321  {
322  case kCodec_MPEG1_VDPAU:
323  vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
324  break;
325  case kCodec_MPEG2_VDPAU:
326  vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
327  break;
328  case kCodec_MPEG4_VDPAU:
329  vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
330  break;
331  case kCodec_H264_VDPAU:
332  vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
333  break;
334  case kCodec_WMV3_VDPAU:
335  vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
336  break;
337  case kCodec_VC1_VDPAU:
338  vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
339  break;
340  default:
341  LOG(VB_GENERAL, LOG_ERR, LOC +
342  "Codec is not supported.");
344  return;
345  }
346 
348  vdp_decoder_profile, max_refs);
349  if (m_decoder)
350  {
351  m_pix_fmt = frame->pix_fmt;
352  LOG(VB_PLAYBACK, LOG_INFO, LOC +
353  QString("Created VDPAU decoder (%1 ref frames)")
354  .arg(max_refs));
355  }
356  else
357  {
358  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create decoder.");
360  return;
361  }
362  }
363  else if (!m_decoder)
364  {
365  LOG(VB_GENERAL, LOG_ERR, LOC +
366  "Pix format already set but no VDPAU decoder.");
368  return;
369  }
370 
371  m_render->Decode(m_decoder, render, info);
372 }
373 
375 {
376  QMutexLocker locker(&m_lock);
377  LOG(VB_PLAYBACK, LOG_INFO, LOC + "ClearAfterSeek()");
378  DiscardFrames(false);
379 }
380 
381 // Always returns the CPU version of a frame
383 {
384  if (!BufferSizeCheck())
385  return nullptr;
386 
388  for (uint i = 0; i < vbuffers.Size(); i++)
389  if (vbuffers.At(i) == gpu)
390  return m_shadowBuffers->At(i);
391  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find frame.");
392  return nullptr;
393 }
394 
395 // Always returns the CPU version of a frame
397 {
398  if (!BufferSizeCheck())
399  return nullptr;
400 
402  for (uint i = 0; i < vbuffers.Size(); i++)
403  if (vbuffers.At(i) == gpu)
404  return m_shadowBuffers->At(i);
405  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find frame.");
406  return nullptr;
407 }
408 
409 // Should work with either the CPU or GPU version of a frame
411 {
412  if (!frame || !BufferSizeCheck())
413  return;
414 
415  // is this a CPU frame
416  for (uint i = 0; i < m_shadowBuffers->Size(); i++)
417  {
418  if (m_shadowBuffers->At(i) == frame)
419  {
420  frame = vbuffers.At(i);
421  break;
422  }
423  }
424 
425  // is this a GPU frame
426  for (uint i = 0; i < vbuffers.Size(); i++)
427  {
428  if (vbuffers.At(i) == frame)
429  {
430  m_lock.lock();
432  m_lock.unlock();
433  return;
434  }
435  }
436 }
437 
438 // Should work with either the CPU or GPU version of a frame
440 {
441  if (!frame || !BufferSizeCheck())
442  return;
443 
444  // is this a CPU frame
445  for (uint i = 0; i < m_shadowBuffers->Size(); i++)
446  {
447  if (m_shadowBuffers->At(i) == frame)
448  {
449  frame = vbuffers.At(i);
450  break;
451  }
452  }
453 
454  // is this a GPU frame
455  for (uint i = 0; i < vbuffers.Size(); i++)
456  {
457  if (vbuffers.At(i) == frame)
458  {
459  m_lock.lock();
460  if (vbuffers.Contains(kVideoBuffer_used, frame))
461  DiscardFrame(frame);
463  m_lock.unlock();
464  return;
465  }
466  }
467 }
468 
470 {
471  if (!frame)
472  return;
473 
474  if ((frame->codec == FMT_VDPAU) && m_render)
475  {
476  if (BufferSizeCheck())
477  {
478  uint surface = 0;
479  struct vdpau_render_state *render =
480  (struct vdpau_render_state *)frame->buf;
481  if (render)
483  // assume a direct mapping of GPU to CPU buffers
484  for (uint i = 0; i < vbuffers.Size(); i++)
485  {
486  if (vbuffers.At(i)->buf == frame->buf)
487  {
488  VideoFrame *vf = m_shadowBuffers->At(i);
489  uint32_t pitches[] = {
490  (uint32_t)vf->pitches[0],
491  (uint32_t)vf->pitches[2],
492  (uint32_t)vf->pitches[1] };
493  void* const planes[] = {
494  vf->buf,
495  vf->buf + vf->offsets[2],
496  vf->buf + vf->offsets[1] };
497  if (!m_render->DownloadYUVFrame(surface, planes, pitches))
498  {
499  LOG(VB_GENERAL, LOG_ERR, LOC +
500  "Failed to get frame from GPU.");
501  }
502  vf->aspect = frame->aspect;
503  vf->disp_timecode = frame->disp_timecode;
504  vf->dummy = frame->dummy;
505  vf->frameNumber = frame->frameNumber;
506  vf->interlaced_frame = frame->interlaced_frame;
507  vf->timecode = frame->timecode;
508  vf->repeat_pict = frame->repeat_pict;
509  vf->top_field_first = frame->top_field_first;
510  }
511  }
512  }
513  }
514 
516 }
517 
518 bool VideoOutputNullVDPAU::InputChanged(const QSize &video_dim_buf,
519  const QSize &video_dim_disp,
520  float aspect,
521  MythCodecID av_codec_id,
522  void */*codec_private*/,
523  bool &aspect_only)
524 {
525  LOG(VB_PLAYBACK, LOG_INFO, LOC +
526  QString("InputChanged(%1,%2,%3) '%4'->'%5'")
527  .arg(video_dim_disp.width()).arg(video_dim_disp.height())
528  .arg(aspect)
529  .arg(toString(video_codec_id)).arg(toString(av_codec_id)));
530 
531  QMutexLocker locker(&m_lock);
532 
533  bool cid_changed = (video_codec_id != av_codec_id);
534  bool res_changed = video_dim_disp != window.GetActualVideoDim();
535 
536  if (!res_changed && !cid_changed)
537  {
538  aspect_only = true;
539  return true;
540  }
541 
542  TearDown();
543  QRect disp = window.GetDisplayVisibleRect();
544  if (Init(video_dim_buf, video_dim_disp,
545  aspect, 0, disp, av_codec_id))
546  {
547  return true;
548  }
549 
550  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to re-initialise video output.");
552 
553  return false;
554 }
555 
557 {
558  QStringList list;
559  if (codec_is_vdpau_hw(myth_codec_id) && !getenv("NO_VDPAU"))
560  list += "nullvdpau";
561  return list;
562 }
563 
564 void VideoOutputNullVDPAU::DiscardFrames(bool next_frame_keyframe)
565 {
566  QMutexLocker locker(&m_lock);
567  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DiscardFrames(%1)")
568  .arg(next_frame_keyframe));
570  vbuffers.DiscardFrames(next_frame_keyframe);
571  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DiscardFrames() 3: %1 -- done()")
572  .arg(vbuffers.GetStatus()));
573 }
574 
576 {
577  QMutexLocker locker(&m_lock);
580  while (it != vbuffers.end(kVideoBuffer_displayed))
581  {
582  VideoFrame* frame = *it;
584  {
585  LOG(VB_PLAYBACK, LOG_INFO, LOC +
586  QString("Frame %1 is in use by avlib and so is "
587  "being held for later discarding.")
588  .arg(DebugString(frame, true)));
589  }
590  else
591  {
593  vbuffers.end_lock();
595  continue;
596  }
597  ++it;
598  }
599  vbuffers.end_lock();
600 }
601 
603 {
604  if (vbuffers.Size() != m_shadowBuffers->Size())
605  {
606  LOG(VB_GENERAL, LOG_ERR, LOC + "Number of GPU buffers not the "
607  "same as number of CPU buffers.");
608  return false;
609  }
610  return true;
611 }
612 
613 void* VideoOutputNullVDPAU::GetDecoderContext(unsigned char* /*buf*/, uint8_t*& /*id*/)
614 {
615  return &m_context;
616 }
int pitches[3]
Y, U, & V pitches.
Definition: mythframe.h:63
void ClearVideoSurface(uint id)
uint CreateVideoSurface(const QSize &size, VdpChromaType type=VDP_CHROMA_TYPE_420, uint existing=0)
#define MAX_REFERENCE_FRAMES
virtual void ReleaseFrame(VideoFrame *frame)
Releases a frame from the ready for decoding queue onto the queue of frames ready for display.
Definition: videooutbase.h:212
void Reset(void)
Resets the class so that Init may be called again.
void SetVideoRenderer(const QString &video_renderer)
void DoneDisplayingFrame(VideoFrame *frame)
Removes frame from used queue and adds it to the available list.
const QString & DebugString(const VideoFrame *frame, bool short_str)
MythRenderVDPAU * m_render
#define MIN_REFERENCE_FRAMES
VideoFrame * GetLastShownFrame(void)
Definition: videobuffers.h:106
uint GetSurfaceOwner(VdpVideoSurface surface)
QString GetStatus(int n=-1) const
QString toString(MarkTypes type)
void Decode(uint id, struct vdpau_render_state *render)
MythCodecID
Definition: mythcodecid.h:10
void SafeEnqueue(BufferType, VideoFrame *frame)
int repeat_pict
Definition: mythframe.h:59
void DrawSlice(VideoFrame *frame, int x, int y, int w, int h) override
Informs video output of new data for frame, used for hardware accelerated decoding.
void DestroyVideoSurface(uint id)
bool CreateVideoSurfaces(uint num)
virtual bool Init(const QSize &video_dim_buf, const QSize &video_dim_disp, float aspect, WId winid, const QRect &win_rect, MythCodecID codec_id)
Performs most of the initialization for VideoOutput.
VideoFrame * At(uint i)
Definition: videobuffers.h:90
void DestroyDecoder(uint id)
unsigned int uint
Definition: compat.h:140
long long timecode
Definition: mythframe.h:49
unsigned char * priv[4]
random empty storage
Definition: mythframe.h:52
VideoDisplayProfile * db_vdisp_profile
Definition: videooutbase.h:330
uint Size(BufferType type) const
static guint32 * tmp
Definition: goom_core.c:35
QVector< uint > m_video_surfaces
bool CreateBuffers(VideoFrameType type, int width, int height, vector< unsigned char * > bufs, vector< YUVInfo > yuvinfo)
int offsets[3]
Y, U, & V offsets.
Definition: mythframe.h:64
void CheckFrameStates(void) override
frame_queue_t::iterator end(BufferType)
VideoFrame * GetLastShownFrame(void) override
Returns frame from the head of the ready to be displayed queue, if StartDisplayingFrame has been call...
void ChangeVideoSurfaceOwner(uint id)
void DoneDisplayingFrame(VideoFrame *frame) override
Releases frame returned from GetLastShownFrame() onto the queue of frames ready for decoding onto.
static void GetRenderOptions(render_opts &opts)
float aspect
Definition: mythframe.h:43
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
VideoBuffers vbuffers
VideoBuffers instance used to track video output buffers.
Definition: videooutbase.h:357
deque< VideoFrame * >::iterator iterator
Definition: mythdeque.h:44
int height
Definition: mythframe.h:42
MythCodecID video_codec_id
Definition: videooutbase.h:329
VideoErrorState errorState
Definition: videooutbase.h:360
union AVVDPAUPictureInfo info
picture parameter information for all supported codecs
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
bool CreateBuffer(int width, int height, uint num, void *data, VideoFrameType fmt)
VideoBuffers * m_shadowBuffers
void DiscardFrame(VideoFrame *) override
Releases frame from any queue onto the queue of frames ready for decoding onto.
long long frameNumber
Definition: mythframe.h:48
void end_lock()
Definition: videobuffers.h:100
uint AddBuffer(int width, int height, void *data, VideoFrameType fmt)
int pix_fmt
Definition: mythframe.h:66
void * GetDecoderContext(unsigned char *buf, uint8_t *&id) override
int top_field_first
1 if top field is first.
Definition: mythframe.h:58
bool Init(const QSize &video_dim_buf, const QSize &video_dim_disp, float aspect, WId winid, const QRect &win_rect, MythCodecID codec_id) override
Performs most of the initialization for VideoOutput.
void * GetRender(uint id)
bool IsErrored() const
Returns true if a fatal error has been encountered.
Definition: videooutbase.h:179
bool DownloadYUVFrame(uint id, void *const planes[3], uint32_t pitches[3])
uint CreateDecoder(const QSize &size, VdpDecoderProfile profile, uint references, uint existing=0)
void DiscardFrames(bool next_frame_keyframe) override
Releases all frames not being actively displayed from any queue onto the queue of frames ready for de...
bool CreateDecodeOnly(void)
QSize GetActualVideoDim(void) const
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
int64_t disp_timecode
Definition: mythframe.h:50
bool InputChanged(const QSize &video_dim_buf, const QSize &video_dim_disp, float aspect, MythCodecID av_codec_id, void *codec_private, bool &aspect_only) override
Tells video output to discard decoded frames and wait for new ones.
void ReleaseFrame(VideoFrame *frame) override
Releases a frame from the ready for decoding queue onto the queue of frames ready for display.
bool IsErrored(void) const
VideoFrame * GetLastDecodedFrame(void)
Definition: videobuffers.h:105
QRect GetDisplayVisibleRect(void) const
#define codec_is_vdpau_hw(id)
Definition: mythcodecid.h:131
bool Contains(BufferType type, VideoFrame *) const
static QStringList GetAllowedRenderers(MythCodecID myth_codec_id)
QSize GetSurfaceSize(uint id)
VideoOutWindow window
Definition: videooutbase.h:320
#define LOC
int interlaced_frame
1 if interlaced.
Definition: mythframe.h:57
void ClearAfterSeek(void) override
Tells video output to toss decoded buffers due to a seek.
This class creates tracks the state of the buffers used by various VideoOutput derived classes.
Definition: videobuffers.h:61
void Init(uint numdecode, bool extra_for_pause, uint need_free, uint needprebuffer_normal, uint needprebuffer_small, uint keepprebuffer)
Creates buffers and sets various buffer management parameters.
frame_queue_t::iterator begin_lock(BufferType)
void DiscardFrames(bool next_frame_keyframe)
Mark all used frames as ready to be reused, this is for seek.
unsigned char * buf
Definition: mythframe.h:39
VideoFrame * GetLastDecodedFrame(void) override
void DeleteBuffers(void)
VdpVideoSurface surface
Used as rendered surface, never changed.
#define MIN_PROCESS_BUFFER
VideoFrameType codec
Definition: mythframe.h:38