2 Copyright (c) 2012, Broadcom Europe Ltd
5 Redistribution and use in source and binary forms, with or without
6 modification, 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.
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON 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
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "containers/core/containers_private.h"
32 #include "containers/core/containers_io_helpers.h"
33 #include "containers/core/containers_utils.h"
34 #include "containers/core/containers_logging.h"
36 #include "simple_common.h"
38 /******************************************************************************
40 ******************************************************************************/
41 #define MAX_LINE_SIZE 512
42 #define LINE_PADDING 3 /* 2 for newline + 1 for null */
45 #define MAX_HEADER_LINES 512
47 typedef enum SIMPLE_VARIANT_T
54 /******************************************************************************
56 ******************************************************************************/
57 typedef struct SIMPLE_PACKET_STATE_T
59 unsigned int track_num;
62 uint64_t metadata_offset; /* Offset in metadata stream */
63 uint32_t data_size; /* Size of current data packet */
64 uint32_t data_left; /* Data left to read in current packet */
68 } SIMPLE_PACKET_STATE_T;
70 typedef struct VC_CONTAINER_TRACK_MODULE_T
72 SIMPLE_PACKET_STATE_T *state;
73 SIMPLE_PACKET_STATE_T local_state;
75 VC_CONTAINER_IO_T *io;
76 uint64_t data_offset; /* Current offset in data stream */
77 char uri[MAX_LINE_SIZE+1];
79 SIMPLE_VARIANT_T variant;
81 } VC_CONTAINER_TRACK_MODULE_T;
83 typedef struct VC_CONTAINER_MODULE_T
85 VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS];
87 char line[MAX_LINE_SIZE + LINE_PADDING];
89 int64_t metadata_offset;
91 /* Shared packet state. This is used when the tracks are in sync,
92 * and for the track at the earliest position in the file when they are
94 SIMPLE_PACKET_STATE_T state;
96 } VC_CONTAINER_MODULE_T;
98 /******************************************************************************
100 ******************************************************************************/
101 VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * );
103 /******************************************************************************
105 ******************************************************************************/
106 static VC_CONTAINER_STATUS_T simple_read_line( VC_CONTAINER_T *ctx )
108 VC_CONTAINER_MODULE_T *module = ctx->priv->module;
109 unsigned int i, bytes = PEEK_BYTES(ctx, module->line, sizeof(module->line)-1);
112 return VC_CONTAINER_ERROR_EOS;
114 /* Find new-line marker */
115 for (i = 0; i < bytes; i++)
116 if (module->line[i] == '\n')
119 /* Bail out if line is bigger than the maximum allowed */
120 if (i == sizeof(module->line)-1)
122 LOG_ERROR(ctx, "line too big");
123 return VC_CONTAINER_ERROR_CORRUPTED;
128 module->line[i++] = 0;
129 if (i < bytes && module->line[i] == '\r')
132 module->line[i] = 0; /* Make sure the line is null terminated */
135 return VC_CONTAINER_SUCCESS;
138 static VC_CONTAINER_STATUS_T simple_read_header( VC_CONTAINER_T *ctx )
140 VC_CONTAINER_MODULE_T *module = ctx->priv->module;
141 VC_CONTAINER_TRACK_T *track = NULL;
142 VC_CONTAINER_FOURCC_T fourcc;
143 int matches, width, height, channels, samplerate, bps, blockalign, value;
144 unsigned int lines = 1;
146 /* Skip the signature */
147 if (simple_read_line(ctx) != VC_CONTAINER_SUCCESS)
148 return VC_CONTAINER_ERROR_CORRUPTED;
150 while (lines++ < MAX_HEADER_LINES &&
151 simple_read_line(ctx) == VC_CONTAINER_SUCCESS)
153 /* Our exit condition is the end signature */
154 if (!memcmp(module->line, SIGNATURE_END_STRING, sizeof(SIGNATURE_STRING)-1))
156 if (track) ctx->tracks[ctx->tracks_num++] = track;
157 return VC_CONTAINER_SUCCESS;
160 /* Start of track description */
161 if (!memcmp(module->line, "TRACK ", sizeof("TRACK ")-1))
163 /* Add track we were constructing */
164 if (track) ctx->tracks[ctx->tracks_num++] = track;
167 if (ctx->tracks_num >= MAX_TRACKS)
169 LOG_ERROR(ctx, "too many tracks, ignoring: %s", module->line);
172 track = vc_container_allocate_track(ctx, sizeof(*track->priv->module));
174 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
176 track->is_enabled = true;
177 track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
179 if ((matches = sscanf(module->line,
180 "TRACK video, %4c, %i, %i",
181 (char *)&fourcc, &width, &height)) > 0)
183 track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
184 track->format->codec = fourcc;
185 if (matches > 1) track->format->type->video.width = width;
186 if (matches > 2) track->format->type->video.height = height;
188 else if ((matches = sscanf(module->line,
189 "TRACK audio, %4c, %i, %i, %i, %i",
190 (char *)&fourcc, &channels, &samplerate, &bps,
193 track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
194 track->format->codec = fourcc;
195 if (matches > 1) track->format->type->audio.channels = channels;
196 if (matches > 2) track->format->type->audio.sample_rate = samplerate;
197 if (matches > 3) track->format->type->audio.bits_per_sample = bps;
198 if (matches > 4) track->format->type->audio.block_align = blockalign;
200 if ((matches = sscanf(module->line,
201 "TRACK subpicture, %4c, %i",
202 (char *)&fourcc, &value)) > 0)
204 track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE;
205 track->format->codec = fourcc;
206 if (matches > 1) track->format->type->subpicture.encoding = value;
211 continue; /* Nothing interesting */
213 /* VARIANT of the syntax */
214 if (sscanf(module->line, CONFIG_VARIANT" %i", &value) == 1)
216 track->priv->module->variant = value;
217 LOG_FORMAT(ctx, CONFIG_VARIANT": %i", value);
219 /* URI for elementary stream */
220 else if (sscanf(module->line, CONFIG_URI" %s", track->priv->module->uri) == 1)
221 LOG_FORMAT(ctx, CONFIG_URI": %s", track->priv->module->uri);
222 /* COCDEC_VARIANT of elementary stream */
223 else if (sscanf(module->line, CONFIG_CODEC_VARIANT" %4c", (char *)&fourcc) == 1)
225 track->format->codec_variant = fourcc;
226 LOG_FORMAT(ctx, CONFIG_CODEC_VARIANT": %4.4s", (char *)&fourcc);
228 /* BITRATE of elementary stream */
229 else if (sscanf(module->line, CONFIG_BITRATE" %i", &value) == 1)
231 track->format->bitrate = value;
232 LOG_FORMAT(ctx, CONFIG_BITRATE": %i", value);
234 /* UNFRAMED elementary stream */
235 else if (!memcmp(module->line, CONFIG_UNFRAMED, sizeof(CONFIG_UNFRAMED)-1))
237 track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
238 LOG_FORMAT(ctx, CONFIG_UNFRAMED);
240 /* VIDEO_CROP information */
241 else if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO &&
242 sscanf(module->line, CONFIG_VIDEO_CROP" %i, %i", &width, &height) == 2)
244 track->format->type->video.visible_width = width;
245 track->format->type->video.visible_height = height;
246 LOG_FORMAT(ctx, CONFIG_VIDEO_CROP": %i, %i", width, height);
248 /* VIDEO_ASPECT information */
249 else if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO &&
250 sscanf(module->line, CONFIG_VIDEO_ASPECT" %i, %i", &width, &height) == 2)
252 track->format->type->video.par_num = width;
253 track->format->type->video.par_den = height;
254 LOG_FORMAT(ctx, CONFIG_VIDEO_ASPECT": %i, %i", width, height);
258 if (track) vc_container_free_track(ctx, track);
259 return VC_CONTAINER_ERROR_CORRUPTED;
262 static uint32_t simple_convert_packet_flags(VC_CONTAINER_T *ctx,
263 unsigned int track_num, uint32_t flags)
265 typedef struct { uint32_t from; uint32_t to; } convert_from_t;
266 const convert_from_t convert_from_mmal[] =
267 { {1<<1, VC_CONTAINER_PACKET_FLAG_FRAME_START},
268 {1<<2, VC_CONTAINER_PACKET_FLAG_FRAME_END},
269 {1<<3, VC_CONTAINER_PACKET_FLAG_KEYFRAME},
270 {1<<4, VC_CONTAINER_PACKET_FLAG_DISCONTINUITY},
271 {1<<5, VC_CONTAINER_PACKET_FLAG_CONFIG},
272 {1<<6, VC_CONTAINER_PACKET_FLAG_ENCRYPTED},
274 const convert_from_t convert_from_omx[] =
275 { {0x10, VC_CONTAINER_PACKET_FLAG_FRAME_END},
276 {0x20, VC_CONTAINER_PACKET_FLAG_KEYFRAME},
277 {0x80, VC_CONTAINER_PACKET_FLAG_CONFIG},
279 const convert_from_t *convert_from = NULL;
282 switch (ctx->tracks[track_num]->priv->module->variant)
284 case VARIANT_MMAL: convert_from = convert_from_mmal; break;
285 case VARIANT_OMX: convert_from = convert_from_omx; break;
291 uint32_t new_flags = 0;
292 for (i = 0; convert_from[i].from; i++)
293 if (convert_from[i].from & flags)
294 new_flags |= convert_from[i].to;
301 static int64_t simple_convert_packet_pts(VC_CONTAINER_T *ctx,
302 unsigned int track_num, int64_t pts, uint32_t flags)
304 if (ctx->tracks[track_num]->priv->module->variant == VARIANT_OMX &&
306 return VC_CONTAINER_TIME_UNKNOWN;
311 /*****************************************************************************
312 Functions exported as part of the Container Module API
313 *****************************************************************************/
314 static VC_CONTAINER_STATUS_T simple_reader_read( VC_CONTAINER_T *ctx,
315 VC_CONTAINER_PACKET_T *packet, uint32_t flags )
317 VC_CONTAINER_MODULE_T *module = ctx->priv->module;
318 VC_CONTAINER_TRACK_MODULE_T *track_module;
319 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
320 SIMPLE_PACKET_STATE_T *state;
322 /* If a specific track has been selected, use the track packet state */
323 if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
324 state = ctx->tracks[packet->track]->priv->module->state;
326 state = &module->state;
328 /* Switch to the next packet when the current one is empty */
329 if (!state->data_left)
331 unsigned int track_num, size;
335 SEEK(ctx, state->metadata_offset);
336 status = simple_read_line(ctx);
337 if (status != VC_CONTAINER_SUCCESS)
340 if (sscanf(module->line, "%u %u %"PRIi64" %i",
341 &track_num, &size, &pts, &flags) != 4 &&
342 (track_num = 0, sscanf(module->line, "%u %"PRIi64" %i",
343 &size, &pts, &flags)) != 3)
345 LOG_ERROR(ctx, "invalid metadata: %s", module->line);
346 return VC_CONTAINER_ERROR_CORRUPTED;
348 state->metadata_offset = STREAM_POSITION(ctx);
350 if (track_num >= ctx->tracks_num)
352 LOG_DEBUG(ctx, "skipping %i bytes for track %d/%d",
353 size, track_num, ctx->tracks_num);
354 return VC_CONTAINER_ERROR_CONTINUE;
357 /* If we are reading from the global state (i.e. normal read or forced
358 read from the track on the global state), and the track we found is
359 not on the global state, reconnect the two */
360 if (state == &module->state &&
361 ctx->tracks[track_num]->priv->module->state != &module->state)
363 LOG_DEBUG(ctx, "reconnect track %u to the global state", track_num);
364 ctx->tracks[track_num]->priv->module->state = &module->state;
365 module->state = ctx->tracks[track_num]->priv->module->local_state;
366 return VC_CONTAINER_ERROR_CONTINUE;
369 state->data_size = state->data_left = size;
370 state->track_num = track_num;
371 state->flags = simple_convert_packet_flags(ctx, track_num, flags);
372 state->pts = simple_convert_packet_pts(ctx, track_num, pts, flags);
374 /* Discard empty packets */
375 if (!state->data_size && !state->flags)
376 return VC_CONTAINER_ERROR_CONTINUE;
379 /* If there is data from another track skip past it */
380 if ((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) &&
381 state->track_num != packet->track)
383 LOG_DEBUG(ctx, "skipping track %d/%d as we are ignoring it",
384 state->track_num, ctx->tracks_num);
386 track_module = ctx->tracks[packet->track]->priv->module;
388 /* Handle disconnection from global state */
389 if (state == &module->state &&
390 ctx->tracks[state->track_num]->priv->module->state == &module->state)
392 /* Make a copy of the global state */
393 LOG_DEBUG(ctx, "using local state on track %d", packet->track);
394 track_module->local_state = module->state;
395 track_module->state = &track_module->local_state;
398 track_module->state->data_left = 0;
399 return VC_CONTAINER_ERROR_CONTINUE;
403 * From this point we know we have the packet which was requested
406 /* !!!! If we aren't in the right position in the file go there now. */
408 track_module = ctx->tracks[state->track_num]->priv->module;
409 packet->track = state->track_num;
410 packet->size = state->data_left;
411 packet->frame_size = (state->flags & VC_CONTAINER_PACKET_FLAG_FRAME) ?
412 state->data_size : 0;
413 packet->flags = state->flags;
414 packet->pts = state->pts;
415 packet->dts = VC_CONTAINER_TIME_UNKNOWN;
416 if (state->data_left != state->data_size)
417 packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START;
419 if (flags & VC_CONTAINER_READ_FLAG_SKIP)
421 track_module->data_offset += state->data_left;
422 state->data_left = 0;
423 return VC_CONTAINER_SUCCESS;
426 if (flags & VC_CONTAINER_READ_FLAG_INFO)
428 return VC_CONTAINER_SUCCESS;
431 /* Now try to read data into buffer */
432 vc_container_io_seek(track_module->io, track_module->data_offset);
434 packet->size = vc_container_io_read(track_module->io, packet->data,
435 MIN(packet->buffer_size, state->data_left));
436 state->data_left -= packet->size;
437 track_module->data_offset += packet->size;
439 if (state->data_left)
440 packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
442 return track_module->io->status;
445 /*****************************************************************************/
446 static VC_CONTAINER_STATUS_T simple_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset,
447 VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
449 VC_CONTAINER_PARAM_UNUSED(ctx);
450 VC_CONTAINER_PARAM_UNUSED(offset);
451 VC_CONTAINER_PARAM_UNUSED(mode);
452 VC_CONTAINER_PARAM_UNUSED(flags);
453 return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
456 /*****************************************************************************/
457 static VC_CONTAINER_STATUS_T simple_reader_close( VC_CONTAINER_T *ctx )
459 VC_CONTAINER_MODULE_T *module = ctx->priv->module;
461 for (; ctx->tracks_num > 0; ctx->tracks_num--)
463 VC_CONTAINER_TRACK_T *track = ctx->tracks[ctx->tracks_num-1];
464 if (track->priv->module->io)
465 vc_container_io_close(track->priv->module->io);
466 vc_container_free_track(ctx, track);
470 return VC_CONTAINER_SUCCESS;
473 /*****************************************************************************/
474 VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T *ctx )
476 VC_CONTAINER_MODULE_T *module = 0;
477 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
478 uint8_t h[sizeof(SIGNATURE_STRING)];
481 /* Check for the signature */
482 if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h) ||
483 memcmp(h, SIGNATURE_STRING, sizeof(SIGNATURE_STRING)-1))
484 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
486 LOG_DEBUG(ctx, "using simple reader");
488 /* Allocate our context */
489 module = malloc(sizeof(*module));
490 if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
491 memset(module, 0, sizeof(*module));
492 ctx->priv->module = module;
493 ctx->tracks = module->tracks;
495 status = simple_read_header(ctx);
496 if (status != VC_CONTAINER_SUCCESS)
499 /* Open all the elementary streams */
500 for (i = 0; i < ctx->tracks_num; i++)
502 VC_CONTAINER_TRACK_T *track = ctx->tracks[i];
505 track->priv->module->io = vc_container_io_open(track->priv->module->uri,
506 VC_CONTAINER_IO_MODE_READ, &status);
508 /* URI might be relative to the path of the metadata file so
509 * try again with that new path */
510 if (!track->priv->module->io &&
511 (uri = malloc(strlen(ctx->priv->io->uri) +
512 strlen(track->priv->module->uri) + 1)) != NULL)
516 strcpy(uri, ctx->priv->io->uri);
518 /* Find the last directory separator */
519 for (end = uri + strlen(ctx->priv->io->uri) + 1; end != uri; end--)
520 if (*(end-1) == '/' || *(end-1) == '\\')
522 strcpy(end, track->priv->module->uri);
524 track->priv->module->io = vc_container_io_open(uri,
525 VC_CONTAINER_IO_MODE_READ, &status);
526 if (!track->priv->module->io)
527 LOG_ERROR(ctx, "could not open elementary stream: %s", uri);
530 if (!track->priv->module->io)
532 LOG_ERROR(ctx, "could not open elementary stream: %s",
533 track->priv->module->uri);
539 * We now have all the information we really need to start playing the stream
542 module->metadata_offset = STREAM_POSITION(ctx);
544 /* Initialise state for all tracks */
545 module->state.metadata_offset = module->metadata_offset;
546 for (i = 0; i < ctx->tracks_num; i++)
548 VC_CONTAINER_TRACK_T *track = ctx->tracks[i];
549 track->priv->module->state = &module->state;
552 /* Look for the codec configuration data for each track so
553 * we can store it in the track format */
554 for (i = 0; i < ctx->tracks_num; i++)
556 VC_CONTAINER_TRACK_T *track = ctx->tracks[i];
557 VC_CONTAINER_PACKET_T packet;
559 status = VC_CONTAINER_ERROR_CONTINUE;
561 while (status == VC_CONTAINER_ERROR_CONTINUE)
562 status = simple_reader_read(ctx, &packet,
563 VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK);
564 if (status != VC_CONTAINER_SUCCESS)
567 status = vc_container_track_allocate_extradata(ctx, track, packet.size);
568 if (status != VC_CONTAINER_SUCCESS)
571 packet.data = track->format->extradata;
572 packet.buffer_size = packet.size;
574 status = simple_reader_read(ctx, &packet,
575 VC_CONTAINER_READ_FLAG_FORCE_TRACK);
576 if (status != VC_CONTAINER_SUCCESS)
579 track->format->extradata_size = packet.size;
582 ctx->priv->pf_close = simple_reader_close;
583 ctx->priv->pf_read = simple_reader_read;
584 ctx->priv->pf_seek = simple_reader_seek;
585 return VC_CONTAINER_SUCCESS;
588 LOG_ERROR(ctx, "simple: error opening stream (%i)", status);
589 simple_reader_close(ctx);
593 /********************************************************************************
595 ********************************************************************************/
597 #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
598 # pragma weak reader_open simple_reader_open