1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include "mmal.h"
29#include "core/mmal_component_private.h"
30#include "core/mmal_port_private.h"
31#include "mmal_logging.h"
32
33#include "containers/containers.h"
34#include "containers/containers_codecs.h"
35#include "containers/core/containers_utils.h"
36
37#define READER_MAX_URI_LENGTH 1024
38
39#define WRITER_PORTS_NUM 3 /**< 3 ports should be enough for video + audio + subpicture */
40
41/* Buffering requirements */
42#define READER_MIN_BUFFER_SIZE (2*1024)
43#define READER_MIN_BUFFER_NUM 1
44#define READER_RECOMMENDED_BUFFER_SIZE (32*1024)
45#define READER_RECOMMENDED_BUFFER_NUM 10
46
47/*****************************************************************************/
48
49/** Private context for this component */
50typedef struct MMAL_COMPONENT_MODULE_T
51{
52 VC_CONTAINER_T *container;
53 char uri[READER_MAX_URI_LENGTH+1];
54 unsigned int ports;
55
56 MMAL_BOOL_T writer;
57 MMAL_BOOL_T error;
58
59 /* Reader specific */
60 MMAL_BOOL_T packet_logged;
61
62 /* Writer specific */
63 unsigned int port_last_used;
64 unsigned int port_writing_frame;
65
66} MMAL_COMPONENT_MODULE_T;
67
68typedef struct MMAL_PORT_MODULE_T
69{
70 unsigned int track;
71 MMAL_QUEUE_T *queue;
72
73 MMAL_BOOL_T flush;
74 MMAL_BOOL_T eos;
75
76 VC_CONTAINER_ES_FORMAT_T *format; /**< Format description for the elementary stream */
77
78} MMAL_PORT_MODULE_T;
79
80/*****************************************************************************/
81static struct {
82 VC_CONTAINER_FOURCC_T codec;
83 MMAL_FOURCC_T encoding;
84 VC_CONTAINER_FOURCC_T codec_variant;
85 MMAL_FOURCC_T encoding_variant;
86} encoding_table[] =
87{
88 {VC_CONTAINER_CODEC_H263, MMAL_ENCODING_H263, 0, 0},
89 {VC_CONTAINER_CODEC_H264, MMAL_ENCODING_H264, 0, 0},
90 {VC_CONTAINER_CODEC_H264, MMAL_ENCODING_H264,
91 VC_CONTAINER_VARIANT_H264_AVC1, MMAL_ENCODING_VARIANT_H264_AVC1},
92 {VC_CONTAINER_CODEC_H264, MMAL_ENCODING_H264,
93 VC_CONTAINER_VARIANT_H264_RAW, MMAL_ENCODING_VARIANT_H264_RAW},
94 {VC_CONTAINER_CODEC_MP4V, MMAL_ENCODING_MP4V, 0, 0},
95 {VC_CONTAINER_CODEC_MP2V, MMAL_ENCODING_MP2V, 0, 0},
96 {VC_CONTAINER_CODEC_MP1V, MMAL_ENCODING_MP1V, 0, 0},
97 {VC_CONTAINER_CODEC_WMV3, MMAL_ENCODING_WMV3, 0, 0},
98 {VC_CONTAINER_CODEC_WMV2, MMAL_ENCODING_WMV2, 0, 0},
99 {VC_CONTAINER_CODEC_WMV1, MMAL_ENCODING_WMV1, 0, 0},
100 {VC_CONTAINER_CODEC_WVC1, MMAL_ENCODING_WVC1, 0, 0},
101 {VC_CONTAINER_CODEC_VP6, MMAL_ENCODING_VP6, 0, 0},
102 {VC_CONTAINER_CODEC_VP7, MMAL_ENCODING_VP7, 0, 0},
103 {VC_CONTAINER_CODEC_VP8, MMAL_ENCODING_VP8, 0, 0},
104 {VC_CONTAINER_CODEC_THEORA, MMAL_ENCODING_THEORA, 0, 0},
105 {VC_CONTAINER_CODEC_SPARK, MMAL_ENCODING_SPARK, 0, 0},
106
107 {VC_CONTAINER_CODEC_GIF, MMAL_ENCODING_GIF, 0, 0},
108 {VC_CONTAINER_CODEC_JPEG, MMAL_ENCODING_JPEG, 0, 0},
109 {VC_CONTAINER_CODEC_PNG, MMAL_ENCODING_PNG, 0, 0},
110 {VC_CONTAINER_CODEC_PPM, MMAL_ENCODING_PPM, 0, 0},
111 {VC_CONTAINER_CODEC_TGA, MMAL_ENCODING_TGA, 0, 0},
112 {VC_CONTAINER_CODEC_BMP, MMAL_ENCODING_BMP, 0, 0},
113
114 {VC_CONTAINER_CODEC_PCM_SIGNED_BE, MMAL_ENCODING_PCM_SIGNED_BE, 0, 0},
115 {VC_CONTAINER_CODEC_PCM_UNSIGNED_BE,MMAL_ENCODING_PCM_UNSIGNED_BE, 0, 0},
116 {VC_CONTAINER_CODEC_PCM_SIGNED_LE, MMAL_ENCODING_PCM_SIGNED_LE, 0, 0},
117 {VC_CONTAINER_CODEC_PCM_UNSIGNED_LE,MMAL_ENCODING_PCM_UNSIGNED_LE, 0, 0},
118 {VC_CONTAINER_CODEC_PCM_FLOAT_BE, MMAL_ENCODING_PCM_FLOAT_BE, 0, 0},
119 {VC_CONTAINER_CODEC_PCM_FLOAT_LE, MMAL_ENCODING_PCM_FLOAT_LE, 0, 0},
120
121 {VC_CONTAINER_CODEC_MPGA, MMAL_ENCODING_MPGA, 0, 0},
122 {VC_CONTAINER_CODEC_MP4A, MMAL_ENCODING_MP4A, 0, 0},
123 {VC_CONTAINER_CODEC_ALAW, MMAL_ENCODING_ALAW, 0, 0},
124 {VC_CONTAINER_CODEC_MULAW, MMAL_ENCODING_MULAW, 0, 0},
125 {VC_CONTAINER_CODEC_ADPCM_MS, MMAL_ENCODING_ADPCM_MS, 0, 0},
126 {VC_CONTAINER_CODEC_ADPCM_IMA_MS, MMAL_ENCODING_ADPCM_IMA_MS, 0, 0},
127 {VC_CONTAINER_CODEC_ADPCM_SWF, MMAL_ENCODING_ADPCM_SWF, 0, 0},
128 {VC_CONTAINER_CODEC_WMA1, MMAL_ENCODING_WMA1, 0, 0},
129 {VC_CONTAINER_CODEC_WMA2, MMAL_ENCODING_WMA2, 0, 0},
130 {VC_CONTAINER_CODEC_WMAP, MMAL_ENCODING_WMAP, 0, 0},
131 {VC_CONTAINER_CODEC_WMAL, MMAL_ENCODING_WMAL, 0, 0},
132 {VC_CONTAINER_CODEC_WMAV, MMAL_ENCODING_WMAV, 0, 0},
133 {VC_CONTAINER_CODEC_AMRNB, MMAL_ENCODING_AMRNB, 0, 0},
134 {VC_CONTAINER_CODEC_AMRWB, MMAL_ENCODING_AMRWB, 0, 0},
135 {VC_CONTAINER_CODEC_AMRWBP, MMAL_ENCODING_AMRWBP, 0, 0},
136 {VC_CONTAINER_CODEC_AC3, MMAL_ENCODING_AC3, 0, 0},
137 {VC_CONTAINER_CODEC_EAC3, MMAL_ENCODING_EAC3, 0, 0},
138 {VC_CONTAINER_CODEC_DTS, MMAL_ENCODING_DTS, 0, 0},
139 {VC_CONTAINER_CODEC_MLP, MMAL_ENCODING_MLP, 0, 0},
140 {VC_CONTAINER_CODEC_FLAC, MMAL_ENCODING_FLAC, 0, 0},
141 {VC_CONTAINER_CODEC_VORBIS, MMAL_ENCODING_VORBIS, 0, 0},
142 {VC_CONTAINER_CODEC_SPEEX, MMAL_ENCODING_SPEEX, 0, 0},
143 {VC_CONTAINER_CODEC_ATRAC3, MMAL_ENCODING_ATRAC3, 0, 0},
144 {VC_CONTAINER_CODEC_ATRACX, MMAL_ENCODING_ATRACX, 0, 0},
145 {VC_CONTAINER_CODEC_ATRACL, MMAL_ENCODING_ATRACL, 0, 0},
146 {VC_CONTAINER_CODEC_MIDI, MMAL_ENCODING_MIDI, 0, 0},
147 {VC_CONTAINER_CODEC_EVRC, MMAL_ENCODING_EVRC, 0, 0},
148 {VC_CONTAINER_CODEC_NELLYMOSER, MMAL_ENCODING_NELLYMOSER, 0, 0},
149 {VC_CONTAINER_CODEC_QCELP, MMAL_ENCODING_QCELP, 0, 0},
150
151 {VC_CONTAINER_CODEC_UNKNOWN, MMAL_ENCODING_UNKNOWN, 0, 0}
152};
153
154static MMAL_FOURCC_T container_to_mmal_encoding(VC_CONTAINER_FOURCC_T codec)
155{
156 unsigned int i;
157 for(i = 0; encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
158 if(encoding_table[i].codec == codec)
159 break;
160 return encoding_table[i].encoding;
161}
162
163static VC_CONTAINER_FOURCC_T mmal_to_container_encoding(uint32_t encoding)
164{
165 unsigned int i;
166 for(i = 0; encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
167 if(encoding_table[i].encoding == encoding)
168 break;
169 return encoding_table[i].codec;
170}
171
172static MMAL_FOURCC_T container_to_mmal_variant(VC_CONTAINER_FOURCC_T codec,
173 VC_CONTAINER_FOURCC_T codec_variant)
174{
175 unsigned int i;
176 for(i = 0; encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
177 if(encoding_table[i].codec == codec &&
178 encoding_table[i].codec_variant == codec_variant)
179 break;
180 return encoding_table[i].encoding_variant;
181}
182
183static VC_CONTAINER_FOURCC_T mmal_to_container_variant(MMAL_FOURCC_T encoding,
184 MMAL_FOURCC_T encoding_variant)
185{
186 unsigned int i;
187 for(i = 0; encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
188 if(encoding_table[i].encoding == encoding &&
189 encoding_table[i].encoding_variant == encoding_variant)
190 break;
191 return encoding_table[i].codec_variant;
192}
193
194/*****************************************************************************/
195static struct {
196 VC_CONTAINER_ES_TYPE_T container_type;
197 MMAL_ES_TYPE_T type;
198} es_type_table[] =
199{
200 {VC_CONTAINER_ES_TYPE_VIDEO, MMAL_ES_TYPE_VIDEO},
201 {VC_CONTAINER_ES_TYPE_AUDIO, MMAL_ES_TYPE_AUDIO},
202 {VC_CONTAINER_ES_TYPE_SUBPICTURE, MMAL_ES_TYPE_SUBPICTURE},
203 {VC_CONTAINER_ES_TYPE_UNKNOWN, MMAL_ES_TYPE_UNKNOWN}
204};
205
206static MMAL_ES_TYPE_T container_to_mmal_es_type(VC_CONTAINER_ES_TYPE_T type)
207{
208 unsigned int i;
209 for(i = 0; es_type_table[i].container_type != VC_CONTAINER_ES_TYPE_UNKNOWN; i++)
210 if(es_type_table[i].container_type == type)
211 break;
212 return es_type_table[i].type;
213}
214
215static VC_CONTAINER_ES_TYPE_T mmal_to_container_es_type(MMAL_ES_TYPE_T type)
216{
217 unsigned int i;
218 for(i = 0; es_type_table[i].container_type != VC_CONTAINER_ES_TYPE_UNKNOWN; i++)
219 if(es_type_table[i].type == type)
220 break;
221 return es_type_table[i].container_type;
222}
223
224static MMAL_STATUS_T container_map_to_mmal_status(VC_CONTAINER_STATUS_T cstatus)
225{
226 switch (cstatus)
227 {
228 case VC_CONTAINER_SUCCESS: return MMAL_SUCCESS;
229 case VC_CONTAINER_ERROR_CORRUPTED: return MMAL_ECORRUPT;
230 case VC_CONTAINER_ERROR_OUT_OF_MEMORY: return MMAL_ENOMEM;
231 case VC_CONTAINER_ERROR_OUT_OF_RESOURCES: return MMAL_ENOSPC;
232 case VC_CONTAINER_ERROR_NOT_READY: return MMAL_ENOTREADY;
233 case VC_CONTAINER_ERROR_NOT_FOUND: return MMAL_ENOENT;
234 case VC_CONTAINER_ERROR_URI_NOT_FOUND: return MMAL_ENOENT;
235 default: return MMAL_EINVAL;
236 }
237}
238
239static MMAL_STATUS_T container_to_mmal_format(MMAL_ES_FORMAT_T *format,
240 VC_CONTAINER_ES_FORMAT_T *container_format)
241{
242 format->type = container_to_mmal_es_type(container_format->es_type);
243 if(format->type == MMAL_ES_TYPE_UNKNOWN)
244 return MMAL_EINVAL;
245
246 format->encoding = container_to_mmal_encoding(container_format->codec);
247 format->encoding_variant = container_to_mmal_variant(container_format->codec, container_format->codec_variant);
248 format->bitrate = container_format->bitrate;
249 format->flags = (container_format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED) ?
250 MMAL_ES_FORMAT_FLAG_FRAMED : 0;
251 memset(format->es, 0, sizeof(*format->es));
252
253 switch(format->type)
254 {
255 case MMAL_ES_TYPE_VIDEO:
256 format->es->video.width = container_format->type->video.width;
257 format->es->video.height = container_format->type->video.height;
258 format->es->video.crop.width = container_format->type->video.visible_width;
259 format->es->video.crop.height = container_format->type->video.visible_height;
260 format->es->video.frame_rate.num = container_format->type->video.frame_rate_num;
261 format->es->video.frame_rate.den = container_format->type->video.frame_rate_den;
262 format->es->video.par.num = container_format->type->video.par_num;
263 format->es->video.par.den = container_format->type->video.par_den;
264 break;
265 case MMAL_ES_TYPE_AUDIO:
266 format->es->audio.channels = container_format->type->audio.channels;
267 format->es->audio.sample_rate = container_format->type->audio.sample_rate;
268 format->es->audio.bits_per_sample = container_format->type->audio.bits_per_sample;
269 format->es->audio.block_align = container_format->type->audio.block_align;
270 break;
271 default:
272 LOG_ERROR("format es type not handled (%i)", (int)format->type);
273 break;
274 }
275
276 if(container_format->extradata_size)
277 {
278 MMAL_STATUS_T status = mmal_format_extradata_alloc(format, container_format->extradata_size);
279 if(status != MMAL_SUCCESS)
280 {
281 LOG_ERROR("couldn't allocate extradata");
282 return status;
283 }
284 format->extradata_size = container_format->extradata_size;
285 memcpy(format->extradata, container_format->extradata, format->extradata_size);
286 }
287
288 return MMAL_SUCCESS;
289}
290
291static MMAL_STATUS_T mmal_to_container_format(VC_CONTAINER_ES_FORMAT_T *container_format,
292 MMAL_ES_FORMAT_T *format)
293{
294 container_format->es_type = mmal_to_container_es_type(format->type);
295 if(container_format->es_type == VC_CONTAINER_ES_TYPE_UNKNOWN)
296 return MMAL_EINVAL;
297
298 container_format->codec = mmal_to_container_encoding(format->encoding);
299 container_format->codec_variant = mmal_to_container_variant(format->encoding, format->encoding_variant);
300 container_format->bitrate = format->bitrate;
301 container_format->flags = 0;
302 if(format->flags & MMAL_ES_FORMAT_FLAG_FRAMED)
303 container_format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
304 memset(container_format->type, 0, sizeof(*container_format->type));
305
306 /* Auto-detect H264 AVC1 variant */
307 if(format->encoding == MMAL_ENCODING_H264 && !format->encoding_variant &&
308 format->extradata_size >= 5 && *format->extradata == 1)
309 container_format->codec_variant = VC_CONTAINER_VARIANT_H264_AVC1;
310
311 switch(format->type)
312 {
313 case MMAL_ES_TYPE_VIDEO:
314 container_format->type->video.width = format->es->video.width;
315 container_format->type->video.height = format->es->video.height;
316 container_format->type->video.frame_rate_num = format->es->video.frame_rate.num;
317 container_format->type->video.frame_rate_den = format->es->video.frame_rate.den;
318 container_format->type->video.par_num = format->es->video.par.num;
319 container_format->type->video.par_den = format->es->video.par.den;
320 break;
321 case MMAL_ES_TYPE_AUDIO:
322 container_format->type->audio.channels = format->es->audio.channels;
323 container_format->type->audio.sample_rate = format->es->audio.sample_rate;
324 container_format->type->audio.bits_per_sample = format->es->audio.bits_per_sample;
325 container_format->type->audio.block_align = format->es->audio.block_align;
326 break;
327 default:
328 LOG_ERROR("format es type not handled (%i)", (int)format->type);
329 break;
330 }
331
332 container_format->extradata_size = format->extradata_size;
333 container_format->extradata = format->extradata;
334
335 return MMAL_SUCCESS;
336}
337
338/*****************************************************************************/
339static void reader_do_processing(MMAL_COMPONENT_T *component)
340{
341 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
342 MMAL_BUFFER_HEADER_T *buffer;
343 VC_CONTAINER_STATUS_T cstatus;
344 VC_CONTAINER_PACKET_T packet;
345 MMAL_STATUS_T status;
346 unsigned int i;
347
348 memset(&packet, 0, sizeof(packet));
349
350 while(1)
351 {
352 cstatus = vc_container_read(module->container, &packet, VC_CONTAINER_READ_FLAG_INFO);
353 if(cstatus == VC_CONTAINER_ERROR_CONTINUE)
354 continue;
355 if(cstatus != VC_CONTAINER_SUCCESS)
356 {
357 LOG_DEBUG("READ EOF (%i)", cstatus);
358 break;
359 }
360
361 if (!module->packet_logged)
362 LOG_DEBUG("packet info: track %i, size %i/%i, pts %"PRId64"%s, dts %"PRId64"%s, flags %x%s",
363 packet.track, packet.size, packet.frame_size,
364 packet.pts == VC_CONTAINER_TIME_UNKNOWN ? 0 : packet.pts,
365 packet.pts == VC_CONTAINER_TIME_UNKNOWN ? ":unknown" : "",
366 packet.dts == VC_CONTAINER_TIME_UNKNOWN ? 0 : packet.dts,
367 packet.dts == VC_CONTAINER_TIME_UNKNOWN ? ":unknown" : "",
368 packet.flags, (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "");
369
370 /* Find the port corresponding to that track */
371 for(i = 0; i < module->ports; i++)
372 if(component->output[i]->priv->module->track == packet.track)
373 break;
374 if(i == module->ports)
375 {
376 vc_container_read(module->container, 0, VC_CONTAINER_READ_FLAG_SKIP);
377 continue;
378 }
379
380 /* Get a buffer from this port */
381 buffer = mmal_queue_get(component->output[i]->priv->module->queue);
382 if(!buffer)
383 {
384 module->packet_logged = 1;
385 break; /* Try again next time */
386 }
387 module->packet_logged = 0;
388
389 if(component->output[i]->priv->module->flush)
390 {
391 buffer->length = 0;
392 component->output[i]->priv->module->flush = MMAL_FALSE;
393 }
394
395 mmal_buffer_header_mem_lock(buffer);
396 packet.data = buffer->data + buffer->length;
397 packet.buffer_size = buffer->alloc_size - buffer->length;
398 packet.size = 0;
399 cstatus = vc_container_read(module->container, &packet, 0);
400 mmal_buffer_header_mem_unlock(buffer);
401 if(cstatus != VC_CONTAINER_SUCCESS)
402 {
403 LOG_DEBUG("TEST read status: %i", cstatus);
404 mmal_queue_put_back(component->output[i]->priv->module->queue, buffer);
405 break;
406 }
407
408 if(!buffer->length)
409 {
410 buffer->pts = packet.pts == VC_CONTAINER_TIME_UNKNOWN ? MMAL_TIME_UNKNOWN : packet.pts;
411 buffer->dts = packet.dts == VC_CONTAINER_TIME_UNKNOWN ? MMAL_TIME_UNKNOWN : packet.dts;
412 buffer->flags = 0;
413 if(packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME)
414 buffer->flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME;
415 if(packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)
416 buffer->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_START;
417 }
418 if(packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)
419 buffer->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
420#ifdef VC_CONTAINER_PACKET_FLAG_CONFIG
421 if(packet.flags & VC_CONTAINER_PACKET_FLAG_CONFIG)
422 buffer->flags |= MMAL_BUFFER_HEADER_FLAG_CONFIG;
423#endif
424
425 buffer->length += packet.size;
426
427 if((component->output[i]->format->flags & MMAL_ES_FORMAT_FLAG_FRAMED) &&
428 buffer->length != buffer->alloc_size &&
429 !(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END))
430 {
431 mmal_queue_put_back(component->output[i]->priv->module->queue, buffer);
432 continue;
433 }
434
435 /* Send buffer back */
436 mmal_port_buffer_header_callback(component->output[i], buffer);
437 }
438
439 if(cstatus == VC_CONTAINER_ERROR_EOS)
440 {
441 /* Send an empty EOS buffer for each port */
442 for(i = 0; i < component->output_num; i++)
443 {
444 MMAL_PORT_T *port = component->output[i];
445 if(!port->is_enabled)
446 continue;
447 if(port->priv->module->eos)
448 continue;
449 /* Get a buffer from this port */
450 buffer = mmal_queue_get(port->priv->module->queue);
451 if(!buffer)
452 continue; /* Try again next time */
453 buffer->length = 0;
454 buffer->pts = buffer->dts = MMAL_TIME_UNKNOWN;
455 buffer->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
456 /* Send buffer back */
457 port->priv->module->eos = 1;
458 mmal_port_buffer_header_callback(port, buffer);
459 }
460 }
461 else if(cstatus != VC_CONTAINER_SUCCESS && !module->error)
462 {
463 status = mmal_event_error_send(component, container_map_to_mmal_status(cstatus));
464 if (status != MMAL_SUCCESS)
465 {
466 LOG_ERROR("unable to send an error event buffer (%i)", (int)status);
467 return;
468 }
469 module->error = 1;
470 }
471
472 return;
473}
474
475/*****************************************************************************/
476static void writer_do_processing(MMAL_COMPONENT_T *component)
477{
478 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
479 VC_CONTAINER_STATUS_T cstatus;
480 MMAL_PORT_MODULE_T *port_module;
481 MMAL_PORT_T *port;
482 MMAL_STATUS_T status;
483 MMAL_BOOL_T eos;
484 int64_t timestamp, timestamp_current;
485 MMAL_BUFFER_HEADER_T *buffer;
486 VC_CONTAINER_PACKET_T packet;
487 unsigned int i, index;
488
489 if(module->error)
490 return;
491
492 /* Select the next port to read from based on earliest timestamp. Buffers without
493 * timestamps will end-up being prioritised. */
494 for(i = 0, index = module->port_last_used, port = 0, timestamp = INT64_C(0);
495 i < component->input_num; i++, index++)
496 {
497 if(index == component->input_num)
498 index = 0;
499
500 if(!component->input[index]->is_enabled)
501 continue;
502
503 buffer = mmal_queue_get(component->input[index]->priv->module->queue);
504 if(!buffer)
505 continue;
506
507 timestamp_current = buffer->dts;
508 if (timestamp_current == MMAL_TIME_UNKNOWN)
509 timestamp_current = buffer->pts;
510 if(!port)
511 timestamp = timestamp_current;
512
513 if(timestamp_current <= timestamp)
514 {
515 port = component->input[index];
516 timestamp = timestamp_current;
517 module->port_last_used = index;
518 }
519 mmal_queue_put_back(component->input[index]->priv->module->queue, buffer);
520 }
521
522 /* If a port is currently writing a frame then we override the decision to avoid
523 * splitting frames */
524 if(module->port_writing_frame && module->port_writing_frame - 1 < component->input_num &&
525 component->input[module->port_writing_frame-1]->is_enabled)
526 port = component->input[module->port_writing_frame-1];
527
528 if(!port)
529 return; /* nothing to write */
530
531 port_module = port->priv->module;
532
533 /* Get a buffer from this port */
534 buffer = mmal_queue_get(port_module->queue);
535 if(!buffer)
536 return; /* nothing to write */
537
538 mmal_buffer_header_mem_lock(buffer);
539 memset(&packet, 0, sizeof(packet));
540 packet.track = port_module->track;
541 packet.size = buffer->length;
542 packet.data = buffer->data + buffer->offset;
543 packet.pts = buffer->pts == MMAL_TIME_UNKNOWN ? VC_CONTAINER_TIME_UNKNOWN : buffer->pts;
544 packet.dts = buffer->dts == MMAL_TIME_UNKNOWN ? VC_CONTAINER_TIME_UNKNOWN : buffer->dts;
545 if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME)
546 packet.flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
547 if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_START)
548 packet.flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
549 if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)
550 packet.flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
551 eos = buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
552
553 if ((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME) == VC_CONTAINER_PACKET_FLAG_FRAME)
554 packet.frame_size = packet.size;
555 else
556 packet.frame_size = 0;
557
558 module->port_writing_frame = port->index + 1;
559 if((buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) ||
560 !(port->format->flags & MMAL_ES_FORMAT_FLAG_FRAMED))
561 module->port_writing_frame = 0;
562
563 LOG_DEBUG("packet info: track %i, size %i/%i, pts %"PRId64", flags %x%s",
564 packet.track, packet.size, packet.frame_size, packet.pts,
565 packet.flags, (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "");
566
567 cstatus = vc_container_write(module->container, &packet);
568 mmal_buffer_header_mem_unlock(buffer);
569
570 /* Send buffer back */
571 buffer->length = 0;
572 mmal_port_buffer_header_callback(port, buffer);
573
574 /* Check for errors */
575 if(cstatus != VC_CONTAINER_SUCCESS)
576 {
577 LOG_ERROR("write failed (%i)", (int)cstatus);
578 status = mmal_event_error_send(component, container_map_to_mmal_status(cstatus));
579 if (status != MMAL_SUCCESS)
580 {
581 LOG_ERROR("unable to send an error event buffer (%i)", (int)status);
582 return;
583 }
584 module->error = 1;
585 return;
586 }
587
588 /* Generate EOS events */
589 if(eos)
590 {
591 MMAL_EVENT_END_OF_STREAM_T *event;
592 status = mmal_port_event_get(component->control, &buffer, MMAL_EVENT_EOS);
593 if (status != MMAL_SUCCESS)
594 {
595 LOG_ERROR("unable to get an event buffer");
596 return;
597 }
598
599 buffer->length = sizeof(*event);
600 event = (MMAL_EVENT_END_OF_STREAM_T *)buffer->data;
601 event->port_type = port->type;
602 event->port_index = port->index;
603 mmal_port_event_send(component->control, buffer);
604 }
605}
606
607/** Destroy a previously created component */
608static MMAL_STATUS_T container_component_destroy(MMAL_COMPONENT_T *component)
609{
610 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
611 unsigned int i;
612
613 if(module->container)
614 vc_container_close(module->container);
615
616 for(i = 0; i < component->input_num; i++)
617 {
618 if(component->input[i]->priv->module->queue)
619 mmal_queue_destroy(component->input[i]->priv->module->queue);
620 if(component->input[i]->priv->module->format)
621 vc_container_format_delete(component->input[i]->priv->module->format);
622 }
623 if(component->input_num)
624 mmal_ports_free(component->input, component->input_num);
625
626 for(i = 0; i < component->output_num; i++)
627 if(component->output[i]->priv->module->queue)
628 mmal_queue_destroy(component->output[i]->priv->module->queue);
629 if(component->output_num)
630 mmal_ports_free(component->output, component->output_num);
631
632 vcos_free(module);
633 return MMAL_SUCCESS;
634}
635
636/** Enable processing on a port */
637static MMAL_STATUS_T container_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
638{
639 MMAL_COMPONENT_T *component = port->component;
640 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
641 MMAL_PORT_MODULE_T *port_module = port->priv->module;
642 MMAL_PARAM_UNUSED(cb);
643
644 if(!module->container)
645 return MMAL_EINVAL;
646
647 if(module->writer)
648 {
649 VC_CONTAINER_STATUS_T cstatus;
650 port_module->track = module->container->tracks_num;
651 cstatus = vc_container_control(module->container, VC_CONTAINER_CONTROL_TRACK_ADD,
652 port_module->format);
653 if(cstatus != VC_CONTAINER_SUCCESS)
654 {
655 LOG_ERROR("error adding track %4.4s (%i)", (char *)&port->format->encoding, (int)cstatus);
656 return container_map_to_mmal_status(cstatus);
657 }
658 }
659
660 if(port_module->track >= module->container->tracks_num)
661 {
662 LOG_ERROR("error 1 adding track %4.4s (%i/%i)", (char *)&port->format->encoding, port_module->track, module->container->tracks_num);
663 return MMAL_EINVAL;
664 }
665 module->container->tracks[port_module->track]->is_enabled = 1;
666 return MMAL_SUCCESS;
667}
668
669/** Flush a port */
670static MMAL_STATUS_T container_port_flush(MMAL_PORT_T *port)
671{
672 MMAL_PORT_MODULE_T *port_module = port->priv->module;
673 MMAL_BUFFER_HEADER_T *buffer;
674
675 /* Flush buffers that our component is holding on to.
676 * If the reading thread is holding onto a buffer it will send it back ASAP as well
677 * so no need to care about that. */
678 buffer = mmal_queue_get(port_module->queue);
679 while(buffer)
680 {
681 buffer->length = 0;
682 mmal_port_buffer_header_callback(port, buffer);
683 buffer = mmal_queue_get(port_module->queue);
684 }
685
686 return MMAL_SUCCESS;
687}
688
689/** Disable processing on a port */
690static MMAL_STATUS_T container_port_disable(MMAL_PORT_T *port)
691{
692 MMAL_COMPONENT_T *component = port->component;
693 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
694 unsigned int track = port->priv->module->track;
695 MMAL_STATUS_T status;
696
697 if(!module->container || track >= module->container->tracks_num)
698 return MMAL_EINVAL;
699
700 /* Actions are prevented from running at that point so a flush
701 * will return all buffers. */
702 status = container_port_flush(port);
703 if(status != MMAL_SUCCESS)
704 return status;
705
706 module->container->tracks[track]->is_enabled = 0;
707 return MMAL_SUCCESS;
708}
709
710/** Send a buffer header to a port */
711static MMAL_STATUS_T container_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
712{
713 mmal_queue_put(port->priv->module->queue, buffer);
714 mmal_component_action_trigger(port->component);
715 return MMAL_SUCCESS;
716}
717
718/** Set format on a port */
719static MMAL_STATUS_T container_port_set_format(MMAL_PORT_T *port)
720{
721 MMAL_COMPONENT_T *component = port->component;
722 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
723 MMAL_STATUS_T status;
724
725 if(!module->writer)
726 return MMAL_EINVAL;
727
728 /* Set format of the track */
729 status = mmal_to_container_format(port->priv->module->format, port->format);
730 if (status != MMAL_SUCCESS)
731 return status;
732
733 port->buffer_num_min = READER_MIN_BUFFER_NUM;
734 port->buffer_num_recommended = READER_RECOMMENDED_BUFFER_NUM;
735 port->buffer_size_min = READER_MIN_BUFFER_SIZE;
736 port->buffer_size_recommended = READER_RECOMMENDED_BUFFER_SIZE;
737 return MMAL_SUCCESS;
738}
739
740static MMAL_STATUS_T reader_container_open(MMAL_COMPONENT_T *component, const char *uri)
741{
742 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
743 VC_CONTAINER_STATUS_T cstatus;
744 VC_CONTAINER_T *container;
745 unsigned int i, port, track;
746
747 /* Open container */
748 module->container = container =
749 vc_container_open_reader(uri, &cstatus, 0, 0);
750 if(!container)
751 {
752 LOG_ERROR("error opening file %s (%i)", uri, cstatus);
753 return container_map_to_mmal_status(cstatus);
754 }
755
756 /* Disable all tracks */
757 for(track = 0; track < container->tracks_num; track++)
758 container->tracks[track]->is_enabled = 0;
759
760 /* Fill-in the ports */
761 for(i = 0, port = 0; i < component->output_num; i++)
762 {
763 VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_VIDEO;
764 if(i == 1) type = VC_CONTAINER_ES_TYPE_AUDIO;
765 if(i == 2) type = VC_CONTAINER_ES_TYPE_SUBPICTURE;
766
767 /* Look for the first track with the specified type */
768 for(track = 0; track < container->tracks_num; track++)
769 if(container->tracks[track]->format->es_type == type)
770 break;
771 if(track == container->tracks_num)
772 continue;
773
774 if(container_to_mmal_encoding(container->tracks[track]->format->codec) == MMAL_ENCODING_UNKNOWN)
775 continue;
776
777 /* Set format of the port */
778 if(container_to_mmal_format(component->output[port]->format,
779 container->tracks[track]->format) != MMAL_SUCCESS)
780 continue;
781
782 component->output[port]->buffer_num_min = READER_MIN_BUFFER_NUM;
783 component->output[port]->buffer_num_recommended = READER_RECOMMENDED_BUFFER_NUM;
784 component->output[port]->buffer_size_min = READER_MIN_BUFFER_SIZE;
785 component->output[port]->buffer_size_recommended = READER_RECOMMENDED_BUFFER_SIZE;
786 component->output[port]->priv->module->track = track;
787
788 /* We're done with this port */
789 port++;
790 }
791 module->ports = port;
792
793 /* Reset the left over ports */
794 for(i = port; i < component->output_num; i++)
795 {
796 component->output[i]->format->type = MMAL_ES_TYPE_UNKNOWN;
797 component->output[i]->format->encoding = MMAL_ENCODING_UNKNOWN;
798 }
799
800 return MMAL_SUCCESS;
801}
802
803static MMAL_STATUS_T reader_container_seek(MMAL_COMPONENT_T *component, const MMAL_PARAMETER_SEEK_T *seek)
804{
805 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
806 VC_CONTAINER_SEEK_FLAGS_T flags = 0;
807 int64_t offset = seek->offset;
808 VC_CONTAINER_STATUS_T cstatus;
809 unsigned int i;
810
811 if(seek->flags & MMAL_PARAM_SEEK_FLAG_PRECISE)
812 flags |= VC_CONTAINER_SEEK_FLAG_PRECISE;
813 if(seek->flags & MMAL_PARAM_SEEK_FLAG_FORWARD)
814 flags |= VC_CONTAINER_SEEK_FLAG_FORWARD;
815
816 mmal_component_action_lock(component);
817 for(i = 0; i < component->output_num; i++)
818 {
819 component->output[i]->priv->module->eos = MMAL_FALSE;
820 component->output[i]->priv->module->flush = MMAL_TRUE;
821 }
822 cstatus = vc_container_seek( module->container, &offset, VC_CONTAINER_SEEK_MODE_TIME, flags);
823 mmal_component_action_unlock(component);
824 return container_map_to_mmal_status(cstatus);
825}
826
827static MMAL_STATUS_T reader_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
828{
829 MMAL_COMPONENT_T *component = port->component;
830 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
831
832 switch(param->id)
833 {
834 case MMAL_PARAMETER_URI:
835 if(module->container)
836 return MMAL_EINVAL;
837
838 memset(module->uri, 0, sizeof(module->uri));
839 strncpy(module->uri, ((const MMAL_PARAMETER_STRING_T *)param)->str, sizeof(module->uri)-1 );
840 return reader_container_open(component, module->uri);
841
842 case MMAL_PARAMETER_SEEK:
843 if(!module->container || param->size < sizeof(MMAL_PARAMETER_SEEK_T))
844 return MMAL_EINVAL;
845
846 return reader_container_seek(component, (const MMAL_PARAMETER_SEEK_T *)param);
847
848 default:
849 return MMAL_ENOSYS;
850 }
851
852 return MMAL_SUCCESS;
853}
854
855/** Create an instance of a component */
856static MMAL_STATUS_T mmal_component_create_reader(const char *name, MMAL_COMPONENT_T *component)
857{
858 MMAL_COMPONENT_MODULE_T *module;
859 unsigned int outputs_num, i;
860 MMAL_STATUS_T status = MMAL_ENOMEM;
861 MMAL_PARAM_UNUSED(name);
862
863 /* Allocate the context for our module */
864 component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
865 if (!module)
866 return MMAL_ENOMEM;
867 memset(module, 0, sizeof(*module));
868
869 component->priv->pf_destroy = container_component_destroy;
870
871 /* Create 3 tracks for now (audio/video/subpicture).
872 * FIXME: ideally we should create 1 track per elementary stream. */
873 outputs_num = 3;
874
875 /* Now the component on reader has been created, we can allocate
876 * the ports for this component */
877 component->output = mmal_ports_alloc(component, outputs_num, MMAL_PORT_TYPE_OUTPUT,
878 sizeof(MMAL_PORT_MODULE_T));
879 if(!component->output)
880 goto error;
881 component->output_num = outputs_num;
882
883 for(i = 0; i < outputs_num; i++)
884 {
885 component->output[i]->priv->pf_enable = container_port_enable;
886 component->output[i]->priv->pf_disable = container_port_disable;
887 component->output[i]->priv->pf_flush = container_port_flush;
888 component->output[i]->priv->pf_send = container_port_send;
889 component->output[i]->priv->module->queue = mmal_queue_create();
890 if(!component->output[i]->priv->module->queue)
891 goto error;
892 }
893 component->control->priv->pf_parameter_set = reader_parameter_set;
894
895 status = mmal_component_action_register(component, reader_do_processing);
896 if (status != MMAL_SUCCESS)
897 goto error;
898
899 return MMAL_SUCCESS;
900
901 error:
902 container_component_destroy(component);
903 return status;
904}
905
906static MMAL_STATUS_T writer_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
907{
908 MMAL_COMPONENT_T *component = port->component;
909 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
910 VC_CONTAINER_STATUS_T cstatus;
911
912 switch(param->id)
913 {
914 case MMAL_PARAMETER_URI:
915 if(module->container)
916 return MMAL_EINVAL;
917
918 memset(module->uri, 0, sizeof(module->uri));
919 strncpy(module->uri, ((const MMAL_PARAMETER_URI_T *)param)->uri, sizeof(module->uri)-1 );
920
921 /* Open container */
922 module->container = vc_container_open_writer(module->uri, &cstatus, 0, 0);
923 if(!module->container)
924 {
925 LOG_ERROR("error opening file %s (%i)", module->uri, cstatus);
926 return container_map_to_mmal_status(cstatus);
927 }
928 return MMAL_SUCCESS;
929
930 default:
931 return MMAL_ENOSYS;
932 }
933
934 return MMAL_SUCCESS;
935}
936
937/** Create an instance of a component */
938static MMAL_STATUS_T mmal_component_create_writer(const char *name, MMAL_COMPONENT_T *component)
939{
940 MMAL_COMPONENT_MODULE_T *module;
941 MMAL_STATUS_T status = MMAL_ENOMEM;
942 unsigned int i;
943 MMAL_PARAM_UNUSED(name);
944
945 /* Allocate the context for our module */
946 component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
947 if (!module)
948 return MMAL_ENOMEM;
949 memset(module, 0, sizeof(*module));
950 module->writer = 1;
951
952 component->priv->pf_destroy = container_component_destroy;
953
954 /* Now the component on reader has been created, we can allocate
955 * the ports for this component */
956 component->input = mmal_ports_alloc(component, WRITER_PORTS_NUM, MMAL_PORT_TYPE_INPUT,
957 sizeof(MMAL_PORT_MODULE_T));
958 if(!component->input)
959 goto error;
960 component->input_num = WRITER_PORTS_NUM;
961
962 for(i = 0; i < component->input_num; i++)
963 {
964 component->input[i]->priv->pf_enable = container_port_enable;
965 component->input[i]->priv->pf_disable = container_port_disable;
966 component->input[i]->priv->pf_flush = container_port_flush;
967 component->input[i]->priv->pf_send = container_port_send;
968 component->input[i]->priv->pf_set_format = container_port_set_format;
969
970 component->input[i]->priv->module->queue = mmal_queue_create();
971 if(!component->input[i]->priv->module->queue)
972 goto error;
973 component->input[i]->priv->module->format = vc_container_format_create(0);
974 if(!component->input[i]->priv->module->format)
975 goto error;
976 }
977 component->control->priv->pf_parameter_set = writer_parameter_set;
978
979 status = mmal_component_action_register(component, writer_do_processing);
980 if (status != MMAL_SUCCESS)
981 goto error;
982
983 return MMAL_SUCCESS;
984
985 error:
986 container_component_destroy(component);
987 return status;
988}
989
990MMAL_CONSTRUCTOR(mmal_register_component_container_reader);
991void mmal_register_component_container_reader(void)
992{
993 mmal_component_supplier_register("container_reader", mmal_component_create_reader);
994}
995
996MMAL_CONSTRUCTOR(mmal_register_component_container_writer);
997void mmal_register_component_container_writer(void)
998{
999 mmal_component_supplier_register("container_writer", mmal_component_create_writer);
1000}
1001