MythTV  master
mythvdpaucontext.cpp
Go to the documentation of this file.
1 // MythTV
2 #include "mythmainwindow.h"
3 #include "avformatdecoder.h"
4 #include "mythvdpauinterop.h"
5 #include "mythvdpauhelper.h"
6 #include "mythvdpaucontext.h"
7 
8 // FFmpeg
9 extern "C" {
10 #include "libavutil/hwcontext_vdpau.h"
11 #include "libavutil/pixdesc.h"
12 #include "libavcodec/vdpau.h"
13 }
14 
15 #define LOC QString("VDPAUDec: ")
16 
24  : MythCodecContext(Parent, CodecID)
25 {
26 }
27 
30 {
31  if (!gCoreContext->IsUIThread() || !Context)
32  return -1;
33 
34  // We need a player to release the interop
35  MythPlayer *player = nullptr;
36  auto *decoder = reinterpret_cast<AvFormatDecoder*>(Context->opaque);
37  if (decoder)
38  player = decoder->GetPlayer();
39  if (!player)
40  return -1;
41 
42  // Retrieve OpenGL render context
44  if (!render)
45  return -1;
46  OpenGLLocker locker(render);
47 
48  // Check interop support
50  return -1;
51 
52  // Create interop
53  auto vdpauid = static_cast<MythCodecID>(kCodec_MPEG1_VDPAU + (mpeg_version(Context->codec_id) - 1));
54  MythVDPAUInterop *interop = MythVDPAUInterop::Create(render, vdpauid);
55  if (!interop)
56  return -1;
57 
58  // Set player
59  interop->SetPlayer(player);
60 
61  // Allocate the device context
62  AVBufferRef* hwdeviceref = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VDPAU, interop);
63  if (!hwdeviceref)
64  return -1;
65 
66  auto* hwdevicecontext = reinterpret_cast<AVHWDeviceContext*>(hwdeviceref->data);
67  if (!hwdevicecontext || (hwdevicecontext && !hwdevicecontext->hwctx))
68  return -1;
69 
70  // Initialise device context
71  if (av_hwdevice_ctx_init(hwdeviceref) < 0)
72  {
73  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise device context");
74  av_buffer_unref(&hwdeviceref);
75  interop->DecrRef();
76  return -1;
77  }
78 
79  // allocate the hardware frames context
80  Context->hw_frames_ctx = av_hwframe_ctx_alloc(hwdeviceref);
81  if (!Context->hw_frames_ctx)
82  {
83  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VDPAU hardware frames context");
84  av_buffer_unref(&hwdeviceref);
85  interop->DecrRef();
86  return -1;
87  }
88 
89  // Add our interop class and set the callback for its release
90  auto* hwframesctx = reinterpret_cast<AVHWFramesContext*>(Context->hw_frames_ctx->data);
91  hwframesctx->user_opaque = interop;
92  hwframesctx->free = &MythCodecContext::FramesContextFinished;
93 
94  // Initialise frames context
95  hwframesctx->sw_format = Context->sw_pix_fmt == AV_PIX_FMT_YUVJ420P ? AV_PIX_FMT_YUV420P : Context->sw_pix_fmt;
96  hwframesctx->format = AV_PIX_FMT_VDPAU;
97  hwframesctx->width = Context->coded_width;
98  hwframesctx->height = Context->coded_height;
99  int res = av_hwframe_ctx_init(Context->hw_frames_ctx);
100  if (res < 0)
101  {
102  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise VDPAU frames context");
103  av_buffer_unref(&hwdeviceref);
104  av_buffer_unref(&(Context->hw_frames_ctx));
105  return res;
106  }
107 
108  auto* vdpaudevicectx = static_cast<AVVDPAUDeviceContext*>(hwdevicecontext->hwctx);
109  if (av_vdpau_bind_context(Context, vdpaudevicectx->device, vdpaudevicectx->get_proc_address, 0) != 0)
110  {
111  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to bind VDPAU context");
112  av_buffer_unref(&hwdeviceref);
113  av_buffer_unref(&(Context->hw_frames_ctx));
114  return -1;
115  }
116 
117  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VDPAU buffer pool created"));
118  av_buffer_unref(&hwdeviceref);
119 
121 
122  return 0;
123 }
124 
126  AVCodec ** /*Codec*/,
127  const QString &Decoder,
128  uint StreamType)
129 {
130  bool decodeonly = Decoder == "vdpau-dec";
131  auto success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_VDPAU_DEC : kCodec_MPEG1_VDPAU) + (StreamType - 1));
132  auto failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
133 
134  if (!Decoder.startsWith("vdpau") || getenv("NO_VDPAU") || IsUnsupportedProfile(*Context))
135  return failure;
136 
137  if (!decodeonly)
138  {
139  // If called from outside of the main thread, we need a MythPlayer instance to
140  // process the callback interop check callback - which may fail otherwise
141  MythPlayer* player = nullptr;
142  if (!gCoreContext->IsUIThread())
143  {
144  auto* decoder = reinterpret_cast<AvFormatDecoder*>((*Context)->opaque);
145  if (decoder)
146  player = decoder->GetPlayer();
147  }
148 
149  // direct rendering needs interop support
151  return failure;
152  }
153 
154  QString codec = ff_codec_id_string((*Context)->codec_id);
155  QString profile = avcodec_profile_name((*Context)->codec_id, (*Context)->profile);
156  QString pixfmt = av_get_pix_fmt_name((*Context)->pix_fmt);
157 
158  // VDPAU only supports 8bit 420p:(
159  VideoFrameType type = PixelFormatToFrameType((*Context)->pix_fmt);
160  bool vdpau = (type == FMT_YV12) && MythVDPAUHelper::HaveVDPAU() &&
161  (decodeonly ? codec_is_vdpau_dechw(success) : codec_is_vdpau_hw(success));
162 
163  if (vdpau)
164  {
165  MythCodecContext::CodecProfile mythprofile =
166  MythCodecContext::FFmpegToMythProfile((*Context)->codec_id, (*Context)->profile);
167  const VDPAUProfiles& profiles = MythVDPAUHelper::GetProfiles();
168  vdpau = false;
169  for (auto vdpauprofile : profiles)
170  {
171  if (vdpauprofile.first == mythprofile &&
172  vdpauprofile.second.Supported((*Context)->width, (*Context)->height, (*Context)->level))
173  {
174  vdpau = true;
175  break;
176  }
177  }
178  }
179 
180  // H264 needs additional checks for old hardware
181  if (vdpau && (success == kCodec_H264_VDPAU || success == kCodec_H264_VDPAU_DEC))
183 
184  QString desc = QString("'%1 %2 %3 %4x%5'")
185  .arg(codec).arg(profile).arg(pixfmt).arg((*Context)->width).arg((*Context)->height);
186 
187  if (!vdpau)
188  {
189  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VDPAU does not support decoding %1").arg(desc));
190  return failure;
191  }
192 
193  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VDPAU supports decoding %1").arg(desc));
194  (*Context)->pix_fmt = AV_PIX_FMT_VDPAU;
195  return success;
196 }
197 
199 enum AVPixelFormat MythVDPAUContext::GetFormat(struct AVCodecContext* Context, const enum AVPixelFormat *PixFmt)
200 {
201  while (*PixFmt != AV_PIX_FMT_NONE)
202  {
203  if (*PixFmt == AV_PIX_FMT_VDPAU)
205  return AV_PIX_FMT_VDPAU;
206  PixFmt++;
207  }
208  return AV_PIX_FMT_NONE;
209 }
210 
212 enum AVPixelFormat MythVDPAUContext::GetFormat2(struct AVCodecContext* Context, const enum AVPixelFormat *PixFmt)
213 {
214  while (*PixFmt != AV_PIX_FMT_NONE)
215  {
216  if (*PixFmt == AV_PIX_FMT_VDPAU)
217  {
218  AVBufferRef *device = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VDPAU, nullptr);
219  if (device)
220  {
222  if (Context->sw_pix_fmt == AV_PIX_FMT_YUVJ420P)
223  Context->sw_pix_fmt = AV_PIX_FMT_YUV420P;
224  Context->hw_device_ctx = device;
225  return AV_PIX_FMT_VDPAU;
226  }
227  }
228  PixFmt++;
229  }
230  return AV_PIX_FMT_NONE;
231 }
232 
233 bool MythVDPAUContext::RetrieveFrame(AVCodecContext* /*unused*/, VideoFrame *Frame, AVFrame *AvFrame)
234 {
235  if (AvFrame->format != AV_PIX_FMT_VDPAU)
236  return false;
238  return RetrieveHWFrame(Frame, AvFrame);
239  return false;
240 }
241 
243 {
244  return m_codecID == kCodec_H264_VDPAU;
245 }
246 
248 {
250 }
251 
262 {
263  if (m_resetRequired)
264  return true;
265 
267  return false;
268  if (!Context)
269  return false;
270  if (!Context->hw_frames_ctx)
271  return false;
272 
273  auto* hwframesctx = reinterpret_cast<AVHWFramesContext*>(Context->hw_frames_ctx->data);
274  auto* interop = reinterpret_cast<MythVDPAUInterop*>(hwframesctx->user_opaque);
275  if (interop && interop->IsPreempted())
276  {
277  m_resetRequired = true;
278  return true;
279  }
280  return false;
281 }
282 
283 void MythVDPAUContext::InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
284 {
286  {
287  Context->get_buffer2 = MythCodecContext::GetBuffer;
288  Context->get_format = MythVDPAUContext::GetFormat;
289  Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
290  return;
291  }
293  {
295  Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
296  DirectRendering = false;
297  return;
298  }
299 
300  MythCodecContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
301 }
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
void SetPlayer(MythPlayer *Player)
QHash< QString, Action * > Context
Definition: action.h:77
static Type GetInteropType(VideoFrameType Format, MythPlayer *Player)
Check whether we support direct rendering for the given VideoFrameType.
MythCodecID
Definition: mythcodecid.h:10
MythVDPAUContext(DecoderBase *Parent, MythCodecID CodecID)
static int InitialiseDecoder(AVCodecContext *Context, CreateHWDecoder Callback, const QString &Debug)
Initialise a hardware decoder that is expected to use AVHWFramesContext.
static enum AVPixelFormat GetFormat(AVCodecContext *Context, const enum AVPixelFormat *PixFmt)
\ brief Confirm pixel format and create VDPAU device for direct rendering (MythVDPAUInterop required)
struct AVFrame AVFrame
static int GetBuffer(struct AVCodecContext *Context, AVFrame *Frame, int Flags)
A generic hardware buffer initialisation method when using AVHWFramesContext.
VideoFrameType PixelFormatToFrameType(AVPixelFormat fmt)
Definition: mythavutil.cpp:68
VideoFrameType
Definition: mythframe.h:23
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool IsUnsupportedProfile(AVCodecContext *Context)
Most hardware decoders do not support these codecs/profiles.
static int InitialiseContext(AVCodecContext *Context)
Create a VDPAU device for use with direct rendering.
bool RetrieveFrame(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame) override
MythCodecID m_codecID
static bool CheckH264Decode(AVCodecContext *Context)
virtual void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
#define LOC
static MythCodecID GetSupportedCodec(AVCodecContext **CodecContext, AVCodec **Codec, const QString &Decoder, uint StreamType)
unsigned int uint
Definition: compat.h:140
static MythVDPAUInterop * Create(MythRenderOpenGL *Context, MythCodecID CodecId)
#define codec_is_vdpau_dec(id)
Definition: mythcodecid.h:304
static CodecProfile FFmpegToMythProfile(AVCodecID CodecID, int Profile)
static void FramesContextFinished(AVHWFramesContext *Context)
static bool HaveVDPAU(void)
uint mpeg_version(int codec_id)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define codec_is_vdpau_dechw(id)
Definition: mythcodecid.h:306
static MythRenderOpenGL * GetOpenGLRender(void)
static enum AVPixelFormat GetFormat2(AVCodecContext *Context, const enum AVPixelFormat *PixFmt)
\ brief Confirm pixel format and create VDPAU device for copy back (no MythVDPAUInterop required)
static void NewHardwareFramesContext(void)
Track the number of concurrent frames contexts.
static const VDPAUProfiles & GetProfiles(void)
#define codec_is_vdpau_hw(id)
Definition: mythcodecid.h:300
virtual bool RetrieveHWFrame(VideoFrame *Frame, AVFrame *AvFrame)
bool DecoderWillResetOnAspect(void) override
QList< VDPAUProfile > VDPAUProfiles
static AVBufferRef * CreateDevice(AVHWDeviceType Type, MythOpenGLInterop *Interop, const QString &Device=QString())
bool DecoderNeedsReset(AVCodecContext *Context) override
Report whether the decoder is known to be errored.
bool DecoderWillResetOnFlush(void) override