| 1 | /* |
| 2 | Copyright (c) 2012, Broadcom Europe Ltd |
| 3 | All rights reserved. |
| 4 | |
| 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. |
| 15 | |
| 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. |
| 26 | */ |
| 27 | #include <stdio.h> |
| 28 | #include <stdlib.h> |
| 29 | #include <string.h> |
| 30 | |
| 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" |
| 35 | |
| 36 | #include "simple_common.h" |
| 37 | |
| 38 | /****************************************************************************** |
| 39 | Defines. |
| 40 | ******************************************************************************/ |
| 41 | #define MAX_LINE_SIZE 512 |
| 42 | #define LINE_PADDING 3 /* 2 for newline + 1 for null */ |
| 43 | |
| 44 | #define MAX_TRACKS 4 |
| 45 | #define 512 |
| 46 | |
| 47 | typedef enum SIMPLE_VARIANT_T |
| 48 | { |
| 49 | VARIANT_DEFAULT = 0, |
| 50 | VARIANT_MMAL, |
| 51 | VARIANT_OMX |
| 52 | } SIMPLE_VARIANT_T; |
| 53 | |
| 54 | /****************************************************************************** |
| 55 | Type definitions |
| 56 | ******************************************************************************/ |
| 57 | typedef struct SIMPLE_PACKET_STATE_T |
| 58 | { |
| 59 | unsigned int track_num; |
| 60 | unsigned int flags; |
| 61 | |
| 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 */ |
| 65 | |
| 66 | int64_t pts; |
| 67 | |
| 68 | } SIMPLE_PACKET_STATE_T; |
| 69 | |
| 70 | typedef struct VC_CONTAINER_TRACK_MODULE_T |
| 71 | { |
| 72 | SIMPLE_PACKET_STATE_T *state; |
| 73 | SIMPLE_PACKET_STATE_T local_state; |
| 74 | |
| 75 | VC_CONTAINER_IO_T *io; |
| 76 | uint64_t data_offset; /* Current offset in data stream */ |
| 77 | char uri[MAX_LINE_SIZE+1]; |
| 78 | |
| 79 | SIMPLE_VARIANT_T variant; |
| 80 | |
| 81 | } VC_CONTAINER_TRACK_MODULE_T; |
| 82 | |
| 83 | typedef struct VC_CONTAINER_MODULE_T |
| 84 | { |
| 85 | VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS]; |
| 86 | |
| 87 | char line[MAX_LINE_SIZE + LINE_PADDING]; |
| 88 | |
| 89 | int64_t metadata_offset; |
| 90 | |
| 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 |
| 93 | * not in sync */ |
| 94 | SIMPLE_PACKET_STATE_T state; |
| 95 | |
| 96 | } VC_CONTAINER_MODULE_T; |
| 97 | |
| 98 | /****************************************************************************** |
| 99 | Function prototypes |
| 100 | ******************************************************************************/ |
| 101 | VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * ); |
| 102 | |
| 103 | /****************************************************************************** |
| 104 | Local Functions |
| 105 | ******************************************************************************/ |
| 106 | static VC_CONTAINER_STATUS_T simple_read_line( VC_CONTAINER_T *ctx ) |
| 107 | { |
| 108 | VC_CONTAINER_MODULE_T *module = ctx->priv->module; |
| 109 | unsigned int i, bytes = PEEK_BYTES(ctx, module->line, sizeof(module->line)-1); |
| 110 | |
| 111 | if (!bytes) |
| 112 | return VC_CONTAINER_ERROR_EOS; |
| 113 | |
| 114 | /* Find new-line marker */ |
| 115 | for (i = 0; i < bytes; i++) |
| 116 | if (module->line[i] == '\n') |
| 117 | break; |
| 118 | |
| 119 | /* Bail out if line is bigger than the maximum allowed */ |
| 120 | if (i == sizeof(module->line)-1) |
| 121 | { |
| 122 | LOG_ERROR(ctx, "line too big" ); |
| 123 | return VC_CONTAINER_ERROR_CORRUPTED; |
| 124 | } |
| 125 | |
| 126 | if (i < bytes) |
| 127 | { |
| 128 | module->line[i++] = 0; |
| 129 | if (i < bytes && module->line[i] == '\r') |
| 130 | i++; |
| 131 | } |
| 132 | module->line[i] = 0; /* Make sure the line is null terminated */ |
| 133 | |
| 134 | SKIP_BYTES(ctx, i); |
| 135 | return VC_CONTAINER_SUCCESS; |
| 136 | } |
| 137 | |
| 138 | static VC_CONTAINER_STATUS_T ( VC_CONTAINER_T *ctx ) |
| 139 | { |
| 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; |
| 145 | |
| 146 | /* Skip the signature */ |
| 147 | if (simple_read_line(ctx) != VC_CONTAINER_SUCCESS) |
| 148 | return VC_CONTAINER_ERROR_CORRUPTED; |
| 149 | |
| 150 | while (lines++ < MAX_HEADER_LINES && |
| 151 | simple_read_line(ctx) == VC_CONTAINER_SUCCESS) |
| 152 | { |
| 153 | /* Our exit condition is the end signature */ |
| 154 | if (!memcmp(module->line, SIGNATURE_END_STRING, sizeof(SIGNATURE_STRING)-1)) |
| 155 | { |
| 156 | if (track) ctx->tracks[ctx->tracks_num++] = track; |
| 157 | return VC_CONTAINER_SUCCESS; |
| 158 | } |
| 159 | |
| 160 | /* Start of track description */ |
| 161 | if (!memcmp(module->line, "TRACK " , sizeof("TRACK " )-1)) |
| 162 | { |
| 163 | /* Add track we were constructing */ |
| 164 | if (track) ctx->tracks[ctx->tracks_num++] = track; |
| 165 | track = NULL; |
| 166 | |
| 167 | if (ctx->tracks_num >= MAX_TRACKS) |
| 168 | { |
| 169 | LOG_ERROR(ctx, "too many tracks, ignoring: %s" , module->line); |
| 170 | continue; |
| 171 | } |
| 172 | track = vc_container_allocate_track(ctx, sizeof(*track->priv->module)); |
| 173 | if (!track) |
| 174 | return VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
| 175 | |
| 176 | track->is_enabled = true; |
| 177 | track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; |
| 178 | |
| 179 | if ((matches = sscanf(module->line, |
| 180 | "TRACK video, %4c, %i, %i" , |
| 181 | (char *)&fourcc, &width, &height)) > 0) |
| 182 | { |
| 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; |
| 187 | } |
| 188 | else if ((matches = sscanf(module->line, |
| 189 | "TRACK audio, %4c, %i, %i, %i, %i" , |
| 190 | (char *)&fourcc, &channels, &samplerate, &bps, |
| 191 | &blockalign)) > 0) |
| 192 | { |
| 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; |
| 199 | } |
| 200 | if ((matches = sscanf(module->line, |
| 201 | "TRACK subpicture, %4c, %i" , |
| 202 | (char *)&fourcc, &value)) > 0) |
| 203 | { |
| 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; |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | if (!track) |
| 211 | continue; /* Nothing interesting */ |
| 212 | |
| 213 | /* VARIANT of the syntax */ |
| 214 | if (sscanf(module->line, CONFIG_VARIANT" %i" , &value) == 1) |
| 215 | { |
| 216 | track->priv->module->variant = value; |
| 217 | LOG_FORMAT(ctx, CONFIG_VARIANT": %i" , value); |
| 218 | } |
| 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) |
| 224 | { |
| 225 | track->format->codec_variant = fourcc; |
| 226 | LOG_FORMAT(ctx, CONFIG_CODEC_VARIANT": %4.4s" , (char *)&fourcc); |
| 227 | } |
| 228 | /* BITRATE of elementary stream */ |
| 229 | else if (sscanf(module->line, CONFIG_BITRATE" %i" , &value) == 1) |
| 230 | { |
| 231 | track->format->bitrate = value; |
| 232 | LOG_FORMAT(ctx, CONFIG_BITRATE": %i" , value); |
| 233 | } |
| 234 | /* UNFRAMED elementary stream */ |
| 235 | else if (!memcmp(module->line, CONFIG_UNFRAMED, sizeof(CONFIG_UNFRAMED)-1)) |
| 236 | { |
| 237 | track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; |
| 238 | LOG_FORMAT(ctx, CONFIG_UNFRAMED); |
| 239 | } |
| 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) |
| 243 | { |
| 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); |
| 247 | } |
| 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) |
| 251 | { |
| 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); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | if (track) vc_container_free_track(ctx, track); |
| 259 | return VC_CONTAINER_ERROR_CORRUPTED; |
| 260 | } |
| 261 | |
| 262 | static uint32_t simple_convert_packet_flags(VC_CONTAINER_T *ctx, |
| 263 | unsigned int track_num, uint32_t flags) |
| 264 | { |
| 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}, |
| 273 | {0, 0} }; |
| 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}, |
| 278 | {0, 0} }; |
| 279 | const convert_from_t *convert_from = NULL; |
| 280 | int i; |
| 281 | |
| 282 | switch (ctx->tracks[track_num]->priv->module->variant) |
| 283 | { |
| 284 | case VARIANT_MMAL: convert_from = convert_from_mmal; break; |
| 285 | case VARIANT_OMX: convert_from = convert_from_omx; break; |
| 286 | default: break; |
| 287 | } |
| 288 | |
| 289 | if (convert_from) |
| 290 | { |
| 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; |
| 295 | return new_flags; |
| 296 | } |
| 297 | |
| 298 | return flags; |
| 299 | } |
| 300 | |
| 301 | static int64_t simple_convert_packet_pts(VC_CONTAINER_T *ctx, |
| 302 | unsigned int track_num, int64_t pts, uint32_t flags) |
| 303 | { |
| 304 | if (ctx->tracks[track_num]->priv->module->variant == VARIANT_OMX && |
| 305 | flags & 0x100) |
| 306 | return VC_CONTAINER_TIME_UNKNOWN; |
| 307 | |
| 308 | return pts; |
| 309 | } |
| 310 | |
| 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 ) |
| 316 | { |
| 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; |
| 321 | |
| 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; |
| 325 | else |
| 326 | state = &module->state; |
| 327 | |
| 328 | /* Switch to the next packet when the current one is empty */ |
| 329 | if (!state->data_left) |
| 330 | { |
| 331 | unsigned int track_num, size; |
| 332 | int64_t pts; |
| 333 | int flags; |
| 334 | |
| 335 | SEEK(ctx, state->metadata_offset); |
| 336 | status = simple_read_line(ctx); |
| 337 | if (status != VC_CONTAINER_SUCCESS) |
| 338 | return status; |
| 339 | |
| 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) |
| 344 | { |
| 345 | LOG_ERROR(ctx, "invalid metadata: %s" , module->line); |
| 346 | return VC_CONTAINER_ERROR_CORRUPTED; |
| 347 | } |
| 348 | state->metadata_offset = STREAM_POSITION(ctx); |
| 349 | |
| 350 | if (track_num >= ctx->tracks_num) |
| 351 | { |
| 352 | LOG_DEBUG(ctx, "skipping %i bytes for track %d/%d" , |
| 353 | size, track_num, ctx->tracks_num); |
| 354 | return VC_CONTAINER_ERROR_CONTINUE; |
| 355 | } |
| 356 | |
| 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) |
| 362 | { |
| 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; |
| 367 | } |
| 368 | |
| 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); |
| 373 | |
| 374 | /* Discard empty packets */ |
| 375 | if (!state->data_size && !state->flags) |
| 376 | return VC_CONTAINER_ERROR_CONTINUE; |
| 377 | } |
| 378 | |
| 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) |
| 382 | { |
| 383 | LOG_DEBUG(ctx, "skipping track %d/%d as we are ignoring it" , |
| 384 | state->track_num, ctx->tracks_num); |
| 385 | |
| 386 | track_module = ctx->tracks[packet->track]->priv->module; |
| 387 | |
| 388 | /* Handle disconnection from global state */ |
| 389 | if (state == &module->state && |
| 390 | ctx->tracks[state->track_num]->priv->module->state == &module->state) |
| 391 | { |
| 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; |
| 396 | } |
| 397 | |
| 398 | track_module->state->data_left = 0; |
| 399 | return VC_CONTAINER_ERROR_CONTINUE; |
| 400 | } |
| 401 | |
| 402 | /* |
| 403 | * From this point we know we have the packet which was requested |
| 404 | */ |
| 405 | |
| 406 | /* !!!! If we aren't in the right position in the file go there now. */ |
| 407 | |
| 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; |
| 418 | |
| 419 | if (flags & VC_CONTAINER_READ_FLAG_SKIP) |
| 420 | { |
| 421 | track_module->data_offset += state->data_left; |
| 422 | state->data_left = 0; |
| 423 | return VC_CONTAINER_SUCCESS; |
| 424 | } |
| 425 | |
| 426 | if (flags & VC_CONTAINER_READ_FLAG_INFO) |
| 427 | { |
| 428 | return VC_CONTAINER_SUCCESS; |
| 429 | } |
| 430 | |
| 431 | /* Now try to read data into buffer */ |
| 432 | vc_container_io_seek(track_module->io, track_module->data_offset); |
| 433 | |
| 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; |
| 438 | |
| 439 | if (state->data_left) |
| 440 | packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; |
| 441 | |
| 442 | return track_module->io->status; |
| 443 | } |
| 444 | |
| 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) |
| 448 | { |
| 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; |
| 454 | } |
| 455 | |
| 456 | /*****************************************************************************/ |
| 457 | static VC_CONTAINER_STATUS_T simple_reader_close( VC_CONTAINER_T *ctx ) |
| 458 | { |
| 459 | VC_CONTAINER_MODULE_T *module = ctx->priv->module; |
| 460 | |
| 461 | for (; ctx->tracks_num > 0; ctx->tracks_num--) |
| 462 | { |
| 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); |
| 467 | } |
| 468 | |
| 469 | free(module); |
| 470 | return VC_CONTAINER_SUCCESS; |
| 471 | } |
| 472 | |
| 473 | /*****************************************************************************/ |
| 474 | VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T *ctx ) |
| 475 | { |
| 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)]; |
| 479 | unsigned int i; |
| 480 | |
| 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; |
| 485 | |
| 486 | LOG_DEBUG(ctx, "using simple reader" ); |
| 487 | |
| 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; |
| 494 | |
| 495 | status = simple_read_header(ctx); |
| 496 | if (status != VC_CONTAINER_SUCCESS) |
| 497 | goto error; |
| 498 | |
| 499 | /* Open all the elementary streams */ |
| 500 | for (i = 0; i < ctx->tracks_num; i++) |
| 501 | { |
| 502 | VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; |
| 503 | char *uri; |
| 504 | |
| 505 | track->priv->module->io = vc_container_io_open(track->priv->module->uri, |
| 506 | VC_CONTAINER_IO_MODE_READ, &status); |
| 507 | |
| 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) |
| 513 | { |
| 514 | char *end; |
| 515 | |
| 516 | strcpy(uri, ctx->priv->io->uri); |
| 517 | |
| 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) == '\\') |
| 521 | break; |
| 522 | strcpy(end, track->priv->module->uri); |
| 523 | |
| 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); |
| 528 | free(uri); |
| 529 | } |
| 530 | if (!track->priv->module->io) |
| 531 | { |
| 532 | LOG_ERROR(ctx, "could not open elementary stream: %s" , |
| 533 | track->priv->module->uri); |
| 534 | goto error; |
| 535 | } |
| 536 | } |
| 537 | |
| 538 | /* |
| 539 | * We now have all the information we really need to start playing the stream |
| 540 | */ |
| 541 | |
| 542 | module->metadata_offset = STREAM_POSITION(ctx); |
| 543 | |
| 544 | /* Initialise state for all tracks */ |
| 545 | module->state.metadata_offset = module->metadata_offset; |
| 546 | for (i = 0; i < ctx->tracks_num; i++) |
| 547 | { |
| 548 | VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; |
| 549 | track->priv->module->state = &module->state; |
| 550 | } |
| 551 | |
| 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++) |
| 555 | { |
| 556 | VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; |
| 557 | VC_CONTAINER_PACKET_T packet; |
| 558 | packet.track = i; |
| 559 | status = VC_CONTAINER_ERROR_CONTINUE; |
| 560 | |
| 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) |
| 565 | continue; |
| 566 | |
| 567 | status = vc_container_track_allocate_extradata(ctx, track, packet.size); |
| 568 | if (status != VC_CONTAINER_SUCCESS) |
| 569 | continue; |
| 570 | |
| 571 | packet.data = track->format->extradata; |
| 572 | packet.buffer_size = packet.size; |
| 573 | packet.size = 0; |
| 574 | status = simple_reader_read(ctx, &packet, |
| 575 | VC_CONTAINER_READ_FLAG_FORCE_TRACK); |
| 576 | if (status != VC_CONTAINER_SUCCESS) |
| 577 | continue; |
| 578 | |
| 579 | track->format->extradata_size = packet.size; |
| 580 | } |
| 581 | |
| 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; |
| 586 | |
| 587 | error: |
| 588 | LOG_ERROR(ctx, "simple: error opening stream (%i)" , status); |
| 589 | simple_reader_close(ctx); |
| 590 | return status; |
| 591 | } |
| 592 | |
| 593 | /******************************************************************************** |
| 594 | Entrypoint function |
| 595 | ********************************************************************************/ |
| 596 | |
| 597 | #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) |
| 598 | # pragma weak reader_open simple_reader_open |
| 599 | #endif |
| 600 | |