1 /*****************************************************************************
2  * mmal.c: MMAL-based decoder plugin for Raspberry Pi
3  *****************************************************************************
4  * Copyright © 2014 jusst technologies GmbH
5  * $Id: 99ff21ca701e6c600c9d61dcebe94a814d16560f $
6  *
7  * Authors: Dennis Hamester <dennis.hamester@gmail.com>
8  *          Julian Scheel <julian@jusst.de>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include <vlc_common.h>
30 #include <vlc_atomic.h>
31 #include <vlc_plugin.h>
32 #include <vlc_codec.h>
33 #include <vlc_threads.h>
34 
35 #include <bcm_host.h>
36 #include <interface/mmal/mmal.h>
37 #include <interface/mmal/util/mmal_util.h>
38 #include <interface/mmal/util/mmal_default_components.h>
39 
40 #include "mmal_picture.h"
41 
42 /*
43  * This seems to be a bit high, but reducing it causes instabilities
44  */
45 #define NUM_EXTRA_BUFFERS 5
46 #define NUM_DECODER_BUFFER_HEADERS 30
47 
48 #define MIN_NUM_BUFFERS_IN_TRANSIT 2
49 
50 #define MMAL_OPAQUE_NAME "mmal-opaque"
51 #define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.")
52 #define MMAL_OPAQUE_LONGTEXT N_("Decode frames directly into RPI VideoCore instead of host memory. This option must only be used with the MMAL video output plugin.")
53 
54 static int OpenDecoder(decoder_t *dec);
55 static void CloseDecoder(decoder_t *dec);
56 
57 vlc_module_begin()
58     set_shortname(N_("MMAL decoder"))
59     set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
60     set_capability("video decoder", 90)
61     add_shortcut("mmal_decoder")
62     add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
63     set_callbacks(OpenDecoder, CloseDecoder)
64 vlc_module_end()
65 
66 struct decoder_sys_t {
67     bool opaque;
68     MMAL_COMPONENT_T *component;
69     MMAL_PORT_T *input;
70     MMAL_POOL_T *input_pool;
71     MMAL_PORT_T *output;
72     MMAL_POOL_T *output_pool; /* only used for non-opaque mode */
73     MMAL_ES_FORMAT_T *output_format;
74     vlc_sem_t sem;
75 
76     bool b_top_field_first;
77     bool b_progressive;
78 
79     /* statistics */
80     int output_in_transit;
81     int input_in_transit;
82     atomic_bool started;
83 };
84 
85 /* Utilities */
86 static int change_output_format(decoder_t *dec);
87 static int send_output_buffer(decoder_t *dec);
88 static void fill_output_port(decoder_t *dec);
89 
90 /* VLC decoder callback */
91 static int decode(decoder_t *dec, block_t *block);
92 static void flush_decoder(decoder_t *dec);
93 
94 /* MMAL callbacks */
95 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
96 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
97 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
98 
OpenDecoder(decoder_t * dec)99 static int OpenDecoder(decoder_t *dec)
100 {
101     int ret = VLC_SUCCESS;
102     decoder_sys_t *sys;
103     MMAL_PARAMETER_UINT32_T extra_buffers;
104     MMAL_STATUS_T status;
105 
106     if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
107             dec->fmt_in.i_codec != VLC_CODEC_H264)
108         return VLC_EGENERIC;
109 
110     sys = calloc(1, sizeof(decoder_sys_t));
111     if (!sys) {
112         ret = VLC_ENOMEM;
113         goto out;
114     }
115     dec->p_sys = sys;
116 
117     sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME);
118     bcm_host_init();
119 
120     status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
121     if (status != MMAL_SUCCESS) {
122         msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
123                 MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
124         ret = VLC_EGENERIC;
125         goto out;
126     }
127 
128     sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
129     status = mmal_port_enable(sys->component->control, control_port_cb);
130     if (status != MMAL_SUCCESS) {
131         msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
132                 sys->component->control->name, status, mmal_status_to_string(status));
133         ret = VLC_EGENERIC;
134         goto out;
135     }
136 
137     sys->input = sys->component->input[0];
138     sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
139     if (dec->fmt_in.i_codec == VLC_CODEC_MPGV)
140         sys->input->format->encoding = MMAL_ENCODING_MP2V;
141     else
142         sys->input->format->encoding = MMAL_ENCODING_H264;
143 
144     if (dec->fmt_in.i_codec == VLC_CODEC_H264) {
145         if (dec->fmt_in.i_extra > 0) {
146             status = mmal_format_extradata_alloc(sys->input->format,
147                     dec->fmt_in.i_extra);
148             if (status == MMAL_SUCCESS) {
149                 memcpy(sys->input->format->extradata, dec->fmt_in.p_extra,
150                         dec->fmt_in.i_extra);
151                 sys->input->format->extradata_size = dec->fmt_in.i_extra;
152             } else {
153                 msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)",
154                         sys->input->name, status, mmal_status_to_string(status));
155             }
156         }
157     }
158 
159     status = mmal_port_format_commit(sys->input);
160     if (status != MMAL_SUCCESS) {
161         msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
162                 sys->input->name, status, mmal_status_to_string(status));
163         ret = VLC_EGENERIC;
164         goto out;
165     }
166     sys->input->buffer_size = sys->input->buffer_size_recommended;
167     sys->input->buffer_num = sys->input->buffer_num_recommended;
168 
169     status = mmal_port_enable(sys->input, input_port_cb);
170     if (status != MMAL_SUCCESS) {
171         msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
172                 sys->input->name, status, mmal_status_to_string(status));
173         ret = VLC_EGENERIC;
174         goto out;
175     }
176 
177     sys->output = sys->component->output[0];
178     sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
179 
180     if (sys->opaque) {
181         extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
182         extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
183         extra_buffers.value = NUM_EXTRA_BUFFERS;
184         status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
185         if (status != MMAL_SUCCESS) {
186             msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
187                     status, mmal_status_to_string(status));
188             ret = VLC_EGENERIC;
189             goto out;
190         }
191 
192         msg_Dbg(dec, "Activate zero-copy for output port");
193         MMAL_PARAMETER_BOOLEAN_T zero_copy = {
194             { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
195             1
196         };
197 
198         status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
199         if (status != MMAL_SUCCESS) {
200            msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
201                     sys->output->name, status, mmal_status_to_string(status));
202            goto out;
203         }
204     }
205 
206     status = mmal_port_enable(sys->output, output_port_cb);
207     if (status != MMAL_SUCCESS) {
208         msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)",
209                 sys->output->name, status, mmal_status_to_string(status));
210         ret = VLC_EGENERIC;
211         goto out;
212     }
213 
214     status = mmal_component_enable(sys->component);
215     if (status != MMAL_SUCCESS) {
216         msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
217                 sys->component->name, status, mmal_status_to_string(status));
218         ret = VLC_EGENERIC;
219         goto out;
220     }
221 
222     sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
223 
224     if (sys->opaque) {
225         dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
226         dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
227     } else {
228         dec->fmt_out.i_codec = VLC_CODEC_I420;
229         dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
230     }
231 
232     dec->pf_decode = decode;
233     dec->pf_flush  = flush_decoder;
234 
235     vlc_sem_init(&sys->sem, 0);
236 
237 out:
238     if (ret != VLC_SUCCESS)
239         CloseDecoder(dec);
240 
241     return ret;
242 }
243 
CloseDecoder(decoder_t * dec)244 static void CloseDecoder(decoder_t *dec)
245 {
246     decoder_sys_t *sys = dec->p_sys;
247     MMAL_BUFFER_HEADER_T *buffer;
248 
249     if (!sys)
250         return;
251 
252     if (sys->component && sys->component->control->is_enabled)
253         mmal_port_disable(sys->component->control);
254 
255     if (sys->input && sys->input->is_enabled)
256         mmal_port_disable(sys->input);
257 
258     if (sys->output && sys->output->is_enabled)
259         mmal_port_disable(sys->output);
260 
261     if (sys->component && sys->component->is_enabled)
262         mmal_component_disable(sys->component);
263 
264     if (sys->input_pool)
265         mmal_pool_destroy(sys->input_pool);
266 
267     if (sys->output_format)
268         mmal_format_free(sys->output_format);
269 
270     if (sys->output_pool)
271         mmal_pool_destroy(sys->output_pool);
272 
273     if (sys->component)
274         mmal_component_release(sys->component);
275 
276     vlc_sem_destroy(&sys->sem);
277     free(sys);
278 
279     bcm_host_deinit();
280 }
281 
change_output_format(decoder_t * dec)282 static int change_output_format(decoder_t *dec)
283 {
284     MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type;
285     decoder_sys_t *sys = dec->p_sys;
286     MMAL_STATUS_T status;
287     int pool_size;
288     int ret = 0;
289 
290     if (atomic_load(&sys->started)) {
291         mmal_format_full_copy(sys->output->format, sys->output_format);
292         status = mmal_port_format_commit(sys->output);
293         if (status != MMAL_SUCCESS) {
294             msg_Err(dec, "Failed to commit output format (status=%"PRIx32" %s)",
295                     status, mmal_status_to_string(status));
296             ret = -1;
297             goto port_reset;
298         }
299         goto apply_fmt;
300     }
301 
302 port_reset:
303     msg_Dbg(dec, "%s: Do full port reset", __func__);
304     status = mmal_port_disable(sys->output);
305     if (status != MMAL_SUCCESS) {
306         msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)",
307                 status, mmal_status_to_string(status));
308         ret = -1;
309         goto out;
310     }
311 
312     mmal_format_full_copy(sys->output->format, sys->output_format);
313     status = mmal_port_format_commit(sys->output);
314     if (status != MMAL_SUCCESS) {
315         msg_Err(dec, "Failed to commit output format (status=%"PRIx32" %s)",
316                 status, mmal_status_to_string(status));
317         ret = -1;
318         goto out;
319     }
320 
321     if (sys->opaque) {
322         sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
323         pool_size = NUM_DECODER_BUFFER_HEADERS;
324     } else {
325         sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended,
326                 MIN_NUM_BUFFERS_IN_TRANSIT);
327         pool_size = sys->output->buffer_num;
328     }
329 
330     sys->output->buffer_size = sys->output->buffer_size_recommended;
331 
332     status = mmal_port_enable(sys->output, output_port_cb);
333     if (status != MMAL_SUCCESS) {
334         msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)",
335                 status, mmal_status_to_string(status));
336         ret = -1;
337         goto out;
338     }
339 
340     if (!atomic_load(&sys->started)) {
341         if (!sys->opaque) {
342             sys->output_pool = mmal_port_pool_create(sys->output, pool_size, 0);
343             msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num);
344         }
345 
346         atomic_store(&sys->started, true);
347 
348         /* we need one picture from vout for each buffer header on the output
349          * port */
350         dec->i_extra_picture_buffers = pool_size;
351 
352         /* remove what VLC core reserves as it is part of the pool_size
353          * already */
354         if (dec->fmt_in.i_codec == VLC_CODEC_H264)
355             dec->i_extra_picture_buffers -= 19;
356         else
357             dec->i_extra_picture_buffers -= 3;
358 
359         msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers);
360     }
361 
362 apply_fmt:
363     dec->fmt_out.video.i_width = sys->output->format->es->video.width;
364     dec->fmt_out.video.i_height = sys->output->format->es->video.height;
365     dec->fmt_out.video.i_x_offset = sys->output->format->es->video.crop.x;
366     dec->fmt_out.video.i_y_offset = sys->output->format->es->video.crop.y;
367     dec->fmt_out.video.i_visible_width = sys->output->format->es->video.crop.width;
368     dec->fmt_out.video.i_visible_height = sys->output->format->es->video.crop.height;
369     dec->fmt_out.video.i_sar_num = sys->output->format->es->video.par.num;
370     dec->fmt_out.video.i_sar_den = sys->output->format->es->video.par.den;
371     dec->fmt_out.video.i_frame_rate = sys->output->format->es->video.frame_rate.num;
372     dec->fmt_out.video.i_frame_rate_base = sys->output->format->es->video.frame_rate.den;
373 
374     /* Query interlaced type */
375     interlace_type.hdr.id = MMAL_PARAMETER_VIDEO_INTERLACE_TYPE;
376     interlace_type.hdr.size = sizeof(MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T);
377     status = mmal_port_parameter_get(sys->output, &interlace_type.hdr);
378     if (status != MMAL_SUCCESS) {
379         msg_Warn(dec, "Failed to query interlace type from decoder output port (status=%"PRIx32" %s)",
380                 status, mmal_status_to_string(status));
381     } else {
382         sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive);
383         sys->b_top_field_first = sys->b_progressive ? true :
384             (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst);
385         msg_Dbg(dec, "Detected %s%s video (%d)",
386                 sys->b_progressive ? "progressive" : "interlaced",
387                 sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"),
388                 interlace_type.eMode);
389     }
390 
391 out:
392     mmal_format_free(sys->output_format);
393     sys->output_format = NULL;
394 
395     return ret;
396 }
397 
send_output_buffer(decoder_t * dec)398 static int send_output_buffer(decoder_t *dec)
399 {
400     decoder_sys_t *sys = dec->p_sys;
401     MMAL_BUFFER_HEADER_T *buffer;
402     picture_sys_t *p_sys;
403     picture_t *picture = NULL;
404     MMAL_STATUS_T status;
405     unsigned buffer_size = 0;
406     int ret = 0;
407 
408     if (!sys->output->is_enabled)
409         return VLC_EGENERIC;
410 
411     /* If local output pool is allocated, use it - this is only the case for
412      * non-opaque modes */
413     if (sys->output_pool) {
414         buffer = mmal_queue_get(sys->output_pool->queue);
415         if (!buffer) {
416             msg_Warn(dec, "Failed to get new buffer");
417             return VLC_EGENERIC;
418         }
419     }
420 
421     if (!decoder_UpdateVideoFormat(dec))
422         picture = decoder_NewPicture(dec);
423     if (!picture) {
424         msg_Warn(dec, "Failed to get new picture");
425         ret = -1;
426         goto err;
427     }
428 
429     p_sys = picture->p_sys;
430     for (int i = 0; i < picture->i_planes; i++)
431         buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch;
432 
433     if (sys->output_pool) {
434         mmal_buffer_header_reset(buffer);
435         buffer->alloc_size = sys->output->buffer_size;
436         if (buffer_size < sys->output->buffer_size) {
437             msg_Err(dec, "Retrieved picture with too small data block (%d < %d)",
438                     buffer_size, sys->output->buffer_size);
439             ret = VLC_EGENERIC;
440             goto err;
441         }
442 
443         if (!sys->opaque)
444             buffer->data = picture->p[0].p_pixels;
445     } else {
446         buffer = p_sys->buffer;
447         if (!buffer) {
448             msg_Warn(dec, "Picture has no buffer attached");
449             picture_Release(picture);
450             return VLC_EGENERIC;
451         }
452         buffer->data = p_sys->buffer->data;
453     }
454     buffer->user_data = picture;
455     buffer->cmd = 0;
456 
457     status = mmal_port_send_buffer(sys->output, buffer);
458     if (status != MMAL_SUCCESS) {
459         msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)",
460                 status, mmal_status_to_string(status));
461         ret = -1;
462         goto err;
463     }
464     atomic_fetch_add(&sys->output_in_transit, 1);
465 
466     return ret;
467 
468 err:
469     if (picture)
470         picture_Release(picture);
471     if (sys->output_pool && buffer) {
472         buffer->data = NULL;
473         mmal_buffer_header_release(buffer);
474     }
475     return ret;
476 }
477 
fill_output_port(decoder_t * dec)478 static void fill_output_port(decoder_t *dec)
479 {
480     decoder_sys_t *sys = dec->p_sys;
481 
482     unsigned max_buffers_in_transit = 0;
483     int buffers_available = 0;
484     int buffers_to_send = 0;
485     int i;
486 
487     if (sys->output_pool) {
488         max_buffers_in_transit = __MAX(sys->output_pool->headers_num,
489                 MIN_NUM_BUFFERS_IN_TRANSIT);
490         buffers_available = mmal_queue_length(sys->output_pool->queue);
491     } else {
492         max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS;
493         buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit);
494     }
495     buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit);
496 
497     if (buffers_to_send > buffers_available)
498         buffers_to_send = buffers_available;
499 
500 #ifndef NDEBUG
501     msg_Dbg(dec, "Send %d buffers to output port (available: %d, "
502                     "in_transit: %d, buffer_num: %d)",
503                     buffers_to_send, buffers_available,
504                     atomic_load(&sys->output_in_transit),
505                     sys->output->buffer_num);
506 #endif
507     for (i = 0; i < buffers_to_send; ++i)
508         if (send_output_buffer(dec) < 0)
509             break;
510 }
511 
flush_decoder(decoder_t * dec)512 static void flush_decoder(decoder_t *dec)
513 {
514     decoder_sys_t *sys = dec->p_sys;
515     MMAL_BUFFER_HEADER_T *buffer;
516     MMAL_STATUS_T status;
517 
518     msg_Dbg(dec, "Flushing decoder ports...");
519     mmal_port_flush(sys->output);
520     mmal_port_flush(sys->input);
521 
522     while (atomic_load(&sys->output_in_transit) ||
523            atomic_load(&sys->input_in_transit))
524         vlc_sem_wait(&sys->sem);
525 }
526 
decode(decoder_t * dec,block_t * block)527 static int decode(decoder_t *dec, block_t *block)
528 {
529     decoder_sys_t *sys = dec->p_sys;
530     MMAL_BUFFER_HEADER_T *buffer;
531     bool need_flush = false;
532     uint32_t len;
533     uint32_t flags = 0;
534     MMAL_STATUS_T status;
535 
536     /*
537      * Configure output port if necessary
538      */
539     if (sys->output_format) {
540         if (change_output_format(dec) < 0)
541             msg_Err(dec, "Failed to change output port format");
542     }
543 
544     if (!block)
545         goto out;
546 
547     /*
548      * Check whether full flush is required
549      */
550     if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
551         flush_decoder(dec);
552         block_Release(block);
553         return VLCDEC_SUCCESS;
554     }
555 
556     if (atomic_load(&sys->started))
557         fill_output_port(dec);
558 
559     /*
560      * Process input
561      */
562 
563     if (block->i_flags & BLOCK_FLAG_CORRUPTED)
564         flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
565 
566     while (block && block->i_buffer > 0) {
567         buffer = mmal_queue_timedwait(sys->input_pool->queue, 100);
568         if (!buffer) {
569             msg_Err(dec, "Failed to retrieve buffer header for input data");
570             need_flush = true;
571             break;
572         }
573         mmal_buffer_header_reset(buffer);
574         buffer->cmd = 0;
575         buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts;
576         buffer->dts = block->i_dts;
577         buffer->alloc_size = sys->input->buffer_size;
578 
579         len = block->i_buffer;
580         if (len > buffer->alloc_size)
581             len = buffer->alloc_size;
582 
583         buffer->data = block->p_buffer;
584         block->p_buffer += len;
585         block->i_buffer -= len;
586         buffer->length = len;
587         if (block->i_buffer == 0) {
588             buffer->user_data = block;
589             block = NULL;
590         }
591         buffer->flags = flags;
592 
593         status = mmal_port_send_buffer(sys->input, buffer);
594         if (status != MMAL_SUCCESS) {
595             msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)",
596                     status, mmal_status_to_string(status));
597             break;
598         }
599         atomic_fetch_add(&sys->input_in_transit, 1);
600     }
601 
602 out:
603     if (need_flush)
604         flush_decoder(dec);
605 
606     return VLCDEC_SUCCESS;
607 }
608 
control_port_cb(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)609 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
610 {
611     decoder_t *dec = (decoder_t *)port->userdata;
612     MMAL_STATUS_T status;
613 
614     if (buffer->cmd == MMAL_EVENT_ERROR) {
615         status = *(uint32_t *)buffer->data;
616         msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
617                 mmal_status_to_string(status));
618     }
619 
620     mmal_buffer_header_release(buffer);
621 }
622 
input_port_cb(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)623 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
624 {
625     block_t *block = (block_t *)buffer->user_data;
626     decoder_t *dec = (decoder_t *)port->userdata;
627     decoder_sys_t *sys = dec->p_sys;
628     buffer->user_data = NULL;
629 
630     mmal_buffer_header_release(buffer);
631     if (block)
632         block_Release(block);
633     atomic_fetch_sub(&sys->input_in_transit, 1);
634     vlc_sem_post(&sys->sem);
635 }
636 
output_port_cb(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)637 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
638 {
639     decoder_t *dec = (decoder_t *)port->userdata;
640     decoder_sys_t *sys = dec->p_sys;
641     picture_t *picture;
642     MMAL_EVENT_FORMAT_CHANGED_T *fmt;
643     MMAL_ES_FORMAT_T *format;
644 
645     if (buffer->cmd == 0) {
646         picture = (picture_t *)buffer->user_data;
647         if (buffer->length > 0) {
648             picture->date = buffer->pts;
649             picture->b_progressive = sys->b_progressive;
650             picture->b_top_field_first = sys->b_top_field_first;
651             decoder_QueueVideo(dec, picture);
652         } else {
653             picture_Release(picture);
654             if (sys->output_pool) {
655                 buffer->user_data = NULL;
656                 buffer->alloc_size = 0;
657                 buffer->data = NULL;
658                 mmal_buffer_header_release(buffer);
659             }
660         }
661         atomic_fetch_sub(&sys->output_in_transit, 1);
662         vlc_sem_post(&sys->sem);
663     } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
664         fmt = mmal_event_format_changed_get(buffer);
665 
666         format = mmal_format_alloc();
667         mmal_format_full_copy(format, fmt->format);
668 
669         if (sys->opaque)
670             format->encoding = MMAL_ENCODING_OPAQUE;
671 
672         sys->output_format = format;
673 
674         mmal_buffer_header_release(buffer);
675     } else {
676         mmal_buffer_header_release(buffer);
677     }
678 }
679