MythTV  master
mythvdpaucontext.cpp
Go to the documentation of this file.
1 // MythTV other libs
3 
4 // MythTV
5 #include "avformatdecoder.h"
8 #include "mythplayerui.h"
10 
11 // FFmpeg
12 extern "C" {
13 #include "libavutil/hwcontext_vdpau.h"
14 #include "libavutil/pixdesc.h"
15 #include "libavcodec/vdpau.h"
16 }
17 
18 #define LOC QString("VDPAUDec: ")
19 
27  : MythCodecContext(Parent, CodecID)
28 {
29 }
30 
32 int MythVDPAUContext::InitialiseContext(AVCodecContext* Context)
33 {
34  if (!gCoreContext->IsUIThread() || !Context)
35  return -1;
36 
37  // The interop must have a reference to the ui player so it can be deleted
38  // from the main thread.
39  auto * player = GetPlayerUI(Context);
40  if (!player)
41  return -1;
42 
43  // Retrieve OpenGL render context
44  auto * render = dynamic_cast<MythRenderOpenGL*>(player->GetRender());
45  if (!render)
46  return -1;
47  OpenGLLocker locker(render);
48 
49  // Create interop
50  auto vdpauid = static_cast<MythCodecID>(kCodec_MPEG1_VDPAU + (mpeg_version(Context->codec_id) - 1));
51  auto * interop = MythVDPAUInterop::CreateVDPAU(player, render, vdpauid);
52  if (!interop)
53  return -1;
54 
55  // Allocate the device context
56  auto * hwdeviceref = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VDPAU, interop);
57  if (!hwdeviceref)
58  return -1;
59 
60  auto * hwdevicecontext = reinterpret_cast<AVHWDeviceContext*>(hwdeviceref->data);
61  if (!hwdevicecontext || !hwdevicecontext->hwctx)
62  return -1;
63 
64  // Initialise device context
65  if (av_hwdevice_ctx_init(hwdeviceref) < 0)
66  {
67  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise device context");
68  av_buffer_unref(&hwdeviceref);
69  interop->DecrRef();
70  return -1;
71  }
72 
73  // Allocate the hardware frames context
74  Context->hw_frames_ctx = av_hwframe_ctx_alloc(hwdeviceref);
75  if (!Context->hw_frames_ctx)
76  {
77  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VDPAU hardware frames context");
78  av_buffer_unref(&hwdeviceref);
79  interop->DecrRef();
80  return -1;
81  }
82 
83  // Add our interop class and set the callback for its release
84  auto * hwframesctx = reinterpret_cast<AVHWFramesContext*>(Context->hw_frames_ctx->data);
85  hwframesctx->user_opaque = interop;
86  hwframesctx->free = &MythCodecContext::FramesContextFinished;
87 
88  // Initialise frames context
89  hwframesctx->sw_format = Context->sw_pix_fmt == AV_PIX_FMT_YUVJ420P ? AV_PIX_FMT_YUV420P : Context->sw_pix_fmt;
90  hwframesctx->format = AV_PIX_FMT_VDPAU;
91  hwframesctx->width = Context->coded_width;
92  hwframesctx->height = Context->coded_height;
93  int res = av_hwframe_ctx_init(Context->hw_frames_ctx);
94  if (res < 0)
95  {
96  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise VDPAU frames context");
97  av_buffer_unref(&hwdeviceref);
98  av_buffer_unref(&(Context->hw_frames_ctx));
99  return res;
100  }
101 
102  auto * vdpaudevicectx = static_cast<AVVDPAUDeviceContext*>(hwdevicecontext->hwctx);
103  if (av_vdpau_bind_context(Context, vdpaudevicectx->device,
104  vdpaudevicectx->get_proc_address, AV_HWACCEL_FLAG_IGNORE_LEVEL) != 0)
105  {
106  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to bind VDPAU context");
107  av_buffer_unref(&hwdeviceref);
108  av_buffer_unref(&(Context->hw_frames_ctx));
109  return -1;
110  }
111 
112  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VDPAU buffer pool created"));
113  av_buffer_unref(&hwdeviceref);
114 
116 
117  return 0;
118 }
119 
121  const AVCodec ** /*Codec*/,
122  const QString &Decoder,
123  uint StreamType)
124 {
125  bool decodeonly = Decoder == "vdpau-dec";
126  auto success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_VDPAU_DEC : kCodec_MPEG1_VDPAU) + (StreamType - 1));
127  auto failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
128 
129  if (!Decoder.startsWith("vdpau") || qEnvironmentVariableIsSet("NO_VDPAU") || IsUnsupportedProfile(*Context))
130  return failure;
131 
132  if (!decodeonly)
133  if (!FrameTypeIsSupported(*Context, FMT_VDPAU))
134  return failure;
135 
136  QString codec = avcodec_get_name((*Context)->codec_id);
137  QString profile = avcodec_profile_name((*Context)->codec_id, (*Context)->profile);
138  QString pixfmt = av_get_pix_fmt_name((*Context)->pix_fmt);
139 
140  // VDPAU only supports 8bit 420p:(
142  bool vdpau = (type == FMT_YV12 || type == FMT_VDPAU) && MythVDPAUHelper::HaveVDPAU() &&
143  (decodeonly ? codec_is_vdpau_dechw(success) : codec_is_vdpau_hw(success));
144 
145  if (vdpau)
146  {
147  MythCodecContext::CodecProfile mythprofile =
148  MythCodecContext::FFmpegToMythProfile((*Context)->codec_id, (*Context)->profile);
149  const VDPAUProfiles& profiles = MythVDPAUHelper::GetProfiles();
150  vdpau = false;
151  for (const auto& vdpauprofile : profiles)
152  {
153  bool match = vdpauprofile.first == mythprofile;
154  if (match)
155  {
156  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Trying %1")
157  .arg(MythCodecContext::GetProfileDescription(mythprofile, QSize())));
158  if (vdpauprofile.second.Supported((*Context)->width, (*Context)->height, (*Context)->level))
159  {
160  vdpau = true;
161  break;
162  }
163  }
164  }
165  }
166 
167  // H264 needs additional checks for old hardware
168  if (vdpau && (success == kCodec_H264_VDPAU || success == kCodec_H264_VDPAU_DEC))
169  {
170  vdpau = MythVDPAUHelper::CheckH264Decode(*Context);
171  if (!vdpau)
172  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "H264 decode check failed");
173  }
174 
175  QString desc = QString("'%1 %2 %3 %4x%5'")
176  .arg(codec, profile, pixfmt).arg((*Context)->width).arg((*Context)->height);
177 
178  if (!vdpau)
179  {
180  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VDPAU does not support decoding %1").arg(desc));
181  return failure;
182  }
183 
184  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VDPAU supports decoding %1").arg(desc));
185  (*Context)->pix_fmt = AV_PIX_FMT_VDPAU;
186  return success;
187 }
188 
190 enum AVPixelFormat MythVDPAUContext::GetFormat(struct AVCodecContext* Context, const enum AVPixelFormat *PixFmt)
191 {
192  while (*PixFmt != AV_PIX_FMT_NONE)
193  {
194  if (*PixFmt == AV_PIX_FMT_VDPAU)
195  if (MythCodecContext::InitialiseDecoder(Context, MythVDPAUContext::InitialiseContext, "VDPAU context creation") >= 0)
196  return AV_PIX_FMT_VDPAU;
197  PixFmt++;
198  }
199  return AV_PIX_FMT_NONE;
200 }
201 
203 enum AVPixelFormat MythVDPAUContext::GetFormat2(struct AVCodecContext* Context, const enum AVPixelFormat *PixFmt)
204 {
205  while (*PixFmt != AV_PIX_FMT_NONE)
206  {
207  if (*PixFmt == AV_PIX_FMT_VDPAU)
208  {
209  AVBufferRef *device = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VDPAU, nullptr);
210  if (device)
211  {
213  if (Context->sw_pix_fmt == AV_PIX_FMT_YUVJ420P)
214  Context->sw_pix_fmt = AV_PIX_FMT_YUV420P;
215  Context->hw_device_ctx = device;
216  return AV_PIX_FMT_VDPAU;
217  }
218  }
219  PixFmt++;
220  }
221  return AV_PIX_FMT_NONE;
222 }
223 
224 bool MythVDPAUContext::RetrieveFrame(AVCodecContext* /*unused*/, MythVideoFrame *Frame, AVFrame *AvFrame)
225 {
226  if (AvFrame->format != AV_PIX_FMT_VDPAU)
227  return false;
229  return RetrieveHWFrame(Frame, AvFrame);
230  return false;
231 }
232 
234 {
235  return m_codecID == kCodec_H264_VDPAU;
236 }
237 
239 {
241 }
242 
252 bool MythVDPAUContext::DecoderNeedsReset(AVCodecContext* Context)
253 {
254  if (m_resetRequired)
255  return true;
256 
258  return false;
259  if (!Context)
260  return false;
261  if (!Context->hw_frames_ctx)
262  return false;
263 
264  auto* hwframesctx = reinterpret_cast<AVHWFramesContext*>(Context->hw_frames_ctx->data);
265  auto* interop = reinterpret_cast<MythVDPAUInterop*>(hwframesctx->user_opaque);
266  if (interop && interop->IsPreempted())
267  {
268  m_resetRequired = true;
269  return true;
270  }
271  return false;
272 }
273 
274 void MythVDPAUContext::InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
275 {
277  {
278  Context->get_buffer2 = MythCodecContext::GetBuffer;
279  Context->get_format = MythVDPAUContext::GetFormat;
280  Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
281  return;
282  }
284  {
285  Context->get_format = MythVDPAUContext::GetFormat2;
286  Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
287  DirectRendering = false;
288  return;
289  }
290 
291  MythCodecContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
292 }
codec_is_vdpau_hw
static bool codec_is_vdpau_hw(MythCodecID id)
Definition: mythcodecid.h:304
MythVDPAUHelper::GetProfiles
static const VDPAUProfiles & GetProfiles(void)
Definition: mythvdpauhelper.cpp:122
codec_is_vdpau_dec
static bool codec_is_vdpau_dec(MythCodecID id)
Definition: mythcodecid.h:309
MythVDPAUContext::DecoderWillResetOnAspect
bool DecoderWillResetOnAspect(void) override
Definition: mythvdpaucontext.cpp:238
MythCodecContext::m_codecID
MythCodecID m_codecID
Definition: mythcodeccontext.h:169
codec_is_vdpau_dechw
static bool codec_is_vdpau_dechw(MythCodecID id)
Definition: mythcodecid.h:312
mythplayerui.h
mythvdpauinterop.h
MythVDPAUContext::InitialiseContext
static int InitialiseContext(AVCodecContext *Context)
Create a VDPAU device for use with direct rendering.
Definition: mythvdpaucontext.cpp:32
VDPAUProfiles
std::vector< VDPAUProfile > VDPAUProfiles
Definition: mythvdpauhelper.h:50
MythCodecContext::CodecProfile
CodecProfile
Definition: mythcodeccontext.h:55
Frame
Definition: zmdefines.h:93
MythVDPAUInterop::CreateVDPAU
static MythVDPAUInterop * CreateVDPAU(MythPlayerUI *Player, MythRenderOpenGL *Context, MythCodecID CodecId)
Definition: mythvdpauinterop.cpp:9
MythCodecContext::NewHardwareFramesContext
static void NewHardwareFramesContext(void)
Track the number of concurrent frames contexts.
Definition: mythcodeccontext.cpp:432
MythVDPAUHelper::HaveVDPAU
static bool HaveVDPAU(bool Reinit=false)
Definition: mythvdpauhelper.cpp:55
MythCoreContext::IsUIThread
bool IsUIThread(void)
Definition: mythcorecontext.cpp:1348
MythCodecContext::InitVideoCodec
virtual void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
Definition: mythcodeccontext.cpp:306
MythVDPAUContext::MythVDPAUContext
MythVDPAUContext(DecoderBase *Parent, MythCodecID CodecID)
Definition: mythvdpaucontext.cpp:26
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythCodecContext::FramesContextFinished
static void FramesContextFinished(AVHWFramesContext *Context)
Definition: mythcodeccontext.cpp:439
MythVDPAUContext::GetFormat
static enum AVPixelFormat GetFormat(AVCodecContext *Context, const enum AVPixelFormat *PixFmt)
\ brief Confirm pixel format and create VDPAU device for direct rendering (MythVDPAUInterop required)
Definition: mythvdpaucontext.cpp:190
MythCodecContext::FFmpegToMythProfile
static CodecProfile FFmpegToMythProfile(AVCodecID CodecID, int Profile)
Definition: mythcodeccontext.cpp:687
kCodec_MPEG2_VDPAU
@ kCodec_MPEG2_VDPAU
Definition: mythcodecid.h:38
kCodec_MPEG1_VDPAU_DEC
@ kCodec_MPEG1_VDPAU_DEC
Definition: mythcodecid.h:53
MythCodecID
MythCodecID
Definition: mythcodecid.h:10
AVFrame
struct AVFrame AVFrame
Definition: BorderDetector.h:15
LOC
#define LOC
Definition: mythvdpaucontext.cpp:18
Decoder
Definition: decoder.h:70
kCodec_H264_VDPAU
@ kCodec_H264_VDPAU
Definition: mythcodecid.h:41
hardwareprofile.scan.profile
profile
Definition: scan.py:97
mythvdpauhelper.h
FMT_YV12
@ FMT_YV12
Definition: mythframe.h:24
kCodec_MPEG1
@ kCodec_MPEG1
Definition: mythcodecid.h:21
MythCodecContext::GetBuffer
static int GetBuffer(struct AVCodecContext *Context, AVFrame *Frame, int Flags)
A generic hardware buffer initialisation method when using AVHWFramesContext.
Definition: mythcodeccontext.cpp:326
mythvdpaucontext.h
MythVDPAUInterop
Definition: mythvdpauinterop.h:25
MythCodecContext::RetrieveHWFrame
virtual bool RetrieveHWFrame(MythVideoFrame *Frame, AVFrame *AvFrame)
Definition: mythcodeccontext.cpp:629
mpeg_version
uint mpeg_version(AVCodecID codec_id)
Definition: mythcodecid.cpp:455
uint
unsigned int uint
Definition: compat.h:81
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
MythVDPAUContext::GetSupportedCodec
static MythCodecID GetSupportedCodec(AVCodecContext **CodecContext, const AVCodec **Codec, const QString &Decoder, uint StreamType)
Definition: mythvdpaucontext.cpp:120
MythCodecContext::IsUnsupportedProfile
static bool IsUnsupportedProfile(AVCodecContext *Context)
Most hardware decoders do not support these codecs/profiles.
Definition: mythcodeccontext.cpp:599
MythAVUtil::PixelFormatToFrameType
static VideoFrameType PixelFormatToFrameType(AVPixelFormat Fmt)
Definition: mythavutil.cpp:71
MythRenderOpenGL
Definition: mythrenderopengl.h:96
MythVDPAUContext::RetrieveFrame
bool RetrieveFrame(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame) override
Definition: mythvdpaucontext.cpp:224
FMT_VDPAU
@ FMT_VDPAU
Definition: mythframe.h:57
MythVDPAUContext::GetFormat2
static enum AVPixelFormat GetFormat2(AVCodecContext *Context, const enum AVPixelFormat *PixFmt)
\ brief Confirm pixel format and create VDPAU device for copy back (no MythVDPAUInterop required)
Definition: mythvdpaucontext.cpp:203
MythCodecContext::CreateDevice
static AVBufferRef * CreateDevice(AVHWDeviceType Type, MythInteropGPU *Interop, const QString &Device=QString())
Definition: mythcodeccontext.cpp:560
avformatdecoder.h
MythCodecContext::FrameTypeIsSupported
static bool FrameTypeIsSupported(AVCodecContext *Context, VideoFrameType Format)
Definition: mythcodeccontext.cpp:516
MythCodecContext::m_resetRequired
bool m_resetRequired
Definition: mythcodeccontext.h:170
MythVDPAUHelper::CheckH264Decode
static bool CheckH264Decode(AVCodecContext *Context)
Definition: mythvdpauhelper.cpp:348
MythVDPAUContext::DecoderWillResetOnFlush
bool DecoderWillResetOnFlush(void) override
Definition: mythvdpaucontext.cpp:233
MythCodecContext
Definition: mythcodeccontext.h:52
VideoFrameType
VideoFrameType
Definition: mythframe.h:20
MythCodecContext::GetProfileDescription
static QString GetProfileDescription(CodecProfile Profile, QSize Size, VideoFrameType Format=FMT_NONE, uint ColorDepth=0)
Definition: mythcodeccontext.cpp:790
MythVideoFrame
Definition: mythframe.h:88
MythVDPAUContext::DecoderNeedsReset
bool DecoderNeedsReset(AVCodecContext *Context) override
Report whether the decoder is known to be errored.
Definition: mythvdpaucontext.cpp:252
kCodec_H264_VDPAU_DEC
@ kCodec_H264_VDPAU_DEC
Definition: mythcodecid.h:57
kCodec_MPEG1_VDPAU
@ kCodec_MPEG1_VDPAU
Definition: mythcodecid.h:37
MythVDPAUContext::InitVideoCodec
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
Definition: mythvdpaucontext.cpp:274
mythmainwindow.h
MythCodecContext::InitialiseDecoder
static int InitialiseDecoder(AVCodecContext *Context, CreateHWDecoder Callback, const QString &Debug)
Initialise a hardware decoder that is expected to use AVHWFramesContext.
Definition: mythcodeccontext.cpp:527
DecoderBase
Definition: decoderbase.h:120
MythCodecContext::GetPlayerUI
static MythPlayerUI * GetPlayerUI(AVCodecContext *Context)
Definition: mythcodeccontext.cpp:507
kCodec_MPEG2_VDPAU_DEC
@ kCodec_MPEG2_VDPAU_DEC
Definition: mythcodecid.h:54
OpenGLLocker
Definition: mythrenderopengl.h:255