MythTV  master
mythmmalcontext.cpp
Go to the documentation of this file.
1 // MythTV
3 #include "mythmmalcontext.h"
4 
5 // FFmpeg
6 extern "C" {
7 #include "libavutil/opt.h"
8 }
9 
10 #define LOC QString("MMAL: ")
11 
13  : MythCodecContext(Parent, Codec)
14 {
15 }
16 
18 {
19  if (m_interop)
21 }
22 
24 {
25  switch (Profile)
26  {
31  if (Width > 1920 || Height > 1088)
32  return false;
33  break;
34  default: break;
35  }
36  return true;
37 }
38 
40  AVCodec **Codec,
41  const QString &Decoder,
42  AVStream *Stream,
43  uint StreamType)
44 {
45  bool decodeonly = Decoder == "mmal-dec";
46  MythCodecID success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_MMAL_DEC : kCodec_MPEG1_MMAL) + (StreamType - 1));
47  MythCodecID failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
48 
49  if (!Decoder.startsWith("mmal"))
50  return failure;
51 
52  // Only MPEG2, MPEG4, VC1 and H264 supported (and HEVC will never be supported)
54  switch ((*Codec)->id)
55  {
56  case AV_CODEC_ID_MPEG2VIDEO: mythprofile = MythCodecContext::MPEG2; break;
57  case AV_CODEC_ID_MPEG4: mythprofile = MythCodecContext::MPEG4; break;
58  case AV_CODEC_ID_VC1: mythprofile = MythCodecContext::VC1; break;
59  case AV_CODEC_ID_H264:
60  if ((*Context)->profile == FF_PROFILE_H264_HIGH_10 ||
61  (*Context)->profile == FF_PROFILE_H264_HIGH_10_INTRA)
62  {
63  return failure;
64  }
65  mythprofile = MythCodecContext::H264; break;
66  default: break;
67  }
68 
69  if (mythprofile == MythCodecContext::NoProfile)
70  return failure;
71 
72  // Check size
73  if (!MythMMALContext::CheckCodecSize((*Context)->width, (*Context)->height, mythprofile))
74  return failure;
75 
76  // check actual decoder support
77  const MMALProfiles& profiles = MythMMALContext::GetProfiles();
78  if (!profiles.contains(mythprofile))
79  return failure;
80 
81  if (!decodeonly)
82  {
83  // If called from outside of the main thread, we need a MythPlayer instance to
84  // process the callback interop check callback - which may fail otherwise
85  MythPlayer* player = nullptr;
86  if (!gCoreContext->IsUIThread())
87  {
88  AvFormatDecoder* decoder = reinterpret_cast<AvFormatDecoder*>((*Context)->opaque);
89  if (decoder)
90  player = decoder->GetPlayer();
91  }
92 
93  // direct rendering needs interop support
95  return failure;
96  }
97 
98  // look for a decoder
99  QString name = QString((*Codec)->name) + "_mmal";
100  if (name == "mpeg2video_mmal")
101  name = "mpeg2_mmal";
102  AVCodec *codec = avcodec_find_decoder_by_name(name.toLocal8Bit());
103  AvFormatDecoder *decoder = dynamic_cast<AvFormatDecoder*>(reinterpret_cast<DecoderBase*>((*Context)->opaque));
104  if (!codec || !decoder)
105  {
106  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to find %1").arg(name));
107  return failure;
108  }
109 
110  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found MMAL/FFmpeg decoder '%1'").arg(name));
111  *Codec = codec;
112  decoder->CodecMap()->freeCodecContext(Stream);
113  *Context = decoder->CodecMap()->getCodecContext(Stream, *Codec);
114  (*Context)->pix_fmt = decodeonly ? (*Context)->pix_fmt : AV_PIX_FMT_MMAL;
115  return success;
116 }
117 
118 void MythMMALContext::InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
119 {
121  {
122  DirectRendering = false;
123  return;
124  }
125  else if (codec_is_mmal(m_codecID))
126  {
127  Context->get_format = MythMMALContext::GetFormat;
128  DirectRendering = false;
129  return;
130  }
131 
132  MythCodecContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
133 }
134 
136 {
138  return GetBuffer(Context, Frame, AvFrame, 0);
139  else if (codec_is_mmal(m_codecID))
140  return GetBuffer2(Context, Frame, AvFrame, 0);
141  return false;
142 }
143 
144 
146 {
147  if (!Context)
148  return -1;
149 
151  return 0;
152 
153  if (!codec_is_mmal(m_codecID) || Context->pix_fmt != AV_PIX_FMT_MMAL)
154  return -1;
155 
158  return m_interop ? 0 : -1;
159 }
160 
161 void MythMMALContext::SetDecoderOptions(AVCodecContext *Context, AVCodec *Codec)
162 {
163  if (!(codec_is_mmal(m_codecID)))
164  return;
165  if (!(Context && Codec))
166  return;
167  if (!(Codec->priv_class && Context->priv_data))
168  return;
169  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Setting number of extra buffers to 8");
170  av_opt_set(Context->priv_data, "extra_buffers", "8", 0);
171 }
172 
173 bool MythMMALContext::GetBuffer(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)
174 {
175  // Sanity checks
176  if (!Context || !AvFrame || !Frame)
177  return false;
178 
179  // Ensure we can render this format
180  AvFormatDecoder *decoder = static_cast<AvFormatDecoder*>(Context->opaque);
181  VideoFrameType type = PixelFormatToFrameType(static_cast<AVPixelFormat>(AvFrame->format));
182  VideoFrameType* supported = decoder->GetPlayer()->DirectRenderFormats();
183  bool found = false;
184  while (*supported != FMT_NONE)
185  {
186  if (*supported == type)
187  {
188  found = true;
189  break;
190  }
191  supported++;
192  }
193 
194  // No fallback currently (unlikely)
195  if (!found)
196  return false;
197 
198  // Re-allocate if necessary
199  if ((Frame->codec != type) || (Frame->width != AvFrame->width) || (Frame->height != AvFrame->height))
200  if (!VideoBuffers::ReinitBuffer(Frame, type, decoder->GetVideoCodecID(), AvFrame->width, AvFrame->height))
201  return false;
202 
203  // Copy data
204  uint count = planes(Frame->codec);
205  for (uint plane = 0; plane < count; ++plane)
206  copyplane(Frame->buf + Frame->offsets[plane], Frame->pitches[plane], AvFrame->data[plane], AvFrame->linesize[plane],
207  pitch_for_plane(Frame->codec, AvFrame->width, plane), height_for_plane(Frame->codec, AvFrame->height, plane));
208 
209  AvFrame->reordered_opaque = Context->reordered_opaque;
210  return true;
211 }
212 
213 bool MythMMALContext::GetBuffer2(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)
214 {
215  // Sanity checks
216  if (!Context || !AvFrame || !Frame || !m_interop)
217  {
218  LOG(VB_GENERAL, LOG_ERR, LOC + "Error");
219  return false;
220  }
221 
222  // MMAL?
223  if (Frame->codec != FMT_MMAL || static_cast<AVPixelFormat>(AvFrame->format) != AV_PIX_FMT_MMAL)
224  {
225  LOG(VB_GENERAL, LOG_ERR, LOC + "Not an MMAL frame");
226  return false;
227  }
228 
229  Frame->width = AvFrame->width;
230  Frame->height = AvFrame->height;
231  Frame->pix_fmt = Context->pix_fmt;
232  Frame->sw_pix_fmt = Context->sw_pix_fmt;
233  Frame->directrendering = 1;
234  AvFrame->opaque = Frame;
235  AvFrame->reordered_opaque = Context->reordered_opaque;
236 
237  // Frame->data[3] holds MMAL_BUFFER_HEADER_T
238  Frame->buf = AvFrame->data[3];
239  // Retain the buffer so it is not released before we display it
240  Frame->priv[0] = reinterpret_cast<unsigned char*>(av_buffer_ref(AvFrame->buf[0]));
241  // Add interop
242  Frame->priv[1] = reinterpret_cast<unsigned char*>(m_interop);
243  // Set the release method
244  AvFrame->buf[1] = av_buffer_create(reinterpret_cast<uint8_t*>(Frame), 0, MythCodecContext::ReleaseBuffer,
245  static_cast<AvFormatDecoder*>(Context->opaque), 0);
246  return true;
247 }
248 
249 AVPixelFormat MythMMALContext::GetFormat(AVCodecContext*, const AVPixelFormat *PixFmt)
250 {
251  while (*PixFmt != AV_PIX_FMT_NONE)
252  {
253  if (*PixFmt == AV_PIX_FMT_MMAL)
254  return AV_PIX_FMT_MMAL;
255  PixFmt++;
256  }
257  return AV_PIX_FMT_NONE;
258 }
259 
261 {
262  static QMutex lock(QMutex::Recursive);
263  QMutexLocker locker(&lock);
264  static bool s_checked = false;
265  static bool s_available = false;
266 
267  if (s_checked)
268  return s_available;
269  s_checked = true;
270 
271  const MMALProfiles& profiles = MythMMALContext::GetProfiles();
272  if (profiles.isEmpty())
273  return s_available;
274 
275  LOG(VB_GENERAL, LOG_INFO, LOC + "Supported/available MMAL decoders:");
276  s_available = true;
277  QSize size{0, 0};
278  foreach (auto profile, profiles)
279  LOG(VB_GENERAL, LOG_INFO, LOC + MythCodecContext::GetProfileDescription(profile, size));
280  return s_available;
281 }
282 
283 void MythMMALContext::GetDecoderList(QStringList &Decoders)
284 {
285  const MMALProfiles& profiles = MythMMALContext::GetProfiles();
286  if (profiles.isEmpty())
287  return;
288 
289  QSize size(0, 0);
290  Decoders.append("MMAL:");
291  for (MythCodecContext::CodecProfile profile : profiles)
292  Decoders.append(MythCodecContext::GetProfileDescription(profile, size));
293 }
294 
295 // Broadcom
296 extern "C" {
297 #include "interface/vmcs_host/vc_vchi_gencmd.h"
298 }
299 
301 {
302  static QMutex lock(QMutex::Recursive);
303  static bool s_initialised = false;
304  static MMALProfiles s_profiles;
305 
306  QMutexLocker locker(&lock);
307  if (s_initialised)
308  return s_profiles;
309  s_initialised = true;
310 
311  static const QPair<QString, MythCodecContext::CodecProfile> s_map[] =
312  {
313  { "MPG2", MythCodecContext::MPEG2 },
314  { "MPG4", MythCodecContext::MPEG4 },
315  { "WVC1", MythCodecContext::VC1 },
316  { "H264", MythCodecContext::H264 }
317  };
318 
319  vcos_init();
320  VCHI_INSTANCE_T vchi_instance;
321  if (vchi_initialise(&vchi_instance) != 0)
322  return s_profiles;
323  if (vchi_connect(nullptr, 0, vchi_instance) != 0)
324  return s_profiles;
325  VCHI_CONNECTION_T *vchi_connection = nullptr;
326  vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1 );
327 
328  for (auto profile : s_map)
329  {
330  char command[32];
331  char* response = nullptr;
332  int responsesize = 0;
333  QString msg = QString("codec_enabled %1").arg(profile.first);
334  if (!vc_gencmd(command, sizeof(command), msg.toLocal8Bit().constData()))
335  vc_gencmd_string_property(command, profile.first.toLocal8Bit().constData(), &response, &responsesize);
336 
337  if (response && responsesize && qstrcmp(response, "enabled") == 0)
338  s_profiles.append(profile.second);
339  }
340 
341  vc_gencmd_stop();
342  vchi_disconnect(vchi_instance);
343 
344  return s_profiles;
345 }
static bool CheckCodecSize(int Width, int Height, MythCodecContext::CodecProfile Profile)
MythPlayer * GetPlayer()
Definition: decoderbase.h:154
static const MMALProfiles & GetProfiles(void)
static void ReleaseBuffer(void *Opaque, uint8_t *Data)
#define codec_is_mmal(id)
Definition: mythcodecid.h:341
QHash< QString, Action * > Context
Definition: action.h:77
void SetDecoderOptions(AVCodecContext *Context, AVCodec *Codec) override
static Type GetInteropType(VideoFrameType Format, MythPlayer *Player)
Check whether we support direct rendering for the given VideoFrameType.
MythCodecID
Definition: mythcodecid.h:10
static bool HaveMMAL(void)
#define codec_is_mmal_dec(id)
Definition: mythcodecid.h:342
static MythCodecID GetSupportedCodec(AVCodecContext **Context, AVCodec **Codec, const QString &Decoder, AVStream *Stream, uint StreamType)
static QString GetProfileDescription(CodecProfile Profile, QSize Size, VideoFrameType Format=FMT_NONE, uint ColorDepth=0)
struct AVFrame AVFrame
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 int pitch_for_plane(VideoFrameType Type, int Width, uint Plane)
Definition: mythframe.h:344
static uint planes(VideoFrameType Type)
Definition: mythframe.h:569
~MythMMALContext() override
MythCodecID m_codecID
static bool ReinitBuffer(VideoFrame *Frame, VideoFrameType Type, MythCodecID CodecID, int Width, int Height)
bool GetBuffer2(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)
static int height_for_plane(VideoFrameType Type, int Height, uint Plane)
Definition: mythframe.h:439
MythMMALInterop * m_interop
VideoFrameType * DirectRenderFormats(void)
Return a list of frame types that can be rendered directly.
Definition: mythplayer.cpp:949
virtual void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
A decoder for video files.
static enum AVPixelFormat GetFormat(AVCodecContext *, const AVPixelFormat *PixFmt)
QList< MythCodecContext::CodecProfile > MMALProfiles
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
unsigned int uint
Definition: compat.h:140
static void copyplane(uint8_t *dst, int dst_pitch, const uint8_t *src, int src_pitch, int width, int height)
Definition: mythframe.h:539
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static void DestroyInterop(MythOpenGLInterop *Interop)
static MythRenderOpenGL * GetOpenGLRender(void)
static void GetDecoderList(QStringList &Decoders)
int HwDecoderInit(AVCodecContext *Context) override
MythCodecID GetVideoCodecID(void) const override
static MythMMALInterop * Create(MythRenderOpenGL *Context, Type InteropType)
#define LOC
bool RetrieveFrame(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame) override
MythMMALContext(DecoderBase *Parent, MythCodecID Codec)
static bool GetBuffer(AVCodecContext *Context, VideoFrame *Frame, AVFrame *AvFrame, int)