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