MythTV master
mythmmalcontext.cpp
Go to the documentation of this file.
1// MythTV
3
5#include "mythplayerui.h"
6#include "mythmmalcontext.h"
7
8// FFmpeg
9extern "C" {
10#include "libavutil/opt.h"
11}
12
13#define LOC QString("MMAL: ")
14
16 : MythCodecContext(Parent, Codec)
17{
18}
19
21{
22 if (m_interop)
24}
25
27{
28 switch (Profile)
29 {
34 if (Width > 1920 || Height > 1088)
35 return false;
36 break;
37 default: break;
38 }
39 return true;
40}
41
43 const AVCodec **Codec,
44 const QString &Decoder,
45 AVStream *Stream,
46 uint StreamType)
47{
48 bool decodeonly = Decoder == "mmal-dec";
49 MythCodecID success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_MMAL_DEC : kCodec_MPEG1_MMAL) + (StreamType - 1));
50 MythCodecID failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
51
52 if (!Decoder.startsWith("mmal"))
53 return failure;
54
55 // Only MPEG2, MPEG4, VC1 and H264 supported (and HEVC will never be supported)
57 switch ((*Codec)->id)
58 {
59 case AV_CODEC_ID_MPEG2VIDEO: mythprofile = MythCodecContext::MPEG2; break;
60 case AV_CODEC_ID_MPEG4: mythprofile = MythCodecContext::MPEG4; break;
61 case AV_CODEC_ID_VC1: mythprofile = MythCodecContext::VC1; break;
62 case AV_CODEC_ID_H264:
63 if ((*Context)->profile == FF_PROFILE_H264_HIGH_10 ||
64 (*Context)->profile == FF_PROFILE_H264_HIGH_10_INTRA)
65 {
66 return failure;
67 }
68 mythprofile = MythCodecContext::H264; break;
69 default: break;
70 }
71
72 if (mythprofile == MythCodecContext::NoProfile)
73 return failure;
74
75 // Check size
76 if (!MythMMALContext::CheckCodecSize((*Context)->width, (*Context)->height, mythprofile))
77 return failure;
78
79 // check actual decoder support
81 if (!profiles.contains(mythprofile))
82 return failure;
83
84 if (!decodeonly)
85 if (!FrameTypeIsSupported(*Context, FMT_MMAL))
86 return failure;
87
88 // look for a decoder
89 QString name = QString((*Codec)->name) + "_mmal";
90 if (name == "mpeg2video_mmal")
91 name = "mpeg2_mmal";
92 const AVCodec *codec = avcodec_find_decoder_by_name(name.toLocal8Bit());
93 AvFormatDecoder *decoder = dynamic_cast<AvFormatDecoder*>(reinterpret_cast<DecoderBase*>((*Context)->opaque));
94 if (!codec || !decoder)
95 {
96 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to find %1").arg(name));
97 return failure;
98 }
99
100 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found MMAL/FFmpeg decoder '%1'").arg(name));
101 *Codec = codec;
102 decoder->CodecMap()->FreeCodecContext(Stream);
103 *Context = decoder->CodecMap()->GetCodecContext(Stream, *Codec);
104 (*Context)->pix_fmt = decodeonly ? (*Context)->pix_fmt : AV_PIX_FMT_MMAL;
105 return success;
106}
107
108void MythMMALContext::InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
109{
111 {
112 DirectRendering = false;
113 return;
114 }
115 else if (codec_is_mmal(m_codecID))
116 {
117 Context->get_format = MythMMALContext::GetFormat;
118 DirectRendering = false;
119 return;
120 }
121
122 MythCodecContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
123}
124
125bool MythMMALContext::RetrieveFrame(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame)
126{
128 return GetBuffer(Context, Frame, AvFrame, 0);
129 else if (codec_is_mmal(m_codecID))
130 return GetBuffer2(Context, Frame, AvFrame, 0);
131 return false;
132}
133
134
135int MythMMALContext::HwDecoderInit(AVCodecContext *Context)
136{
137 if (!Context)
138 return -1;
139
141 return 0;
142
143 if (!codec_is_mmal(m_codecID) || Context->pix_fmt != AV_PIX_FMT_MMAL)
144 return -1;
145
146 if (auto * player = GetPlayerUI(Context); player != nullptr)
147 if (FrameTypeIsSupported(Context, FMT_MMAL))
148 m_interop = MythMMALInterop::CreateMMAL(dynamic_cast<MythRenderOpenGL*>(player->GetRender()));
149
150 return m_interop ? 0 : -1;
151}
152
153void MythMMALContext::SetDecoderOptions(AVCodecContext *Context, const AVCodec *Codec)
154{
155 if (!(codec_is_mmal(m_codecID)))
156 return;
157 if (!(Context && Codec))
158 return;
159 if (!(Codec->priv_class && Context->priv_data))
160 return;
161 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Setting number of extra buffers to 8");
162 av_opt_set(Context->priv_data, "extra_buffers", "8", 0);
163}
164
165bool MythMMALContext::GetBuffer(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame, int)
166{
167 // Sanity checks
168 if (!Context || !AvFrame || !Frame)
169 return false;
170
171 // Ensure we can render this format
172 AvFormatDecoder *decoder = static_cast<AvFormatDecoder*>(Context->opaque);
173 VideoFrameType type = MythAVUtil::PixelFormatToFrameType(static_cast<AVPixelFormat>(AvFrame->format));
174 const VideoFrameTypes* supported = Frame->m_renderFormats;
175 auto foundIt = std::find(supported->cbegin(), supported->cend(), type);
176 // No fallback currently (unlikely)
177 if (foundIt == supported->end())
178 return false;
179
180 // Re-allocate if necessary
181 if ((Frame->m_type != type) || (Frame->m_width != AvFrame->width) || (Frame->m_height != AvFrame->height))
182 if (!VideoBuffers::ReinitBuffer(Frame, type, decoder->GetVideoCodecID(), AvFrame->width, AvFrame->height))
183 return false;
184
185 // Copy data
186 uint count = MythVideoFrame::GetNumPlanes(Frame->m_type);
187 for (uint plane = 0; plane < count; ++plane)
188 {
189 MythVideoFrame::CopyPlane(Frame->m_buffer + Frame->m_offsets[plane], Frame->m_pitches[plane],
190 AvFrame->data[plane], AvFrame->linesize[plane],
191 MythVideoFrame::GetPitchForPlane(Frame->m_type, AvFrame->width, plane),
192 MythVideoFrame::GetHeightForPlane(Frame->m_type, AvFrame->height, plane));
193 }
194
195 return true;
196}
197
198bool MythMMALContext::GetBuffer2(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame, int)
199{
200 // Sanity checks
201 if (!Context || !AvFrame || !Frame || !m_interop)
202 {
203 LOG(VB_GENERAL, LOG_ERR, LOC + "Error");
204 return false;
205 }
206
207 // MMAL?
208 if (Frame->m_type != FMT_MMAL || static_cast<AVPixelFormat>(AvFrame->format) != AV_PIX_FMT_MMAL)
209 {
210 LOG(VB_GENERAL, LOG_ERR, LOC + "Not an MMAL frame");
211 return false;
212 }
213
214 Frame->m_width = AvFrame->width;
215 Frame->m_height = AvFrame->height;
216 Frame->m_pixFmt = Context->pix_fmt;
217 Frame->m_swPixFmt = Context->sw_pix_fmt;
218 Frame->m_directRendering = 1;
219 AvFrame->opaque = Frame;
220
221 // Frame->data[3] holds MMAL_BUFFER_HEADER_T
222 Frame->m_buffer = AvFrame->data[3];
223 // Retain the buffer so it is not released before we display it
224 Frame->m_priv[0] = reinterpret_cast<unsigned char*>(av_buffer_ref(AvFrame->buf[0]));
225 // Add interop
226 Frame->m_priv[1] = reinterpret_cast<unsigned char*>(m_interop);
227 // Set the release method
228 AvFrame->buf[1] = av_buffer_create(reinterpret_cast<uint8_t*>(Frame), 0, MythCodecContext::ReleaseBuffer,
229 static_cast<AvFormatDecoder*>(Context->opaque), 0);
230 return true;
231}
232
233AVPixelFormat MythMMALContext::GetFormat(AVCodecContext*, const AVPixelFormat *PixFmt)
234{
235 while (*PixFmt != AV_PIX_FMT_NONE)
236 {
237 if (*PixFmt == AV_PIX_FMT_MMAL)
238 return AV_PIX_FMT_MMAL;
239 PixFmt++;
240 }
241 return AV_PIX_FMT_NONE;
242}
243
244bool MythMMALContext::HaveMMAL(bool Reinit /*=false*/)
245{
246 static QRecursiveMutex lock;
247 QMutexLocker locker(&lock);
248 static bool s_checked = false;
249 static bool s_available = false;
250
251 if (s_checked && !Reinit)
252 return s_available;
253 s_checked = true;
254
255 const MMALProfiles& profiles = MythMMALContext::GetProfiles();
256 if (profiles.isEmpty())
257 return s_available;
258
259 LOG(VB_GENERAL, LOG_INFO, LOC + "Supported/available MMAL decoders:");
260 s_available = true;
261 QSize size{0, 0};
262 for (auto profile : std::as_const(profiles))
263 LOG(VB_GENERAL, LOG_INFO, LOC + MythCodecContext::GetProfileDescription(profile, size));
264 return s_available;
265}
266
267void MythMMALContext::GetDecoderList(QStringList &Decoders)
268{
269 const MMALProfiles& profiles = MythMMALContext::GetProfiles();
270 if (profiles.isEmpty())
271 return;
272
273 QSize size(0, 0);
274 Decoders.append("MMAL:");
276 Decoders.append(MythCodecContext::GetProfileDescription(profile, size));
277}
278
279// Broadcom
280extern "C" {
281#include "interface/vmcs_host/vc_vchi_gencmd.h"
282}
283
285{
286 static QRecursiveMutex lock;
287 static bool s_initialised = false;
288 static MMALProfiles s_profiles;
289
290 QMutexLocker locker(&lock);
291 if (s_initialised)
292 return s_profiles;
293 s_initialised = true;
294
295 static const QPair<QString, MythCodecContext::CodecProfile> s_map[] =
296 {
297 { "MPG2", MythCodecContext::MPEG2 },
298 { "MPG4", MythCodecContext::MPEG4 },
299 { "WVC1", MythCodecContext::VC1 },
300 { "H264", MythCodecContext::H264 }
301 };
302
303 vcos_init();
304 VCHI_INSTANCE_T vchi_instance;
305 if (vchi_initialise(&vchi_instance) != 0)
306 return s_profiles;
307 if (vchi_connect(nullptr, 0, vchi_instance) != 0)
308 return s_profiles;
309 VCHI_CONNECTION_T *vchi_connection = nullptr;
310 vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1 );
311
312 for (auto profile : s_map)
313 {
314 char command[32];
315 char* response = nullptr;
316 int responsesize = 0;
317 QString msg = QString("codec_enabled %1").arg(profile.first);
318 if (!vc_gencmd(command, sizeof(command), msg.toLocal8Bit().constData()))
319 vc_gencmd_string_property(command, profile.first.toLocal8Bit().constData(), &response, &responsesize);
320
321 if (response && responsesize && qstrcmp(response, "enabled") == 0)
322 s_profiles.append(profile.second);
323 }
324
325 vc_gencmd_stop();
326 vchi_disconnect(vchi_instance);
327
328 return s_profiles;
329}
AVFrame AVFrame
A decoder for media files.
MythCodecMap * CodecMap(void)
MythCodecID GetVideoCodecID(void) const override
static VideoFrameType PixelFormatToFrameType(AVPixelFormat Fmt)
Definition: mythavutil.cpp:72
static bool FrameTypeIsSupported(AVCodecContext *Context, VideoFrameType Format)
static void ReleaseBuffer(void *Opaque, uint8_t *Data)
static void DestroyInterop(MythInteropGPU *Interop)
virtual void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
static QString GetProfileDescription(CodecProfile Profile, QSize Size, VideoFrameType Format=FMT_NONE, uint ColorDepth=0)
static MythPlayerUI * GetPlayerUI(AVCodecContext *Context)
MythCodecID m_codecID
AVCodecContext * GetCodecContext(const AVStream *Stream, const AVCodec *Codec=nullptr, bool NullCodec=false)
Definition: mythavutil.cpp:288
void FreeCodecContext(const AVStream *Stream)
Definition: mythavutil.cpp:333
static const MMALProfiles & GetProfiles(void)
static bool GetBuffer(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame, int)
MythMMALInterop * m_interop
int HwDecoderInit(AVCodecContext *Context) override
static bool HaveMMAL(bool Reinit=false)
~MythMMALContext() override
bool RetrieveFrame(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame) override
bool GetBuffer2(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame, int)
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
static bool CheckCodecSize(int Width, int Height, MythCodecContext::CodecProfile Profile)
void SetDecoderOptions(AVCodecContext *Context, const AVCodec *Codec) override
static void GetDecoderList(QStringList &Decoders)
static MythCodecID GetSupportedCodec(AVCodecContext **Context, const AVCodec **Codec, const QString &Decoder, AVStream *Stream, uint StreamType)
static enum AVPixelFormat GetFormat(AVCodecContext *, const AVPixelFormat *PixFmt)
MythMMALContext(DecoderBase *Parent, MythCodecID Codec)
static MythMMALInterop * CreateMMAL(MythRenderOpenGL *Context)
Create an MMAL interop.
static uint GetNumPlanes(VideoFrameType Type)
Definition: mythframe.h:213
static int GetPitchForPlane(VideoFrameType Type, int Width, uint Plane)
Definition: mythframe.h:303
static int GetHeightForPlane(VideoFrameType Type, int Height, uint Plane)
Definition: mythframe.h:257
static void CopyPlane(uint8_t *To, int ToPitch, const uint8_t *From, int FromPitch, int PlaneWidth, int PlaneHeight)
Definition: mythframe.cpp:188
static bool ReinitBuffer(MythVideoFrame *Frame, VideoFrameType Type, MythCodecID CodecID, int Width, int Height)
unsigned int uint
Definition: compat.h:68
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
MythCodecID
Definition: mythcodecid.h:14
@ kCodec_MPEG1_MMAL
Definition: mythcodecid.h:248
@ kCodec_MPEG1
Definition: mythcodecid.h:24
@ kCodec_MPEG1_MMAL_DEC
Definition: mythcodecid.h:264
static bool codec_is_mmal_dec(MythCodecID id)
Definition: mythcodecid.h:365
static bool codec_is_mmal(MythCodecID id)
Definition: mythcodecid.h:363
std::vector< VideoFrameType > VideoFrameTypes
Definition: mythframe.h:82
VideoFrameType
Definition: mythframe.h:20
@ FMT_MMAL
Definition: mythframe.h:59
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
QList< MythCodecContext::CodecProfile > MMALProfiles