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