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#include <stdlib.h>
28#include <string.h>
29
30/* Work-around for MSVC debugger issue */
31#define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_FLV_READER_T
32#define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_FLV_READER_T
33
34//#define ENABLE_FLV_EXTRA_LOGGING
35#define CONTAINER_IS_BIG_ENDIAN
36#include "containers/core/containers_private.h"
37#include "containers/core/containers_io_helpers.h"
38#include "containers/core/containers_utils.h"
39#include "containers/core/containers_index.h"
40#include "containers/core/containers_logging.h"
41#undef CONTAINER_HELPER_LOG_INDENT
42#define CONTAINER_HELPER_LOG_INDENT(a) 0
43
44VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx );
45
46/******************************************************************************
47Defines.
48******************************************************************************/
49#define FLV_TRACKS_MAX 2
50
51#define FLV_TAG_TYPE_AUDIO 8
52#define FLV_TAG_TYPE_VIDEO 9
53#define FLV_TAG_TYPE_METADATA 18
54#define FLV_TAG_HEADER_SIZE 15
55
56#define FLV_SCRIPT_DATA_TYPE_NUMBER 0
57#define FLV_SCRIPT_DATA_TYPE_BOOL 1
58#define FLV_SCRIPT_DATA_TYPE_STRING 2
59#define FLV_SCRIPT_DATA_TYPE_ECMA 8
60#define FLV_SCRIPT_DATA_TYPE_LONGSTRING 12
61
62#define FLV_FLAG_DISCARD 1
63#define FLV_FLAG_KEYFRAME 2
64#define FLV_FLAG_INTERFRAME 4
65
66/******************************************************************************
67Type definitions.
68******************************************************************************/
69typedef struct
70{
71 VC_CONTAINER_STATUS_T status;
72
73 int64_t tag_position; /* position of the current tag we're reading */
74 int64_t data_position; /* position to the start of the data within the tag */
75 int data_offset; /* current position inside the tag's data */
76 int data_size; /* size of the data from the current tag */
77 int tag_prev_size; /* size of the previous tag in the stream */
78 int flags; /* flags for the current tag */
79 uint32_t timestamp; /* timestamp for the current tag */
80 uint32_t track; /* track the current tag belongs to */
81 VC_CONTAINER_INDEX_T *index; /* index of key frames */
82
83} FLV_READER_STATE_T;
84
85typedef struct VC_CONTAINER_TRACK_MODULE_T
86{
87 FLV_READER_STATE_T *state;
88 FLV_READER_STATE_T track_state;
89
90} VC_CONTAINER_TRACK_MODULE_T;
91
92typedef struct VC_CONTAINER_MODULE_T
93{
94 VC_CONTAINER_TRACK_T *tracks[FLV_TRACKS_MAX];
95
96 int64_t data_offset; /*< offset to the first FLV tag in the stream */
97
98 FLV_READER_STATE_T state; /*< global state of the reader */
99
100 int audio_track;
101 int video_track;
102
103 uint32_t meta_videodatarate;
104 uint32_t meta_audiodatarate;
105 float meta_framerate;
106 uint32_t meta_width;
107 uint32_t meta_height;
108
109} VC_CONTAINER_MODULE_T;
110
111/******************************************************************************
112Static functions within this file.
113******************************************************************************/
114/** Reads an FLV tag header
115 *
116 * @param p_ctx pointer to our context
117 * @param[out] p_prev_size size of the previous tag
118 * @param[out] p_type type of the tag
119 * @param[out] p_type size of the tag
120 * @param[out] p_type timestamp for the tag
121 * @return VC_CONTAINER_SUCCESS on success
122 */
123static VC_CONTAINER_STATUS_T flv_read_tag_header(VC_CONTAINER_T *p_ctx, int *p_prev_size,
124 int *p_type, int *p_size, uint32_t *p_timestamp)
125{
126 int prev_size, type, size;
127 uint32_t timestamp;
128
129 prev_size = READ_U32(p_ctx, "PreviousTagSize");
130 type = READ_U8(p_ctx, "TagType");
131 size = READ_U24(p_ctx, "DataSize");
132 timestamp = READ_U24(p_ctx, "Timestamp");
133 timestamp |= (READ_U8(p_ctx, "TimestampExtended") << 24);
134 SKIP_U24(p_ctx, "StreamID");
135
136 if(p_prev_size) *p_prev_size = prev_size + 4;
137 if(p_type) *p_type = type;
138 if(p_size) *p_size = size;
139 if(p_timestamp) *p_timestamp = timestamp;
140
141 return STREAM_STATUS(p_ctx);
142}
143
144/** Reads an FLV video data header.
145 * This contains the codec id and the current frame type.
146 *
147 * @param p_ctx pointer to our context
148 * @param[out] codec video codec
149 * @param[out] frame_type type of the current frame
150 * @return VC_CONTAINER_SUCCESS on success
151 */
152static VC_CONTAINER_STATUS_T flv_read_videodata_header(VC_CONTAINER_T *p_ctx,
153 VC_CONTAINER_FOURCC_T *codec, int *frame_type)
154{
155 uint8_t header = READ_U8(p_ctx, "FrameType/CodecID");
156
157 if(frame_type)
158 *frame_type = (header >> 4) == 1 ? FLV_FLAG_KEYFRAME :
159 (header >> 4) == 3 ? FLV_FLAG_INTERFRAME : 0;
160
161 switch(header &0xF)
162 {
163 case 2: *codec = VC_CONTAINER_CODEC_SPARK; break;
164 case 3: *codec = VC_FOURCC('s','c','r','1'); break; /* screen video */
165 case 4: *codec = VC_CONTAINER_CODEC_VP6; break;
166 case 5: *codec = VC_FOURCC('v','p','6','a'); break; /* vp6 alpha */
167 case 6: *codec = VC_FOURCC('s','c','r','2'); break; /* screen video 2 */
168 case 7: *codec = VC_CONTAINER_CODEC_H264; break;
169 default: *codec = 0; break;
170 }
171
172 return STREAM_STATUS(p_ctx);
173}
174
175/** Get the properties of a video frame
176 * This is only really useful at setup time when trying to detect
177 * the type of content we are dealing with.
178 * This will try to get some of the properties of the video stream
179 * as well as codec configuration data if there is any.
180 *
181 * @param p_ctx pointer to our context
182 * @param track track number this data/tag belongs to
183 * @param size size of the data we are parsing
184 * @return VC_CONTAINER_SUCCESS on success
185 */
186static VC_CONTAINER_STATUS_T flv_read_videodata_properties(VC_CONTAINER_T *p_ctx,
187 VC_CONTAINER_TRACK_T *track, int size)
188{
189 VC_CONTAINER_STATUS_T status;
190 int width = 0, height = 0;
191
192 if(track->format->codec == VC_CONTAINER_CODEC_VP6 ||
193 track->format->codec == VC_FOURCC('v','p','6','a'))
194 {
195 /* Just extract the width / height */
196 uint8_t data = _READ_U8(p_ctx);
197 _SKIP_U16(p_ctx);
198 height = _READ_U8(p_ctx) * 16;
199 width = _READ_U8(p_ctx) * 16;
200 width -= data >> 4;
201 height -= data & 0xf;
202 }
203 else if(track->format->codec == VC_CONTAINER_CODEC_H264)
204 {
205 uint8_t type = _READ_U8(p_ctx); size--;
206 if(type || size <= 8) return VC_CONTAINER_ERROR_CORRUPTED;
207 _SKIP_U24(p_ctx); size-=3;
208
209 track->format->codec_variant = VC_FOURCC('a','v','c','C');
210 status = vc_container_track_allocate_extradata(p_ctx, track, size);
211 if(status != VC_CONTAINER_SUCCESS) return status;
212 track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size);
213 }
214
215 track->format->type->video.width = width;
216 track->format->type->video.height = height;
217
218 return STREAM_STATUS(p_ctx);
219}
220
221/** Reads an FLV audio data header.
222 * This contains the codec id, samplerate, number of channels and bitrate.
223 *
224 * @param p_ctx pointer to our context
225 * @param[out] codec audio codec
226 * @param[out] p_samplerate audio sampling rate
227 * @param[out] p_channels number of audio channels
228 * @param[out] p_bps bits per sample
229 * @return VC_CONTAINER_SUCCESS on success
230 */
231static VC_CONTAINER_STATUS_T flv_read_audiodata_header(VC_CONTAINER_T *p_ctx,
232 VC_CONTAINER_FOURCC_T *codec, int *p_samplerate, int *p_channels, int *p_bps)
233{
234 int samplerate, channels, bps;
235 uint8_t header = _READ_U8(p_ctx);
236
237 switch((header >> 2) & 0x3)
238 {
239 case 0: samplerate = 5512; break;
240 case 1: samplerate = 11025; break;
241 case 2: samplerate = 22050; break;
242 default:
243 case 3: samplerate = 44100; break;
244 }
245
246 channels = 1 << (header & 1);
247 bps = 8 << ((header >> 1) & 1);
248
249 switch(header >> 4)
250 {
251 case 0: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED; break;
252 case 1: *codec = VC_CONTAINER_CODEC_ADPCM_SWF; break;
253 case 2: *codec = VC_CONTAINER_CODEC_MPGA; break;
254 case 3: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED_LE; break;
255 case 4: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 8000; channels = 1; break;
256 case 5: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 16000; channels = 1; break;
257 case 6: *codec = VC_CONTAINER_CODEC_NELLYMOSER; channels = 1; break;
258 case 7: *codec = VC_CONTAINER_CODEC_ALAW; break;
259 case 8: *codec = VC_CONTAINER_CODEC_MULAW; break;
260 case 10: *codec = VC_CONTAINER_CODEC_MP4A; samplerate = 44100; channels = 2; break;
261 case 11: *codec = VC_CONTAINER_CODEC_SPEEX; break;
262 case 14: *codec = VC_CONTAINER_CODEC_MPGA; samplerate = 8000; break;
263 default: *codec = 0; break;
264 }
265
266 if(p_samplerate) *p_samplerate = samplerate;
267 if(p_channels) *p_channels = channels;
268 if(p_bps) *p_bps = bps;
269
270 return STREAM_STATUS(p_ctx);
271}
272
273/** Get the properties of an audio frame
274 * This is only really useful at setup time when trying to detect
275 * the type of content we are dealing with.
276 * This will try to get some of the properties of the audio stream
277 * as well as codec configuration data if there is any.
278 *
279 * @param p_ctx pointer to our context
280 * @param track track number this data/tag belongs to
281 * @param size size of the data we are parsing
282 * @param samplerate sampling rate of the audio data
283 * @param channels number of channels of the audio data
284 * @param bps bits per sample
285 * @return VC_CONTAINER_SUCCESS on success
286 */
287static VC_CONTAINER_STATUS_T flv_read_audiodata_properties(VC_CONTAINER_T *p_ctx,
288 VC_CONTAINER_TRACK_T *track, int size, int samplerate, int channels, int bps)
289{
290 static const int aac_freq[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000,
291 22050, 16000, 12000, 11025, 8000, 7350};
292 VC_CONTAINER_STATUS_T status;
293
294 if(track->format->codec == VC_CONTAINER_CODEC_MP4A)
295 {
296 uint8_t *p_data, data = _READ_U8(p_ctx);
297 size--;
298 if(data || size <= 0) return VC_CONTAINER_ERROR_FAILED;
299
300 /* Read the AudioSpecificConfig */
301 status = vc_container_track_allocate_extradata(p_ctx, track, size);
302 if(status != VC_CONTAINER_SUCCESS) return status;
303 track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size);
304
305 if(track->format->extradata_size >= 2)
306 {
307 p_data = track->format->extradata;
308 data = ((p_data[0] & 0x7) << 1) | (p_data[1] >> 7);
309 if(data >= countof(aac_freq))
310 return VC_CONTAINER_ERROR_FAILED;
311
312 samplerate = aac_freq[data];
313 channels = (p_data[1] >> 3) & 0xf;
314 if(track->format->extradata_size >= 5 && data == 0xf)
315 {
316 samplerate = ((p_data[1] & 0x7f) << 17) | (p_data[2] << 9) |
317 (p_data[3] << 1) | (p_data[4] >> 7);
318 channels = (p_data[4] >> 3) & 0xf;
319 }
320 }
321 }
322
323 track->format->type->audio.sample_rate = samplerate;
324 track->format->type->audio.channels = channels;
325 track->format->type->audio.bits_per_sample = bps;
326
327 return STREAM_STATUS(p_ctx);
328}
329
330/** Reads an FLV metadata tag.
331 * This contains metadata information about the stream.
332 * All the data we extract from this will be placed directly in the context.
333 *
334 * @param p_ctx pointer to our context
335 * @param size size of the tag
336 * @return FLV_FILE_NO_ERROR on success
337 */
338static int flv_read_metadata(VC_CONTAINER_T *p_ctx, int size)
339{
340 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
341#define MAX_METADATA_STRING_SIZE 25
342 char psz_string[MAX_METADATA_STRING_SIZE+1];
343 uint16_t length, num_values;
344 double f_value;
345 uint64_t u_value;
346 uint8_t type;
347
348 /* We're looking for an onMetaData script */
349 type = READ_U8(p_ctx, "Type"); size--;
350 if(type != FLV_SCRIPT_DATA_TYPE_STRING) return VC_CONTAINER_SUCCESS;
351 length = READ_U16(p_ctx, "StringLength"); size -= 2;
352 if(!length || length > size || length > MAX_METADATA_STRING_SIZE) return VC_CONTAINER_SUCCESS;
353 if(READ_BYTES(p_ctx, psz_string, length) != length) return VC_CONTAINER_SUCCESS;
354 psz_string[length] = 0; size -= length;
355 if(strcmp(psz_string, "onMetaData")) return VC_CONTAINER_SUCCESS;
356 if(size < 5) return VC_CONTAINER_SUCCESS;
357 type = READ_U8(p_ctx, "Type"); size--;
358 if(type != FLV_SCRIPT_DATA_TYPE_ECMA) return VC_CONTAINER_SUCCESS;
359 num_values = READ_U32(p_ctx, "ECMAArrayLength"); size -= 4;
360
361 /* We found our script, now extract the metadata values */
362 while(num_values-- && size >= 2)
363 {
364 uint16_t length = _READ_U16(p_ctx); size -= 2;
365 if(!length || length >= size || length > MAX_METADATA_STRING_SIZE) break;
366 if(READ_BYTES(p_ctx, psz_string, length) != length) break;
367 psz_string[length] = 0; size -= length;
368 type = _READ_U8(p_ctx); size--;
369
370 switch(type)
371 {
372 case FLV_SCRIPT_DATA_TYPE_NUMBER:
373 /* We only cope with DOUBLE types*/
374 if(size < 8) return VC_CONTAINER_SUCCESS;
375
376 u_value = _READ_U64(p_ctx); size -= 8;
377 /* Convert value into a double */
378 {
379 int64_t value = ((u_value & ((UINT64_C(1)<<52)-1)) + (UINT64_C(1)<<52)) * ((((int64_t)u_value)>>63)|1);
380 int exp = ((u_value>>52)&0x7FF)-1075 + 16;
381 if(exp >= 0) value <<= exp;
382 else value >>= -exp;
383 f_value = ((double)value) / (1 << 16);
384 }
385
386 LOG_DEBUG(p_ctx, "metadata (%s=%i.%i)", psz_string,
387 ((int)(f_value*100))/100, ((int)(f_value*100))%100);
388
389 if(!strcmp(psz_string, "duration"))
390 p_ctx->duration = (int64_t)(f_value*INT64_C(1000000));
391 if(!strcmp(psz_string, "videodatarate"))
392 module->meta_videodatarate = (uint32_t)f_value;
393 if(!strcmp(psz_string, "width"))
394 module->meta_width = (uint32_t)f_value;
395 if(!strcmp(psz_string, "height"))
396 module->meta_height = (uint32_t)f_value;
397 if(!strcmp(psz_string, "framerate"))
398 module->meta_framerate = f_value;
399 if(!strcmp(psz_string, "audiodatarate"))
400 module->meta_audiodatarate = (uint32_t)f_value;
401 continue;
402
403 /* We skip these */
404 case FLV_SCRIPT_DATA_TYPE_BOOL:
405 if(size < 1) return VC_CONTAINER_SUCCESS;
406 u_value = _READ_U8(p_ctx); size -= 1;
407 LOG_DEBUG(p_ctx, "metadata (%s=%i)", psz_string, (int)u_value);
408 continue;
409
410 case FLV_SCRIPT_DATA_TYPE_STRING:
411 if(size < 2) return VC_CONTAINER_SUCCESS;
412 length = _READ_U16(p_ctx); size -= 2;
413 if(length > size) return VC_CONTAINER_SUCCESS;
414 SKIP_BYTES(p_ctx, length); size -= length;
415 LOG_DEBUG(p_ctx, "metadata skipping (%s)", psz_string);
416 continue;
417
418 /* We can't cope with anything else */
419 default:
420 LOG_DEBUG(p_ctx, "unknown amf type (%s,%i)", psz_string, type);
421 return VC_CONTAINER_SUCCESS;
422 }
423 }
424
425 return STREAM_STATUS(p_ctx);
426}
427
428/** Reads an FLV frame header.
429 * This reads the current tag header and matches the contained frame
430 * with one of the tracks we have. If no match can be found, the frame is marked
431 * for discarding. The current read position will be updated to the start
432 * of the data (i.e. the frame) contained within the FLV tag.
433 *
434 * @param p_ctx pointer to our context
435 * @param[out] p_track track this frame belongs to
436 * @param[out] p_size size of the frame
437 * @param[out] p_timestamp timestamp of the frame
438 * @param[out] p_flags flags associated with the frame
439 * @param b_extra_check whether to perform extra sanity checking on the tag
440 * @return VC_CONTAINER_SUCCESS on success
441 */
442static int flv_read_frame_header(VC_CONTAINER_T *p_ctx, int *p_prev_size,
443 int *p_track, int *p_size, uint32_t *p_timestamp, int *p_flags,
444 int b_extra_check)
445{
446 int64_t position = STREAM_POSITION(p_ctx);
447 int type, size, flags = 0, frametype = 0;
448 VC_CONTAINER_STATUS_T status;
449 VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN;
450 unsigned int track = p_ctx->tracks_num;
451 uint32_t codec = 0;
452
453 status = flv_read_tag_header(p_ctx, p_prev_size, &type, &size, p_timestamp);
454 if(status != VC_CONTAINER_SUCCESS) return status;
455
456 if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return status;
457 if(position == STREAM_POSITION(p_ctx)) return VC_CONTAINER_ERROR_EOS;
458 if(type == 0) return VC_CONTAINER_ERROR_CORRUPTED;
459
460 /* Sanity checking */
461 if(b_extra_check && type != FLV_TAG_TYPE_AUDIO &&
462 type != FLV_TAG_TYPE_VIDEO && type != FLV_TAG_TYPE_METADATA)
463 return VC_CONTAINER_ERROR_CORRUPTED;
464
465 /* We're only interested in audio / video */
466 if((type != FLV_TAG_TYPE_AUDIO && type != FLV_TAG_TYPE_VIDEO) || !size)
467 {
468 flags |= FLV_FLAG_DISCARD;
469 goto end;
470 }
471
472 if(type == FLV_TAG_TYPE_AUDIO)
473 {
474 flv_read_audiodata_header(p_ctx, &codec, 0, 0, 0);
475 es_type = VC_CONTAINER_ES_TYPE_AUDIO;
476 }
477 else if(type == FLV_TAG_TYPE_VIDEO)
478 {
479 flv_read_videodata_header(p_ctx, &codec, &frametype);
480 es_type = VC_CONTAINER_ES_TYPE_VIDEO;
481 }
482 size--;
483
484 /* Find which track this belongs to */
485 for(track = 0; track < p_ctx->tracks_num; track++)
486 if(es_type == p_ctx->tracks[track]->format->es_type) break;
487 if(track == p_ctx->tracks_num)
488 flags |= FLV_FLAG_DISCARD;
489
490 /* Sanity checking */
491 if(b_extra_check && codec != p_ctx->tracks[track]->format->codec)
492 return VC_CONTAINER_ERROR_CORRUPTED;
493
494 end:
495 // add to the index if we have one, and we're not discarding this frame.
496 // also require that we either have no video track or this is a video frame marked as a key frame.
497 if(p_ctx->priv->module->state.index && !(flags & FLV_FLAG_DISCARD) &&
498 (p_ctx->priv->module->video_track < 0 || (es_type == VC_CONTAINER_ES_TYPE_VIDEO && (frametype & FLV_FLAG_KEYFRAME))))
499 vc_container_index_add(p_ctx->priv->module->state.index, (int64_t) (*p_timestamp) * 1000LL, position);
500
501 *p_flags = flags | frametype;
502 *p_size = size;
503 *p_track = track;
504 return VC_CONTAINER_SUCCESS;
505}
506
507/** Validate the data contained within the frame and update the read
508 * position to the start of the frame data that we want to feed to the codec.
509 *
510 * Each codec is packed slightly differently so this function is necessary
511 * to prepare for reading the actual codec data.
512 *
513 * @param p_ctx pointer to our context
514 * @param track track this frame belongs to
515 * @param[in] p_size size of the frame
516 * @param[out] p_size updated size of the frame
517 * @param[in] p_timestamp timestamp for the frame
518 * @param[out] p_timestamp updated timestamp for the frame
519 * @return VC_CONTAINER_SUCCESS on success
520 */
521static VC_CONTAINER_STATUS_T flv_validate_frame_data(VC_CONTAINER_T *p_ctx,
522 int track, int *p_size, uint32_t *p_timestamp)
523{
524 int32_t time_offset;
525
526 if(track >= (int)p_ctx->tracks_num)
527 return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE;
528
529 switch(p_ctx->tracks[track]->format->codec)
530 {
531 case VC_CONTAINER_CODEC_VP6:
532 if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED;
533 _READ_U8(p_ctx); *p_size -= 1;
534 break;
535 case VC_CONTAINER_CODEC_MP4A:
536 if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED;
537 *p_size -= 1;
538 if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/
539 break;
540 case VC_CONTAINER_CODEC_H264:
541 if(*p_size < 4) return VC_CONTAINER_ERROR_CORRUPTED;
542 *p_size -= 1;
543 if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/
544 time_offset = _READ_U24(p_ctx);
545 time_offset <<= 8; time_offset >>= 8; /* change to signed */
546 *p_timestamp += time_offset;
547 *p_size -= 3;
548 break;
549 default:
550 break;
551 }
552
553 return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE;
554}
555
556/** Small utility function to update the reading position of a track
557 */
558static void flv_update_track_position(VC_CONTAINER_T *p_ctx, int track,
559 int64_t tag_position, int tag_prev_size, int64_t data_position,
560 int data_size, uint32_t timestamp, int flags)
561{
562 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module;
563 track_module->state->tag_position = tag_position;
564 track_module->state->tag_prev_size = tag_prev_size;
565 track_module->state->data_position = data_position;
566 track_module->state->data_size = data_size;
567 track_module->state->data_offset = 0;
568 track_module->state->timestamp = timestamp;
569 track_module->state->flags = flags;
570 track_module->state->track = track;
571}
572
573/** Utility function to find the next frame of a given track in the stream.
574 *
575 * This will basically walk through all the tags in the file until it
576 * finds a tag/frame which belongs to the given track.
577 *
578 * @param p_ctx pointer to our context
579 * @param track track wanted
580 * @param[out] p_size size of the frame
581 * @param[out] p_timestamp timestamp of the frame
582 * @param[out] p_flags flags associated with the frame
583 * @param b_keyframe whether we specifically want a keyframe or not
584 * @param b_extra_check whether to perform extra sanity checking on the tag
585 * @return VC_CONTAINER_SUCCESS on success
586 */
587static VC_CONTAINER_STATUS_T flv_find_next_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size,
588 uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check)
589{
590 int frame_track, prev_size, size, flags;
591 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
592 FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state;
593 uint32_t timestamp;
594 int64_t position;
595 VC_CONTAINER_PARAM_UNUSED(b_extra_check);
596
597 /* Seek to the next tag in the stream or the current position
598 * if none of its data has been consumed */
599 position = state->tag_position;
600 if(state->data_offset)
601 position = state->data_position + state->data_size;
602 status = SEEK(p_ctx, position);
603 if(status != VC_CONTAINER_SUCCESS) return status;
604
605 /* Look for the next frame we want */
606 while (status == VC_CONTAINER_SUCCESS)
607 {
608 position = STREAM_POSITION(p_ctx);
609 status = flv_read_frame_header(p_ctx, &prev_size, &frame_track,
610 &size, &timestamp, &flags, 0);
611 if(status != VC_CONTAINER_SUCCESS) break;
612
613 if(flags & FLV_FLAG_DISCARD) goto skip;
614 if(frame_track != track) goto skip;
615
616 if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO &&
617 !(flags & FLV_FLAG_KEYFRAME)) goto skip;
618
619 if(flv_validate_frame_data(p_ctx, track, &size, &timestamp) != VC_CONTAINER_SUCCESS)
620 goto skip;
621
622 /* We have what we need */
623 flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx),
624 size, timestamp, flags);
625 break;
626
627 skip:
628 flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx),
629 size, timestamp, 0);
630 state->data_offset = size; /* consume data */
631
632 if(SKIP_BYTES(p_ctx, size) != (size_t)size) status = STREAM_STATUS(p_ctx);
633 }
634
635 if(!status)
636 {
637 if(p_size) *p_size = size;
638 if(p_timestamp) *p_timestamp = timestamp;
639 if(p_flags) *p_flags = flags;
640 }
641
642 return status;
643}
644
645/** Utility function to find the previous frame of a given track in the stream.
646 *
647 * This will basically walk back through all the tags in the file until it
648 * finds a tag/frame which belongs to the given track.
649 *
650 * @param p_ctx pointer to our context
651 * @param track track wanted
652 * @param[out] p_size size of the frame
653 * @param[out] p_timestamp timestamp of the frame
654 * @param[out] p_flags flags associated with the frame
655 * @param b_keyframe whether we specifically want a keyframe or not
656 * @param b_extra_check whether to perform extra sanity checking on the tag
657 * @return VC_CONTAINER_SUCCESS on success
658 */
659static VC_CONTAINER_STATUS_T flv_find_previous_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size,
660 uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check)
661{
662 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
663 FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state;
664 int frame_track, prev_size, size, flags;
665 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
666 uint32_t timestamp;
667 int64_t position;
668
669 /* Look for the previous frame we want */
670 while (status == VC_CONTAINER_SUCCESS)
671 {
672 /* Seek to the previous tag in the stream */
673 position = state->tag_position - state->tag_prev_size;
674 if(position < module->data_offset) position = module->data_offset;
675 status = SEEK(p_ctx, position);
676 if(status != VC_CONTAINER_SUCCESS) return status;
677
678 status = flv_read_frame_header(p_ctx, &prev_size, &frame_track,
679 &size, &timestamp, &flags, 0);
680 if(status) break;
681
682 if(flags & FLV_FLAG_DISCARD) goto skip;
683 if(frame_track != track) goto skip;
684
685 if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO &&
686 !(flags & FLV_FLAG_KEYFRAME)) goto skip;
687
688 if(flv_validate_frame_data(p_ctx, track, &size, &timestamp) != VC_CONTAINER_SUCCESS)
689 goto skip;
690
691 /* We have what we need */
692 flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx),
693 size, timestamp, flags);
694 break;
695
696 skip:
697 if(position <= module->data_offset)
698 {
699 /* We're back at the beginning but we still want to return something */
700 flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0,
701 (int64_t)module->data_offset, 0, 0, 0);
702 return flv_find_next_frame(p_ctx, track, p_size, p_timestamp, p_flags, b_keyframe, b_extra_check);
703 }
704 flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx),
705 size, timestamp, 0);
706 state->data_offset = size; /* consume data */
707 }
708
709 if(!status)
710 {
711 if(p_size) *p_size = size;
712 if(p_timestamp) *p_timestamp = timestamp;
713 if(p_flags) *p_flags = flags;
714 }
715 return status;
716}
717
718/*****************************************************************************/
719static VC_CONTAINER_STATUS_T flv_reader_close( VC_CONTAINER_T *p_ctx )
720{
721 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
722 unsigned int i;
723
724 for(i = 0; i < p_ctx->tracks_num; i++)
725 vc_container_free_track(p_ctx, p_ctx->tracks[i]);
726
727 if(module->state.index)
728 vc_container_index_free(module->state.index);
729
730 free(module);
731 return VC_CONTAINER_SUCCESS;
732}
733
734/*****************************************************************************/
735static VC_CONTAINER_STATUS_T flv_read_sample_header( VC_CONTAINER_T *p_ctx,
736 FLV_READER_STATE_T *state)
737{
738 int track, prev_size, size, flags;
739 uint32_t timestamp;
740 int64_t position;
741
742 /* Check if we still have some data left to read from the current frame */
743 if(state->data_offset < state->data_size)
744 return state->status;
745
746 /* Read the next tag header */
747 position = STREAM_POSITION(p_ctx);
748 state->status = flv_read_frame_header(p_ctx, &prev_size, &track,
749 &size, &timestamp, &flags, 0);
750 if(state->status != VC_CONTAINER_SUCCESS)
751 return state->status;
752
753 state->status = flv_validate_frame_data(p_ctx, track, &size, &timestamp);
754 if(state->status == VC_CONTAINER_ERROR_CONTINUE)
755 {
756 /* Skip it */
757 state->status = VC_CONTAINER_SUCCESS;
758 track = p_ctx->tracks_num;
759 }
760 if(state->status != VC_CONTAINER_SUCCESS)
761 return state->status;
762
763 state->tag_position = position;
764 state->data_position = STREAM_POSITION(p_ctx);
765 state->data_size = size;
766 state->data_offset = 0;
767 state->flags = flags;
768 state->tag_prev_size = prev_size;
769 state->timestamp = timestamp;
770 state->track = track;
771 return state->status;
772}
773
774/*****************************************************************************/
775static VC_CONTAINER_STATUS_T flv_read_sample_data( VC_CONTAINER_T *p_ctx,
776 FLV_READER_STATE_T *state, uint8_t *data, unsigned int *data_size )
777{
778 unsigned int size = state->data_size - state->data_offset;
779
780 if(state->status != VC_CONTAINER_SUCCESS) return state->status;
781
782 if(data_size && *data_size < size) size = *data_size;
783
784 if(!data) size = SKIP_BYTES(p_ctx, size);
785 else size = READ_BYTES(p_ctx, data, size);
786 state->data_offset += size;
787
788 if(data_size) *data_size = size;
789 state->status = STREAM_STATUS(p_ctx);
790
791 return state->status;
792}
793
794/*****************************************************************************/
795static VC_CONTAINER_STATUS_T flv_reader_read( VC_CONTAINER_T *p_ctx,
796 VC_CONTAINER_PACKET_T *packet, uint32_t flags )
797{
798 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
799 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
800 FLV_READER_STATE_T *state = &module->state;
801 unsigned int data_size;
802
803 /* TODO: select right state */
804
805 status = flv_read_sample_header(p_ctx, state);
806 if(status != VC_CONTAINER_SUCCESS) return status;
807
808#ifdef ENABLE_FLV_EXTRA_LOGGING
809 LOG_DEBUG(p_ctx, "read_sample_header (%i,%i,%i/%i/%i/%i)", state->timestamp, state->flags,
810 (int)state->tag_position, (int)(state->data_position-state->tag_position), state->data_offset, state->data_size);
811#endif
812
813 if(state->track >= p_ctx->tracks_num || !p_ctx->tracks[state->track]->is_enabled)
814 {
815 /* Skip packet */
816 status = flv_read_sample_data(p_ctx, state, 0, 0);
817 if(status != VC_CONTAINER_SUCCESS) return status;
818 return VC_CONTAINER_ERROR_CONTINUE;
819 }
820
821 if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */
822 return flv_read_sample_data(p_ctx, state, 0, 0);
823
824 packet->dts = packet->pts = state->timestamp * (int64_t)1000;
825 packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END;
826 if(state->flags & FLV_FLAG_KEYFRAME) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
827 if(!state->data_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
828 packet->track = state->track;
829
830 // The frame size is all the data
831 packet->frame_size = state->data_size;
832
833 // the size is what's left
834 packet->size = state->data_size - state->data_offset;
835
836 if(flags & VC_CONTAINER_READ_FLAG_SKIP)
837 return flv_read_sample_data(p_ctx, state, 0, 0);
838 else if(flags & VC_CONTAINER_READ_FLAG_INFO)
839 return VC_CONTAINER_SUCCESS;
840
841 data_size = packet->buffer_size;
842 status = flv_read_sample_data(p_ctx, state, packet->data, &data_size);
843 if(status != VC_CONTAINER_SUCCESS)
844 {
845 /* FIXME */
846 return status;
847 }
848
849 packet->size = data_size;
850 if(state->data_offset != state->data_size)
851 packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
852
853 return VC_CONTAINER_SUCCESS;
854}
855
856/*****************************************************************************/
857static VC_CONTAINER_STATUS_T flv_reader_seek(VC_CONTAINER_T *p_ctx,
858 int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
859{
860 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
861 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
862 FLV_READER_STATE_T last_state = {0};
863 FLV_READER_STATE_T *state;
864 uint32_t time = (*offset / 1000), timestamp, previous_time;
865 unsigned int i, track;
866 int size, past = 0;
867 int64_t position;
868 VC_CONTAINER_PARAM_UNUSED(mode);
869
870 /* If we have a video track, then we want to find the keyframe closest to
871 * the requested time, otherwise we just look for the tag with the
872 * closest timestamp */
873
874 /* Select the track on which we'll do our seeking */
875 for(i = 0, track = 0; i < p_ctx->tracks_num; i++)
876 {
877 if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue;
878 track = i;
879 break;
880 }
881 if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_CORRUPTED;
882 state = p_ctx->tracks[track]->priv->module->state;
883 previous_time = state->timestamp;
884
885 LOG_DEBUG(p_ctx, "seek (%i, prev %i)", time, previous_time);
886
887 if(state->index && vc_container_index_get(state->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD,
888 offset, &position, &past) == VC_CONTAINER_SUCCESS)
889 {
890 flv_update_track_position(p_ctx, track, position, 0, position, 0, (uint32_t) (*offset / 1000LL), 0);
891 }
892 else
893 {
894 if(time < state->timestamp / 2)
895 flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0,
896 (int64_t)module->data_offset, 0, 0, 0);
897 past = 1;
898 }
899
900 /* If past it clear then we're done, otherwise we need to find our point from here */
901 if(past == 0)
902 {
903 status = flv_find_next_frame(p_ctx, track, &size, &timestamp, 0, 1 /*keyframe*/, 0);
904 }
905 else
906 {
907 if(time > previous_time)
908 {
909 while(!status)
910 {
911 status = flv_find_next_frame(p_ctx, track, &size, &timestamp, 0, 1 /*keyframe*/, 0);
912 if(status) break;
913
914 /* Check if we have our frame */
915 if(time <= timestamp) break;
916
917 last_state = *state;
918 state->data_offset = size; /* consume data */
919 }
920 }
921 else
922 {
923 while(!status)
924 {
925 status = flv_find_previous_frame(p_ctx, track, &size, &timestamp, 0, 1 /*keyframe*/, 0);
926 if(status) break;
927
928 /* Check if we have our frame */
929 if(time >= timestamp) break;
930
931 /* Detect when we've reached the 1st keyframe to avoid an infinite loop */
932 if(state->timestamp == last_state.timestamp) break;
933
934 last_state = *state;
935 state->data_offset = size; /* consume data */
936 }
937 }
938 }
939
940 if(status != VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
941 {
942 LOG_DEBUG(p_ctx, "seek failed (%i)", status);
943 return status;
944 }
945 else if(status != VC_CONTAINER_SUCCESS)
946 {
947 LOG_DEBUG(p_ctx, "seek failed (%i), look for previous frame", status);
948 if(last_state.tag_position) *state = last_state;
949 else status = flv_find_previous_frame(p_ctx, track, &size, &timestamp, 0, 1 /*keyframe*/, 0);
950 }
951
952 LOG_DEBUG(p_ctx, "seek done (%i)", timestamp);
953 state->status = VC_CONTAINER_SUCCESS;
954 last_state.status = VC_CONTAINER_SUCCESS;
955
956 if(past == 1)
957 {
958 /* Make adjustment based on seek mode */
959 if((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp < time && timestamp < previous_time)
960 {
961 if(last_state.tag_position) *state = last_state;
962 else status = flv_find_next_frame(p_ctx, track, &size, &timestamp, 0, 1 /*keyframe*/, 0);
963 }
964 else if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp > time)
965 {
966 if(last_state.tag_position) *state = last_state;
967 else status = flv_find_previous_frame(p_ctx, track, &size, &timestamp, 0, 1 /*keyframe*/, 0);
968 }
969
970 LOG_DEBUG(p_ctx, "seek adjustment (%i)", timestamp);
971 }
972
973 if(state->data_position == last_state.data_position)
974 status = SEEK(p_ctx, state->data_position);
975
976 *offset = timestamp * INT64_C(1000);
977
978 return VC_CONTAINER_SUCCESS;
979}
980
981/******************************************************************************
982Global function definitions.
983******************************************************************************/
984
985VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx )
986{
987 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
988 VC_CONTAINER_MODULE_T *module = 0;
989 uint8_t buffer[4], type_flags;
990 unsigned int i, frames, audio_present, video_present;
991 uint32_t data_offset;
992
993 /* Check the FLV marker */
994 if( PEEK_BYTES(p_ctx, buffer, 4) < 4 ) goto error;
995 if( buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V' )
996 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
997 /* Check FLV version */
998 if( buffer[3] > 4 )
999 {
1000 LOG_DEBUG(p_ctx, "Version too high: %d", buffer[3]);
1001 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1002 }
1003
1004 SKIP_BYTES(p_ctx, 4); /* FLV marker and version */
1005
1006 /* Find out which tracks should be available.
1007 * FLV can only have up to 1 audio track and 1 video track. */
1008 type_flags = READ_U8(p_ctx, "TypeFlags");
1009 audio_present = !!(type_flags & 0x04);
1010 video_present = !!(type_flags & 0x01);
1011
1012 /* Sanity check DataOffset */
1013 data_offset = READ_U32(p_ctx, "DataOffset");
1014 if(data_offset < 9) goto error;
1015
1016 /*
1017 * We are dealing with an FLV file
1018 */
1019
1020 LOG_DEBUG(p_ctx, "using flv reader");
1021
1022 /* Allocate our context */
1023 module = malloc(sizeof(*module));
1024 if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
1025 memset(module, 0, sizeof(*module));
1026 p_ctx->priv->module = module;
1027 p_ctx->tracks = module->tracks;
1028 module->data_offset = data_offset;
1029 module->audio_track = -1;
1030 module->video_track = -1;
1031
1032 /* Skip to the start of the actual data */
1033 SKIP_BYTES(p_ctx, data_offset - 9);
1034
1035 /* We'll start parsing a few of the FLV tags to find out the
1036 * metadata / audio / video properties.
1037 * The first tag we should see is the metadata one which will give us all the
1038 * properties of the stream. However we do not rely on that being there and we
1039 * actually look at the first audio / video tags as well. */
1040 for(frames = 0; frames < 20; frames++)
1041 {
1042 VC_CONTAINER_TRACK_T *track;
1043 int64_t offset, skip;
1044 int prev_size, type, size, channels, samplerate, bps;
1045 uint32_t codec, timestamp;
1046
1047 /* Stop there if we have everything we want */
1048 if(audio_present == (module->audio_track >= 0) &&
1049 video_present == (module->video_track >= 0)) break;
1050 if(module->audio_track >= 0 && module->video_track >= 0) break;
1051
1052 /* Start reading the next tag */
1053 if(flv_read_tag_header(p_ctx, &prev_size, &type, &size, &timestamp)) break;
1054 if(!size) continue;
1055
1056 offset = STREAM_POSITION(p_ctx); /* to keep track of how much data we read */
1057
1058 switch(type)
1059 {
1060 case FLV_TAG_TYPE_AUDIO:
1061 if(module->audio_track >= 0) break; /* We already have our audio track */
1062 flv_read_audiodata_header(p_ctx, &codec, &samplerate, &channels, &bps);
1063
1064 p_ctx->tracks[p_ctx->tracks_num] = track =
1065 vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
1066 if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1067
1068 track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
1069 track->format->codec = codec;
1070 flv_read_audiodata_properties(p_ctx, track, size - 1, samplerate, channels, bps);
1071
1072 module->audio_track = p_ctx->tracks_num++;
1073 track->is_enabled = 1;
1074 track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
1075 break;
1076
1077 case FLV_TAG_TYPE_VIDEO:
1078 if(module->video_track >= 0) break; /* We already have our video track */
1079 flv_read_videodata_header(p_ctx, &codec, 0);
1080
1081 p_ctx->tracks[p_ctx->tracks_num] = track =
1082 vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
1083 if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1084
1085 track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
1086 track->format->codec = codec;
1087
1088 status = flv_read_videodata_properties(p_ctx, track, size - 1);
1089 if(status != VC_CONTAINER_SUCCESS) { vc_container_free_track(p_ctx, track); break; }
1090
1091 module->video_track = p_ctx->tracks_num++;
1092 track->is_enabled = 1;
1093 track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
1094 break;
1095
1096 case FLV_TAG_TYPE_METADATA:
1097 flv_read_metadata(p_ctx, size);
1098 break;
1099
1100 default: break;
1101 }
1102
1103 /* Skip any data that's left unparsed from the current tag */
1104 skip = size - (STREAM_POSITION(p_ctx) - offset);
1105 if(skip < 0) break;
1106 SKIP_BYTES(p_ctx, (size_t)skip);
1107 }
1108
1109 /* Make sure we found something we can play */
1110 if(!p_ctx->tracks_num) {LOG_DEBUG(p_ctx, "didn't find any track"); goto error;}
1111
1112 /* Try and create an index. All times are signed, so adding a base timestamp
1113 * of zero means that we will always seek back to the start of the file, even if
1114 * the actual frame timestamps start at some higher number. */
1115 if(vc_container_index_create(&module->state.index, 512) == VC_CONTAINER_SUCCESS)
1116 vc_container_index_add(module->state.index, 0LL, (int64_t) data_offset);
1117
1118 /* Use the metadata we read */
1119 if(module->audio_track >= 0)
1120 {
1121 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->audio_track];
1122 track->format->bitrate = module->meta_audiodatarate;
1123 }
1124 if(module->video_track >= 0)
1125 {
1126 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->video_track];
1127 track->format->bitrate = module->meta_videodatarate;
1128 if(module->meta_framerate)
1129 {
1130 track->format->type->video.frame_rate_num = (uint32_t)(100 * module->meta_framerate);
1131 track->format->type->video.frame_rate_den = 100;
1132 }
1133
1134 if(module->meta_width && module->meta_width > track->format->type->video.width)
1135 track->format->type->video.width = module->meta_width;
1136 if(module->meta_height && module->meta_height > track->format->type->video.height)
1137 track->format->type->video.height = module->meta_height;
1138 }
1139
1140 status = SEEK(p_ctx, data_offset);
1141 if(status != VC_CONTAINER_SUCCESS) goto error;
1142
1143 /* Some initialisation */
1144 module->state.tag_position = data_offset;
1145 module->state.data_position = data_offset;
1146 for(i = 0; i < p_ctx->tracks_num; i++)
1147 {
1148 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module;
1149 track_module->state = &module->state;
1150 }
1151
1152 if(STREAM_SEEKABLE(p_ctx))
1153 p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
1154
1155 p_ctx->priv->pf_close = flv_reader_close;
1156 p_ctx->priv->pf_read = flv_reader_read;
1157 p_ctx->priv->pf_seek = flv_reader_seek;
1158
1159 return VC_CONTAINER_SUCCESS;
1160
1161 error:
1162 if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1163 LOG_DEBUG(p_ctx, "flv: error opening stream");
1164 if(module) flv_reader_close(p_ctx);
1165 return status;
1166}
1167
1168/********************************************************************************
1169 Entrypoint function
1170 ********************************************************************************/
1171
1172#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
1173# pragma weak reader_open flv_reader_open
1174#endif
1175