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#define CONTAINER_IS_LITTLE_ENDIAN
31//#define ENABLE_CONTAINERS_LOG_FORMAT
32//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
33#define CONTAINER_HELPER_LOG_INDENT(a) 0
34#include "containers/core/containers_private.h"
35#include "containers/core/containers_io_helpers.h"
36#include "containers/core/containers_utils.h"
37#include "containers/core/containers_logging.h"
38#include "containers/core/containers_waveformat.h"
39
40/******************************************************************************
41Defines.
42******************************************************************************/
43#define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */
44#define AVIF_MUSTUSEINDEX 0x00000020
45#define AVIF_TRUSTCKTYPE 0x00000800 /*< (OpenDML) keyframe information reliable. */
46
47#define AVIIF_LIST 0x00000001
48#define AVIIF_KEYFRAME 0x00000010
49#define AVIIF_NOTIME 0x00000100
50
51#define AVI_INDEX_OF_INDEXES 0x00
52#define AVI_INDEX_OF_CHUNKS 0x01
53#define AVI_INDEX_2FIELD 0x01
54#define AVI_INDEX_DELTAFRAME 0x80000000
55
56#define AVI_TRACKS_MAX 16 /*< We won't try to handle streams with more tracks than this */
57
58#define AVI_TWOCC(a,b) ((a) | (b << 8))
59
60#define AVI_SYNC_CHUNK(ctx) \
61 while(STREAM_POSITION(ctx) & 1) \
62 { \
63 if (SKIP_BYTES(ctx, 1) != 1) break; \
64 }
65
66#define AVI_SKIP_CHUNK(ctx, size) \
67 do { \
68 SKIP_BYTES(ctx, size); \
69 AVI_SYNC_CHUNK(ctx); \
70 } while(0)
71
72/******************************************************************************
73Type definitions
74******************************************************************************/
75typedef struct AVI_TRACK_STREAM_STATE_T
76{
77 unsigned current_track_num; /**< Number of track currently being read */
78 int64_t data_offset; /**< Offset within the stream to find the track data */
79 uint32_t chunk_size; /**< Size of the current chunk being read */
80 uint32_t chunk_data_left; /**< Data left from the current chunk being read */
81
82 unsigned extra_chunk_track_num; /**< Temporary storage for in-band data e.g. 'dd'
83 chunks */
84 uint32_t extra_chunk_data[4];
85 uint32_t extra_chunk_data_offs;
86 uint32_t extra_chunk_data_len;
87} AVI_TRACK_STREAM_STATE_T;
88
89typedef struct AVI_TRACK_CHUNK_STATE_T
90{
91 uint64_t index;
92 uint64_t offs; /**< offset into bytestream consisting of all chunks for this track */
93 int64_t time_pos; /**< pts of chunk (if known) */
94 uint32_t flags; /**< flags associated with chunk */
95 AVI_TRACK_STREAM_STATE_T local_state;
96 AVI_TRACK_STREAM_STATE_T *state;
97} AVI_TRACK_CHUNK_STATE_T;
98
99typedef struct VC_CONTAINER_TRACK_MODULE_T
100{
101 int64_t time_start; /**< i.e. 'dwStart' in 'strh' (converted to microseconds) */
102 int64_t duration; /**< i.e. 'dwLength' in 'strh' (converted to microseconds) */
103 uint32_t time_num; /**< i.e. 'dwScale' in 'strh' */
104 uint32_t time_den; /**< i.e. 'dwRate' in 'strh', time_num / time_den =
105 samples (or frames) / second for audio (or video) */
106 uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */
107
108 uint64_t index_offset; /**< Offset to the start of an OpenDML index i.e. 'indx'
109 (if available) */
110 uint32_t index_size; /**< Size of the OpenDML index chunk */
111 AVI_TRACK_CHUNK_STATE_T chunk;
112} VC_CONTAINER_TRACK_MODULE_T;
113
114typedef struct VC_CONTAINER_MODULE_T
115{
116 VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX];
117 uint64_t data_offset; /**< Offset to the start of data packets i.e.
118 the data in the 'movi' list */
119 uint64_t data_size; /**< Size of the chunk containing data packets */
120 uint64_t index_offset; /**< Offset to the start of index data e.g.
121 the data in a 'idx1' list */
122 uint32_t index_size; /**< Size of the chunk containing index data */
123 AVI_TRACK_STREAM_STATE_T state;
124} VC_CONTAINER_MODULE_T;
125
126/******************************************************************************
127Function prototypes
128******************************************************************************/
129VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * );
130
131/******************************************************************************
132Local Functions
133******************************************************************************/
134
135static VC_CONTAINER_STATUS_T avi_find_chunk(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T id, uint32_t *size)
136{
137 VC_CONTAINER_STATUS_T status;
138 VC_CONTAINER_FOURCC_T chunk_id;
139 uint32_t chunk_size;
140
141 do {
142 chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
143 chunk_size = READ_U32(p_ctx, "Chunk size");
144 if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
145
146 if(chunk_id == id)
147 {
148 *size = chunk_size;
149 return VC_CONTAINER_SUCCESS;
150 }
151 /* Not interested in this chunk, skip it. */
152 AVI_SKIP_CHUNK(p_ctx, chunk_size);
153 } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS);
154
155 return status; /* chunk not found */
156}
157
158static VC_CONTAINER_STATUS_T avi_find_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T fourcc, uint32_t *size)
159{
160 VC_CONTAINER_STATUS_T status;
161 VC_CONTAINER_FOURCC_T chunk_id;
162 uint32_t chunk_size;
163 uint32_t peek_buf[1];
164
165 do {
166 chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
167 chunk_size = READ_U32(p_ctx, "Chunk size");
168 if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
169
170 if(chunk_id == VC_FOURCC('L','I','S','T'))
171 {
172 if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4)
173 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
174 if (peek_buf[0] == fourcc)
175 {
176 *size = chunk_size;
177 return VC_CONTAINER_SUCCESS;
178 }
179 }
180 /* Not interested in this chunk, skip it. */
181 AVI_SKIP_CHUNK(p_ctx, chunk_size);
182 } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS);
183
184 return status; /* list not found */
185}
186
187static int64_t avi_stream_ticks_to_us(VC_CONTAINER_TRACK_MODULE_T *track_module, uint64_t ticks)
188{
189 int64_t time;
190 vc_container_assert(track_module->time_den != 0);
191 time = INT64_C(1000000) * track_module->time_num * ticks / track_module->time_den;
192 return time;
193}
194
195static int64_t avi_calculate_chunk_time(VC_CONTAINER_TRACK_MODULE_T *track_module)
196{
197 if (track_module->sample_size == 0)
198 return track_module->time_start + avi_stream_ticks_to_us(track_module, track_module->chunk.index);
199 else
200 return track_module->time_start + avi_stream_ticks_to_us(track_module,
201 ((track_module->chunk.offs + (track_module->sample_size >> 1)) / track_module->sample_size));
202}
203
204static VC_CONTAINER_STATUS_T avi_read_stream_header_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track,
205 VC_CONTAINER_TRACK_MODULE_T *track_module)
206{
207 VC_CONTAINER_STATUS_T status;
208 int64_t list_offset;
209 uint32_t list_size, chunk_id, chunk_size;
210
211 int stream_header_chunk_read = 0, stream_format_chunk_read = 0;
212
213 /* Look for a 'strl' LIST (sub)chunk */
214 status = avi_find_list(p_ctx, VC_FOURCC('s','t','r','l'), &chunk_size);
215 if (status != VC_CONTAINER_SUCCESS)
216 {
217 LOG_DEBUG(p_ctx, "'strl' LIST not found for stream");
218 return VC_CONTAINER_ERROR_FORMAT_INVALID;
219 }
220
221 list_offset = STREAM_POSITION(p_ctx);
222 list_size = chunk_size;
223 SKIP_FOURCC(p_ctx, "strl");
224
225 while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS && STREAM_POSITION(p_ctx) < list_offset + list_size)
226 {
227 int64_t offset = STREAM_POSITION(p_ctx);
228 chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
229 chunk_size = READ_U32(p_ctx, "Chunk size");
230 LOG_FORMAT(p_ctx, "chunk %4.4s, offset: %"PRIi64", size: %i", (char *)&chunk_id, offset, chunk_size);
231
232 if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
233
234 if(chunk_id == VC_FOURCC('s','t','r','h'))
235 {
236 VC_CONTAINER_FOURCC_T fourcc_type, fourcc_handler;
237 uint32_t flags, scale, rate, div, start, length, sample_size;
238
239 /* We won't accept more than one 'strh' per stream */
240 if (stream_header_chunk_read)
241 {
242 LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strh'");
243 return VC_CONTAINER_ERROR_FORMAT_INVALID;
244 }
245
246 fourcc_type = READ_FOURCC(p_ctx, "fccType");
247 fourcc_handler = READ_FOURCC(p_ctx, "fccHandler");
248 flags = READ_U32(p_ctx, "dwFlags");
249 SKIP_U16(p_ctx, "wPriority");
250 SKIP_U16(p_ctx, "wLanguage");
251 SKIP_U32(p_ctx, "dwInitialFrames");
252 scale = READ_U32(p_ctx, "dwScale");
253 rate = READ_U32(p_ctx, "dwRate");
254 start = READ_U32(p_ctx, "dwStart");
255 length = READ_U32(p_ctx, "dwLength");
256 SKIP_U32(p_ctx, "dwSuggestedBufferSize");
257 SKIP_U32(p_ctx, "dwQuality");
258 sample_size = READ_U32(p_ctx, "dwSampleSize");
259 SKIP_U16(p_ctx, "rcFrame.left");
260 SKIP_U16(p_ctx, "rcFrame.top");
261 SKIP_U16(p_ctx, "rcFrame.right");
262 SKIP_U16(p_ctx, "rcFrame.bottom");
263
264 /* In AVI, sec/frame = scale/rate and frames/sec = rate/scale */
265 if (rate == 0)
266 {
267 LOG_DEBUG(p_ctx, "invalid dwRate: 0, using 1 as a guess");
268 LOG_DEBUG(p_ctx, "timestamps will almost certainly be wrong");
269 rate = 1;
270 }
271
272 div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate);
273 scale = scale / div;
274 rate = rate / div;
275
276 track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
277 if(fourcc_type == VC_FOURCC('v','i','d','s'))
278 {
279 track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
280 track->format->type->video.frame_rate_num = rate;
281 track->format->type->video.frame_rate_den = scale;
282
283 if (sample_size != 0)
284 {
285 LOG_DEBUG(p_ctx, "ignoring dwSampleSize (%d) for video stream", sample_size);
286 sample_size = 0;
287 }
288 }
289 else if(fourcc_type == VC_FOURCC('a','u','d','s'))
290 {
291 track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
292 /* VBR audio is going to be non-framed */
293 track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
294 }
295 else if(fourcc_type == VC_FOURCC('t','x','t','s'))
296 track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE;
297
298 /* Don't overwrite any existing value (i.e. in the unlikely case where we
299 see 'strf' before 'strh') */
300 if(!track->format->codec) track->format->codec = vfw_fourcc_to_codec(fourcc_handler);
301
302 /* FIXME: enable this once read_media does the right thing */
303 if (!(flags & AVISF_DISABLED) || 1)
304 track->is_enabled = 1;
305
306 track_module->time_num = scale;
307 track_module->time_den = rate;
308 track_module->time_start = avi_stream_ticks_to_us(track_module, (uint64_t)start);
309 track_module->duration = avi_stream_ticks_to_us(track_module, (uint64_t)length);
310 track_module->sample_size = sample_size;
311
312 p_ctx->duration = MAX(p_ctx->duration, track_module->duration);
313
314 stream_header_chunk_read = 1;
315 }
316 else if(chunk_id == VC_FOURCC('s','t','r','f'))
317 {
318 uint8_t *buffer;
319 unsigned extra_offset = 0, extra_size = 0;
320
321 /* We won't accept more than one 'strf' per stream */
322 if (stream_format_chunk_read)
323 {
324 LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strf'");
325 return VC_CONTAINER_ERROR_FORMAT_INVALID;
326 }
327
328 /* Use the extradata buffer for reading in the entire 'strf' (should not be a large chunk) */
329 if ((status = vc_container_track_allocate_extradata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS)
330 {
331 LOG_DEBUG(p_ctx, "failed to allocate memory for 'strf' (%d bytes)", chunk_size);
332 return status;
333 }
334
335 buffer = track->priv->extradata;
336 if(READ_BYTES(p_ctx, buffer, chunk_size) != chunk_size)
337 return VC_CONTAINER_ERROR_FORMAT_INVALID;
338 AVI_SYNC_CHUNK(p_ctx);
339
340 if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
341 {
342 status = vc_container_bitmapinfoheader_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format);
343 }
344 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
345 {
346 status = vc_container_waveformatex_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format);
347 if (track_module->sample_size != 0 && track_module->sample_size != track->format->type->audio.block_align)
348 {
349 LOG_DEBUG(p_ctx, "invalid dwSampleSize (%d), should match nBlockAlign (%d) for audio streams.",
350 track_module->sample_size, track->format->type->audio.block_align);
351
352 /* Note that if nBlockAlign really is 0, strf is seriously broken... */
353 if (track->format->type->audio.block_align != 0)
354 track_module->sample_size = track->format->type->audio.block_align;
355 }
356 else
357 {
358 /* Flawed muxers might only set nBlockAlign (i.e. not set dwSampleSize correctly). */
359 if (track->format->type->audio.block_align == 1)
360 track_module->sample_size = 1;
361 }
362 }
363
364 if (status != VC_CONTAINER_SUCCESS) return status;
365
366 if (extra_size)
367 {
368 track->format->extradata = buffer + extra_offset;
369 track->format->extradata_size = extra_size;
370 }
371
372 /* Codec specific fix-up */
373 if (track->format->codec == VC_CONTAINER_CODEC_MP4A &&
374 track->format->extradata_size)
375 {
376 /* This is going to be raw AAC so it will be framed */
377 track_module->sample_size = 0;
378 track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
379 }
380
381 /* WMA specific fix-up */
382 if ((track->format->codec == VC_CONTAINER_CODEC_WMA1 ||
383 track->format->codec == VC_CONTAINER_CODEC_WMA2 ||
384 track->format->codec == VC_CONTAINER_CODEC_WMAP ||
385 track->format->codec == VC_CONTAINER_CODEC_WMAL ||
386 track->format->codec == VC_CONTAINER_CODEC_WMAV) &&
387 track->format->extradata_size)
388 {
389 track_module->sample_size = 0;
390 track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
391 }
392
393 stream_format_chunk_read = 1;
394 }
395 else if(chunk_id == VC_FOURCC('s','t','r','d'))
396 {
397 /* The data in a 'strd' chunk is either codec configuration data or DRM information,
398 we can safely assume it might be either as long as we don't overwrite any config
399 data read previously from a 'strf' chunk */
400 if ((status = vc_container_track_allocate_drmdata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS)
401 {
402 LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size);
403 return status;
404 }
405
406 if(READ_BYTES(p_ctx, track->priv->drmdata, chunk_size) != chunk_size)
407 return VC_CONTAINER_ERROR_FORMAT_INVALID;
408 AVI_SYNC_CHUNK(p_ctx);
409
410 if (!track->format->extradata)
411 {
412 if (vc_container_track_allocate_extradata(p_ctx, track, chunk_size) == VC_CONTAINER_SUCCESS)
413 {
414 memcpy(track->format->extradata, track->priv->drmdata, chunk_size);
415
416 track->format->extradata = track->priv->extradata;
417 track->format->extradata_size = chunk_size;
418 }
419 else
420 {
421 LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size);
422 LOG_DEBUG(p_ctx, "no codec configuration data set");
423 }
424 }
425 }
426 else if(chunk_id == VC_FOURCC('i','n','d','x'))
427 {
428 track_module->index_offset = STREAM_POSITION(p_ctx);
429 track_module->index_size = chunk_size;
430 }
431 else
432 {
433 /* Not interested in this chunk, skip it. */
434 }
435
436 /* Skip any left-over data */
437 AVI_SKIP_CHUNK(p_ctx, offset + chunk_size + 8 - STREAM_POSITION(p_ctx) );
438 }
439
440 if (!stream_header_chunk_read || !stream_format_chunk_read)
441 {
442 LOG_DEBUG(p_ctx, "invalid 'strl', 'strh' and 'strf' are both required");
443 return VC_CONTAINER_ERROR_FORMAT_INVALID;
444 }
445
446 return status;
447}
448
449static VC_CONTAINER_STATUS_T avi_find_next_data_chunk(VC_CONTAINER_T *p_ctx, uint32_t *id, uint32_t *size)
450{
451 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
452 VC_CONTAINER_FOURCC_T chunk_id;
453 uint32_t chunk_size = 0;
454 uint32_t peek_buf[1];
455
456 do
457 {
458 chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
459 chunk_size = READ_U32(p_ctx, "Chunk size");
460 if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
461 break;
462
463 /* Check if this is a 'rec ' or a 'movi' LIST instead of a plain data chunk */
464 if(chunk_id == VC_FOURCC('L','I','S','T'))
465 {
466 if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4)
467 return VC_CONTAINER_ERROR_EOS;
468 if (peek_buf[0] == VC_FOURCC('r','e','c',' '))
469 SKIP_FOURCC(p_ctx, "rec ");
470 else if (peek_buf[0] == VC_FOURCC('m','o','v','i'))
471 SKIP_FOURCC(p_ctx, "movi");
472 else
473 AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this LIST chunk, skip it. */
474 continue;
475 }
476
477 /* Check if this is a 'AVIX' RIFF header instead of a data chunk */
478 if(chunk_id == VC_FOURCC('R','I','F','F'))
479 {
480 if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4)
481 return VC_CONTAINER_ERROR_EOS;
482 if (peek_buf[0] == VC_FOURCC('A','V','I','X'))
483 SKIP_FOURCC(p_ctx, "AVIX");
484 else
485 AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this RIFF header, skip it. */
486 continue;
487 }
488
489 /* We treat only db/dc/dd or wb chunks as data */
490 if((uint32_t)chunk_id >> 16 == AVI_TWOCC('d','c') ||
491 (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','b') ||
492 (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','d') ||
493 (uint32_t)chunk_id >> 16 == AVI_TWOCC('w','b'))
494 {
495 *id = chunk_id;
496 *size = chunk_size;
497 break;
498 }
499
500 /* Need to exit if a zero sized chunk encountered so we don't loop forever. */
501 if(chunk_size == 0 && chunk_id == 0) return VC_CONTAINER_ERROR_EOS;
502
503 /* Not interested in this chunk, skip it */
504 AVI_SKIP_CHUNK(p_ctx, chunk_size);
505 } while ((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS);
506
507 return status;
508}
509
510static void avi_track_from_chunk_id(VC_CONTAINER_FOURCC_T chunk_id, uint16_t *data_type, uint16_t *track_num)
511{
512 *track_num = (((uint8_t*)&chunk_id)[0] - 48) * 10 + ((uint8_t*)&chunk_id)[1] - 48;
513 *data_type = (uint32_t)chunk_id >> 16;
514}
515
516static VC_CONTAINER_STATUS_T avi_check_track(VC_CONTAINER_T *p_ctx, uint16_t data_type, uint16_t track_num)
517{
518 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
519
520 if (track_num < p_ctx->tracks_num)
521 {
522 if (data_type == AVI_TWOCC('w','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_AUDIO)
523 {
524 LOG_DEBUG(p_ctx, "suspicious track type ('wb'), track %d is not an audio track", track_num);
525 status = VC_CONTAINER_ERROR_FAILED;
526 }
527 if (data_type == AVI_TWOCC('d','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
528 {
529 LOG_DEBUG(p_ctx, "suspicious track type ('db'), track %d is not a video track", track_num);
530 status = VC_CONTAINER_ERROR_FAILED;
531 }
532 if (data_type == AVI_TWOCC('d','c') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
533 {
534 LOG_DEBUG(p_ctx, "suspicious track type ('dc'), track %d is not a video track", track_num);
535 status = VC_CONTAINER_ERROR_FAILED;
536 }
537 if (data_type == AVI_TWOCC('d','d') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
538 {
539 LOG_DEBUG(p_ctx, "suspicious track type ('dd'), track %d is not a video track", track_num);
540 status = VC_CONTAINER_ERROR_FAILED;
541 }
542 }
543 else
544 {
545 LOG_DEBUG(p_ctx, "invalid track number %d (no more than %d tracks expected)",
546 track_num, p_ctx->tracks_num);
547 status = VC_CONTAINER_ERROR_FAILED;
548 }
549
550 return status;
551}
552
553static int avi_compare_seek_time(int64_t chunk_time, int64_t seek_time,
554 int chunk_is_keyframe, VC_CONTAINER_SEEK_FLAGS_T seek_flags)
555{
556 if (chunk_time == seek_time && chunk_is_keyframe && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
557 return 0;
558
559 if (chunk_time > seek_time && chunk_is_keyframe && (seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
560 return 0;
561
562 if (chunk_time > seek_time && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
563 return 1; /* Chunk time is past seek time, caller should use the previous keyframe */
564
565 return -1;
566}
567
568static VC_CONTAINER_STATUS_T avi_scan_legacy_index_chunk(VC_CONTAINER_T *p_ctx, int seek_track_num,
569 int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos)
570{
571 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
572 VC_CONTAINER_STATUS_T status;
573 VC_CONTAINER_TRACK_MODULE_T *track_module;
574 AVI_TRACK_CHUNK_STATE_T selected_chunk;
575 int64_t base_offset = module->data_offset;
576 int64_t selected_chunk_offset = base_offset + 4;
577 int32_t extra_offset = 0;
578 int first_chunk_offset = 1;
579 uint64_t position;
580
581 SEEK(p_ctx, module->index_offset);
582 memset(&selected_chunk, 0, sizeof(selected_chunk));
583
584 while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS &&
585 (uint64_t)STREAM_POSITION(p_ctx) < module->index_offset + module->index_size)
586 {
587 VC_CONTAINER_FOURCC_T chunk_id;
588 uint16_t data_type, track_num;
589 uint32_t chunk_flags, offset, size;
590
591 chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
592 chunk_flags = READ_U32(p_ctx, "dwFlags");
593 offset = READ_U32(p_ctx, "dwOffset");
594 size = READ_U32(p_ctx, "dwSize");
595
596 if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) break;
597
598 /* Although it's rare, the offsets might be given from the start of the file
599 instead of the data chunk, we have to handle both cases. */
600 if (first_chunk_offset)
601 {
602 if (offset > module->data_offset) base_offset = INT64_C(0);
603 selected_chunk_offset = base_offset + 4;
604 first_chunk_offset = 0;
605 }
606
607 avi_track_from_chunk_id(chunk_id, &data_type, &track_num);
608 LOG_DEBUG(p_ctx, "reading track %"PRIu16, track_num);
609
610 if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS)
611 {
612 LOG_DEBUG(p_ctx, "skipping index entry for track %d/%d", track_num, p_ctx->tracks_num);
613 continue;
614 }
615
616 track_module = p_ctx->tracks[track_num]->priv->module;
617
618 if (data_type == AVI_TWOCC('d','d'))
619 {
620 if (track_num == seek_track_num)
621 track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED;
622 extra_offset = -(size + 8);
623 }
624
625 /* If this entry does not affect timing, skip it */
626 if ((chunk_flags & (AVIIF_LIST | AVIIF_NOTIME)) || data_type == AVI_TWOCC('d','d'))
627 continue;
628
629 position = base_offset + offset + extra_offset;
630 extra_offset = INT64_C(0);
631
632 /* Check validity of position */
633 if (position <= module->data_offset /* || (*pos > module->data_offset + module->data_size*/)
634 return VC_CONTAINER_ERROR_FORMAT_INVALID;
635
636 if (track_num == seek_track_num)
637 {
638 bool is_keyframe = true;
639 int res;
640
641 if (p_ctx->tracks[track_num]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
642 is_keyframe = chunk_flags & AVIIF_KEYFRAME;
643
644 if (is_keyframe)
645 track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
646 else
647 track_module->chunk.flags &= ~(VC_CONTAINER_PACKET_FLAG_KEYFRAME);
648
649 res = avi_compare_seek_time(track_module->chunk.time_pos, *time, is_keyframe, flags);
650 if (res > 0)
651 break; /* We've found the keyframe we wanted */
652
653 if (is_keyframe)
654 {
655 selected_chunk_offset = position;
656 selected_chunk = track_module->chunk;
657 }
658
659 if (res == 0)
660 break; /* We've found the keyframe we wanted */
661
662 track_module->chunk.index++;
663 track_module->chunk.offs += size;
664 track_module->chunk.time_pos = avi_calculate_chunk_time(track_module);
665
666 LOG_DEBUG(p_ctx, "index %"PRIu64", offs %"PRIu64", time %"PRIi64"us", track_module->chunk.index,
667 track_module->chunk.offs, track_module->chunk.time_pos);
668 }
669 }
670
671 if (status == VC_CONTAINER_SUCCESS ||
672 /* When seeking backwards, always return the last good position */
673 !(flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
674 {
675 *pos = selected_chunk_offset;
676 track_module = p_ctx->tracks[seek_track_num]->priv->module;
677 track_module->chunk.index = selected_chunk.index;
678 track_module->chunk.offs = selected_chunk.offs;
679 track_module->chunk.flags = selected_chunk.flags;
680 track_module->chunk.time_pos = selected_chunk.time_pos;
681 *time = track_module->chunk.time_pos;
682 return VC_CONTAINER_SUCCESS;
683 }
684
685 return VC_CONTAINER_ERROR_NOT_FOUND;
686}
687
688static VC_CONTAINER_STATUS_T avi_scan_standard_index_chunk(VC_CONTAINER_T *p_ctx, uint64_t index_offset,
689 unsigned seek_track_num, int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos)
690{
691 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND;
692 VC_CONTAINER_TRACK_MODULE_T *track_module = NULL;
693 VC_CONTAINER_FOURCC_T chunk_id;
694 uint32_t chunk_size;
695 uint16_t data_type, track_num;
696 uint8_t index_type, index_sub_type;
697 uint32_t entry, entry_count = 0;
698 uint16_t entry_size;
699 uint64_t base_offset = UINT64_C(0);
700 uint64_t position = UINT64_C(0);
701 uint64_t prev_keyframe_offs = INT64_C(0);
702 AVI_TRACK_CHUNK_STATE_T prev_keyframe_chunk = { 0 };
703
704 SEEK(p_ctx, index_offset);
705
706 chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
707 chunk_size = READ_U32(p_ctx, "Chunk Size");
708
709 entry_size = READ_U16(p_ctx, "wLongsPerEntry");
710 index_sub_type = READ_U8(p_ctx, "bIndexSubType");
711 index_type = READ_U8(p_ctx, "bIndexType");
712 entry_count = READ_U32(p_ctx, "nEntriesInUse");
713 chunk_id = READ_FOURCC(p_ctx, "dwChunkId");
714 base_offset = READ_U64(p_ctx, "qwBaseOffset");
715 SKIP_U32(p_ctx, "dwReserved");
716
717 if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
718 return status;
719
720 avi_track_from_chunk_id(chunk_id, &data_type, &track_num);
721 status = avi_check_track(p_ctx, data_type, track_num);
722 if (status || chunk_size < 24 || track_num != seek_track_num)
723 return VC_CONTAINER_ERROR_FORMAT_INVALID;
724
725 if (entry_size != 2 || index_sub_type != 0 || index_type != AVI_INDEX_OF_CHUNKS)
726 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
727
728 entry_count = MIN(entry_count, (chunk_size - 24) / (entry_size * 4));
729
730 track_module = p_ctx->tracks[seek_track_num]->priv->module;
731
732 for (entry = 0; entry < entry_count; ++entry)
733 {
734 uint32_t chunk_offset;
735 int key_frame = 0;
736
737 chunk_offset = READ_U32(p_ctx, "dwOffset");
738 chunk_size = READ_U32(p_ctx, "dwSize");
739
740 if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
741 break;
742
743 status = VC_CONTAINER_ERROR_NOT_FOUND;
744
745 if (!(chunk_size & AVI_INDEX_DELTAFRAME))
746 key_frame = 1;
747 chunk_size &= ~AVI_INDEX_DELTAFRAME;
748
749 position = base_offset + chunk_offset - 8;
750
751 if (key_frame)
752 track_module->chunk.flags = VC_CONTAINER_PACKET_FLAG_KEYFRAME;
753 else
754 track_module->chunk.flags = 0;
755
756 if (time != NULL)
757 {
758 int res;
759 status = VC_CONTAINER_ERROR_NOT_FOUND;
760 res = avi_compare_seek_time(track_module->chunk.time_pos, *time, key_frame, flags);
761
762 if (res == 0)
763 {
764 *pos = position;
765 *time = track_module->chunk.time_pos;
766 status = VC_CONTAINER_SUCCESS;
767 break;
768 }
769 else if (res > 0)
770 {
771 if (prev_keyframe_offs)
772 {
773 *pos = prev_keyframe_offs;
774 track_module->chunk = prev_keyframe_chunk;
775 *time = track_module->chunk.time_pos;
776 status = VC_CONTAINER_SUCCESS;
777 }
778 break;
779 }
780
781 if (key_frame)
782 {
783 prev_keyframe_offs = position;
784 prev_keyframe_chunk = track_module->chunk;
785 }
786 }
787 else
788 {
789 /* Not seeking to a time position, but scanning
790 track chunk state up to a certain file position
791 instead */
792 if (position >= *pos)
793 {
794 status = VC_CONTAINER_SUCCESS;
795 break;
796 }
797 }
798
799 track_module->chunk.index++;
800 track_module->chunk.offs += chunk_size;
801 track_module->chunk.time_pos = avi_calculate_chunk_time(track_module);
802 }
803
804 return status;
805}
806
807static VC_CONTAINER_STATUS_T avi_scan_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned index_track_num,
808 int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos)
809{
810 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND;
811 VC_CONTAINER_FOURCC_T chunk_id;
812 uint64_t index_offset;
813 uint32_t index_size;
814 uint16_t data_type, track_num;
815 uint32_t entry, entry_count;
816 uint16_t entry_size;
817 uint8_t index_sub_type, index_type;
818
819 index_offset = p_ctx->tracks[index_track_num]->priv->module->index_offset;
820 index_size = p_ctx->tracks[index_track_num]->priv->module->index_size;
821
822 SEEK(p_ctx, index_offset);
823
824 entry_size = READ_U16(p_ctx, "wLongsPerEntry");
825 index_sub_type = READ_U8(p_ctx, "bIndexSubType");
826 index_type = READ_U8(p_ctx, "bIndexType");
827 entry_count = READ_U32(p_ctx, "nEntriesInUse");
828 chunk_id = READ_FOURCC(p_ctx, "dwChunkId");
829 SKIP_U32(p_ctx, "dwReserved0");
830 SKIP_U32(p_ctx, "dwReserved1");
831 SKIP_U32(p_ctx, "dwReserved2");
832
833 if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
834 return status;
835
836 if (index_type == AVI_INDEX_OF_INDEXES)
837 {
838 avi_track_from_chunk_id(chunk_id, &data_type, &track_num);
839 status = avi_check_track(p_ctx, data_type, track_num);
840 if (status || index_size < 24 || track_num != index_track_num) return VC_CONTAINER_ERROR_FORMAT_INVALID;
841
842 /* FIXME: We should probably support AVI_INDEX_2FIELD as well */
843 if (entry_size != 4 || index_sub_type != 0)
844 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
845
846 entry_count = MIN(entry_count, (index_size - 24) / entry_size);
847
848 for (entry = 0; entry < entry_count; ++entry)
849 {
850 uint64_t entry_offset, standard_index_offset;
851 standard_index_offset = READ_U64(p_ctx, "qwOffset");
852 SKIP_U32(p_ctx, "dwSize");
853 SKIP_U32(p_ctx, "dwDuration");
854
855 if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
856 break;
857
858 if (standard_index_offset == UINT64_C(0))
859 {
860 status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Not plausible */
861 break;
862 }
863
864 entry_offset = STREAM_POSITION(p_ctx);
865 status = avi_scan_standard_index_chunk(p_ctx, standard_index_offset, index_track_num, time, flags, pos);
866 if (status != VC_CONTAINER_ERROR_NOT_FOUND) break;
867 SEEK(p_ctx, entry_offset); /* Move to next entry ('ix' chunk); */
868 }
869 }
870 else if (index_type == AVI_INDEX_OF_CHUNKS)
871 {
872 /* It seems we are dealing with a standard index instead... */
873 status = avi_scan_standard_index_chunk(p_ctx, index_offset, index_track_num, time, flags, pos);
874 }
875 else
876 {
877 status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
878 }
879
880 return status;
881}
882
883static VC_CONTAINER_STATUS_T avi_read_dd_chunk( VC_CONTAINER_T *p_ctx,
884 AVI_TRACK_STREAM_STATE_T *p_state, uint16_t data_type, uint32_t chunk_size,
885 uint16_t track_num )
886{
887 if (data_type == AVI_TWOCC('d','d'))
888 {
889 if (p_state->extra_chunk_data_len ||
890 chunk_size > sizeof(p_state->extra_chunk_data))
891 {
892 LOG_DEBUG(p_ctx, "cannot handle multiple consecutive 'dd' chunks");
893 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
894 }
895 if(READ_BYTES(p_ctx, p_state->extra_chunk_data, chunk_size) != chunk_size)
896 return VC_CONTAINER_ERROR_FORMAT_INVALID;
897
898 AVI_SYNC_CHUNK(p_ctx);
899 p_state->extra_chunk_track_num = track_num;
900 p_state->extra_chunk_data_len = chunk_size;
901 p_state->extra_chunk_data_offs = 0;
902
903 return VC_CONTAINER_ERROR_CONTINUE;
904 }
905 else if (p_state->extra_chunk_data_len &&
906 p_state->extra_chunk_track_num != track_num)
907 {
908 LOG_DEBUG(p_ctx, "dropping data from '%02ddd' chunk, not for this track (%d)",
909 p_state->extra_chunk_track_num, track_num);
910 p_state->extra_chunk_data_len = 0;
911 }
912
913 return VC_CONTAINER_SUCCESS;
914}
915
916/*****************************************************************************
917Functions exported as part of the Container Module API
918 *****************************************************************************/
919
920static VC_CONTAINER_STATUS_T avi_reader_read( VC_CONTAINER_T *p_ctx,
921 VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
922{
923 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
924 VC_CONTAINER_TRACK_MODULE_T *track_module = NULL;
925 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
926 AVI_TRACK_STREAM_STATE_T *p_state = &module->state;
927
928 if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
929 {
930 p_state = p_ctx->tracks[p_packet->track]->priv->module->chunk.state;
931 }
932
933 LOG_DEBUG(p_ctx, "seeking to %"PRIi64, p_state->data_offset);
934 SEEK(p_ctx, p_state->data_offset);
935
936 if (p_state->chunk_data_left == 0)
937 {
938 VC_CONTAINER_FOURCC_T chunk_id;
939 uint32_t chunk_size;
940 uint16_t data_type, track_num;
941
942 if ((status = avi_find_next_data_chunk(p_ctx, &chunk_id, &chunk_size)) != VC_CONTAINER_SUCCESS)
943 {
944 LOG_DEBUG(p_ctx, "unable to find the next data chunk %d", status);
945 p_state->data_offset = STREAM_POSITION(p_ctx);
946 return status;
947 }
948
949 avi_track_from_chunk_id(chunk_id, &data_type, &track_num);
950
951 if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS)
952 {
953 AVI_SKIP_CHUNK(p_ctx, chunk_size);
954 LOG_DEBUG(p_ctx, "skipping data for track %d/%d", track_num, p_ctx->tracks_num);
955
956 p_state->data_offset = STREAM_POSITION(p_ctx);
957 return VC_CONTAINER_ERROR_CONTINUE;
958 }
959
960 /* If we are reading from the global state (i.e. normal read or forced
961 read from the track on the global state), and the track we found is
962 not on the global state, connect the two */
963 if (p_state == &module->state &&
964 p_ctx->tracks[track_num]->priv->module->chunk.state != &module->state)
965 {
966 int64_t next_chunk;
967
968 /* The track's offset is past the current position, skip it as we are
969 not interested in track data from before the track's offset. If we
970 were to read it we would return the same data multiple times. */
971 next_chunk = (STREAM_POSITION(p_ctx) + chunk_size + 1) & ~1;
972 if (p_ctx->tracks[track_num]->priv->module->chunk.state->data_offset > next_chunk)
973 {
974 AVI_SKIP_CHUNK(p_ctx, chunk_size);
975 LOG_DEBUG(p_ctx, "skipping track %d/%d as we have already read it", track_num, p_ctx->tracks_num);
976 p_state->data_offset = STREAM_POSITION(p_ctx);
977 return VC_CONTAINER_ERROR_CONTINUE;
978 }
979
980 /* The track state must be pointing to the current chunk. We need to
981 reconnect the track to the global state. */
982 LOG_DEBUG(p_ctx, "reconnect track %u to the global state", track_num);
983
984 p_ctx->tracks[track_num]->priv->module->chunk.state = &module->state;
985
986 module->state = p_ctx->tracks[track_num]->priv->module->chunk.local_state;
987
988 vc_container_assert(chunk_size >= p_state->chunk_data_left);
989 vc_container_assert(!p_state->chunk_data_left ||
990 ((p_state->data_offset + p_state->chunk_data_left + 1) & ~1) == next_chunk);
991 vc_container_assert(p_state->current_track_num == track_num);
992
993 return VC_CONTAINER_ERROR_CONTINUE;
994 }
995
996 /* If we are not forcing, or if we are and found the track we are
997 interested in, check for dd data and set the track module for the later code */
998 if (!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) ||
999 (track_num == p_packet->track))
1000 {
1001 if ((status = avi_read_dd_chunk(p_ctx, p_state, data_type, chunk_size, track_num)) != VC_CONTAINER_SUCCESS)
1002 {
1003 p_state->data_offset = STREAM_POSITION(p_ctx);
1004 return status;
1005 }
1006 }
1007
1008 p_state->chunk_size = p_state->chunk_data_left = chunk_size;
1009 p_state->current_track_num = track_num;
1010 }
1011
1012 /* If there is data from another track skip past it */
1013 if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK &&
1014 p_state->current_track_num != p_packet->track)
1015 {
1016 p_state->data_offset = STREAM_POSITION(p_ctx);
1017
1018 AVI_SKIP_CHUNK(p_ctx, p_state->chunk_data_left);
1019 LOG_DEBUG(p_ctx, "skipping track %d/%d as we are ignoring it",
1020 p_state->current_track_num, p_ctx->tracks_num);
1021
1022 track_module = p_ctx->tracks[p_packet->track]->priv->module;
1023
1024 /* Handle disconnection from global state */
1025 if (p_state == &module->state &&
1026 p_ctx->tracks[p_state->current_track_num]->priv->module->chunk.state == &module->state)
1027 {
1028 /* Make a copy of the global state */
1029 LOG_DEBUG(p_ctx, "using local state on track %d", p_packet->track);
1030 track_module->chunk.local_state = module->state;
1031 track_module->chunk.state = &track_module->chunk.local_state;
1032 }
1033
1034 track_module->chunk.state->data_offset = STREAM_POSITION(p_ctx);
1035 track_module->chunk.state->chunk_data_left = 0;
1036
1037 return VC_CONTAINER_ERROR_CONTINUE;
1038 }
1039
1040 track_module = p_ctx->tracks[p_state->current_track_num]->priv->module;
1041
1042 if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
1043 {
1044 vc_container_assert(p_state->current_track_num == p_packet->track);
1045 }
1046
1047 LOG_DEBUG(p_ctx, "reading track %u chunk at time %"PRIi64"us, offset %"PRIu64,
1048 p_state->current_track_num, track_module->chunk.time_pos,
1049 track_module->chunk.offs);
1050 if (p_state->extra_chunk_data_len)
1051 track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED;
1052 else
1053 track_module->chunk.flags &= ~VC_CONTAINER_PACKET_FLAG_ENCRYPTED;
1054
1055 if (p_packet)
1056 {
1057 p_packet->track = p_state->current_track_num;
1058 p_packet->size = p_state->chunk_data_left +
1059 p_state->extra_chunk_data_len;
1060 p_packet->flags = track_module->chunk.flags;
1061
1062 if (p_state->chunk_data_left == p_state->chunk_size)
1063 {
1064 p_packet->pts = track_module->chunk.time_pos;
1065 if (track_module->sample_size == 0)
1066 p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME;
1067 }
1068 else
1069 {
1070 p_packet->pts = VC_CONTAINER_TIME_UNKNOWN;
1071 if (track_module->sample_size == 0)
1072 p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
1073 }
1074
1075 p_packet->dts = VC_CONTAINER_TIME_UNKNOWN;
1076 }
1077
1078 if (flags & VC_CONTAINER_READ_FLAG_SKIP)
1079 {
1080 SKIP_BYTES(p_ctx, p_state->chunk_data_left);
1081 AVI_SYNC_CHUNK(p_ctx);
1082 p_state->chunk_data_left = 0;
1083 p_state->extra_chunk_data_len = 0;
1084 }
1085
1086 if (flags & VC_CONTAINER_READ_FLAG_INFO)
1087 {
1088 p_state->data_offset = STREAM_POSITION(p_ctx);
1089
1090 LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset);
1091
1092 return VC_CONTAINER_SUCCESS;
1093 }
1094
1095 if (p_packet)
1096 {
1097 uint8_t *data = p_packet->data;
1098 uint32_t buffer_size = p_packet->buffer_size;
1099 uint32_t size = 0;
1100 uint32_t len;
1101
1102 /* See if we need to insert extra data */
1103 if (p_state->extra_chunk_data_len)
1104 {
1105 len = MIN(buffer_size, p_state->extra_chunk_data_len);
1106 memcpy(data, p_state->extra_chunk_data + p_state->extra_chunk_data_offs, len);
1107 data += len;
1108 buffer_size -= len;
1109 size = len;
1110 p_state->extra_chunk_data_len -= len;
1111 p_state->extra_chunk_data_offs += len;
1112 }
1113
1114 /* Now try to read data into buffer */
1115 len = MIN(buffer_size, p_state->chunk_data_left);
1116 READ_BYTES(p_ctx, data, len);
1117 size += len;
1118 p_state->chunk_data_left -= len;
1119 p_packet->size = size;
1120
1121 if (p_state->chunk_data_left)
1122 p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
1123 }
1124
1125 if (p_state->chunk_data_left == 0)
1126 {
1127 AVI_SYNC_CHUNK(p_ctx);
1128 track_module->chunk.index++;
1129 track_module->chunk.offs += p_state->chunk_size;
1130 track_module->chunk.flags = 0;
1131 track_module->chunk.time_pos = avi_calculate_chunk_time(track_module);
1132 }
1133
1134 /* Update the track's position */
1135 p_state->data_offset = STREAM_POSITION(p_ctx);
1136
1137 LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset);
1138
1139 return STREAM_STATUS(p_ctx);
1140}
1141
1142/*****************************************************************************/
1143static VC_CONTAINER_STATUS_T avi_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset,
1144 VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
1145{
1146 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1147 VC_CONTAINER_STATUS_T status;
1148 uint64_t position, pos;
1149 AVI_TRACK_CHUNK_STATE_T chunk_state[AVI_TRACKS_MAX];
1150 AVI_TRACK_STREAM_STATE_T global_state;
1151 unsigned seek_track_num, i;
1152
1153 if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx))
1154 return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1155
1156 LOG_DEBUG(p_ctx, "AVI seeking to %"PRIi64"us", *p_offset);
1157
1158 /* Save current position and chunk state so we can restore it if we
1159 hit an error whilst scanning index data */
1160 position = STREAM_POSITION(p_ctx);
1161 for(i = 0; i < p_ctx->tracks_num; i++)
1162 chunk_state[i] = p_ctx->tracks[i]->priv->module->chunk;
1163 global_state = p_ctx->priv->module->state;
1164
1165 /* Clear track state affected by a seek operation of any kind */
1166 for(i = 0; i < p_ctx->tracks_num; i++)
1167 {
1168 p_ctx->tracks[i]->priv->module->chunk.index = INT64_C(0);
1169 p_ctx->tracks[i]->priv->module->chunk.offs = INT64_C(0);
1170 p_ctx->tracks[i]->priv->module->chunk.flags = 0;
1171 p_ctx->tracks[i]->priv->module->chunk.time_pos = p_ctx->tracks[i]->priv->module->time_start;
1172 p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->tracks[i]->priv->module->chunk.local_state;
1173 p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_data_left = UINT64_C(0);
1174 p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_size = UINT64_C(0);
1175 p_ctx->tracks[i]->priv->module->chunk.local_state.extra_chunk_data_len = 0;
1176 p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = module->data_offset + 4;
1177 }
1178
1179 /* Clear the global state */
1180 p_ctx->priv->module->state.chunk_data_left = UINT64_C(0);
1181 p_ctx->priv->module->state.chunk_size = UINT64_C(0);
1182 p_ctx->priv->module->state.extra_chunk_data_len = 0;
1183 p_ctx->priv->module->state.data_offset = module->data_offset + 4;
1184
1185 /* Choose track to use for seeking, favor video tracks and tracks
1186 that are enabled */
1187 for(i = 0; i < p_ctx->tracks_num; i++)
1188 if(p_ctx->tracks[i]->is_enabled &&
1189 p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break;
1190 if(i == p_ctx->tracks_num)
1191 for(i = 0; i < p_ctx->tracks_num; i++)
1192 if(p_ctx->tracks[i]->is_enabled) break;
1193 if(i == p_ctx->tracks_num) i = 0;
1194
1195 LOG_DEBUG(p_ctx, "seek on track %d/%d", i, p_ctx->tracks_num);
1196 seek_track_num = i;
1197
1198 if (p_ctx->tracks[seek_track_num]->priv->module->index_offset)
1199 {
1200 LOG_DEBUG(p_ctx, "seeking using the super index");
1201 status = avi_scan_super_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos);
1202 if (status != VC_CONTAINER_SUCCESS) goto error;
1203
1204 /* As AVI chunks don't convey timestamp information, we need to scan all tracks
1205 to the seek file position */
1206 for(i = 0; i < p_ctx->tracks_num; i++)
1207 {
1208 if (p_ctx->tracks[i]->priv->module->index_offset && i != seek_track_num)
1209 {
1210 uint64_t track_pos;
1211 int64_t track_time = *p_offset;
1212
1213 status = avi_scan_super_index_chunk(p_ctx, i, &track_time, flags, &track_pos);
1214 if (status != VC_CONTAINER_SUCCESS) goto error;
1215 p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos;
1216 }
1217 }
1218 }
1219 else
1220 {
1221 LOG_DEBUG(p_ctx, "seeking using the legacy index");
1222
1223 /* The legacy index comes after data so it might not have been available at the
1224 time the container was opened; if this is the case, see if we can find an index
1225 now, if we can't, then there's no way we can proceed with the seek. */
1226 if(!module->index_offset)
1227 {
1228 uint32_t chunk_size;
1229
1230 LOG_DEBUG(p_ctx, "no index offset, searching for one");
1231
1232 /* Locate data chunk and skip it */
1233 SEEK(p_ctx, module->data_offset);
1234 AVI_SKIP_CHUNK(p_ctx, module->data_size);
1235 /* Now search for the index */
1236 status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size);
1237 if (status == VC_CONTAINER_SUCCESS)
1238 {
1239 /* Store offset to index data */
1240 module->index_offset = STREAM_POSITION(p_ctx);
1241 module->index_size = chunk_size;
1242 p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX;
1243 p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG;
1244 }
1245 }
1246 /* Check again, we may or may not have an index */
1247 if (!module->index_offset)
1248 {
1249 /* If there is no index and we are seeking to 0 we can assume the
1250 correct location is the start of the data. Otherwise we are unable
1251 to seek to a specified non-zero location without an index */
1252 if (*p_offset != INT64_C(0))
1253 {
1254 LOG_DEBUG(p_ctx, "failed to find the legacy index, unable to seek");
1255 status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1256 goto error;
1257 }
1258 pos = module->data_offset;
1259 }
1260 else
1261 {
1262 LOG_DEBUG(p_ctx, "scanning the legacy index chunk");
1263 status = avi_scan_legacy_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos);
1264 if (status != VC_CONTAINER_SUCCESS) goto error;
1265
1266 for (i = 0; i < p_ctx->tracks_num; i++)
1267 {
1268 if (i != seek_track_num)
1269 {
1270 uint64_t track_pos = pos;
1271 int64_t track_time = *p_offset;
1272
1273 status = avi_scan_legacy_index_chunk(p_ctx, i, &track_time, flags, &track_pos);
1274 if (status != VC_CONTAINER_SUCCESS) goto error;
1275 p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos;
1276 p_ctx->tracks[i]->priv->module->chunk.local_state.current_track_num = i;
1277 }
1278 }
1279 }
1280 }
1281
1282 position = pos;
1283
1284 /* Set the seek track's data offset */
1285 p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.data_offset = position;
1286 p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.current_track_num = seek_track_num;
1287
1288 /* Connect the earlier track(s) to the global state. Needs 2 passes */
1289 module->state.data_offset = INT64_MAX;
1290 for(i = 0; i < p_ctx->tracks_num; i++)
1291 {
1292 if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset <
1293 module->state.data_offset)
1294 module->state = p_ctx->tracks[i]->priv->module->chunk.local_state;
1295 }
1296 for(i = 0; i < p_ctx->tracks_num; i++)
1297 {
1298 if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset ==
1299 module->state.data_offset)
1300 p_ctx->tracks[i]->priv->module->chunk.state = &module->state;
1301 }
1302
1303 LOG_DEBUG(p_ctx, "seek to %"PRIi64", position %"PRIu64, *p_offset, pos);
1304
1305 return SEEK(p_ctx, position);
1306
1307error:
1308 p_ctx->priv->module->state = global_state;
1309 for(i = 0; i < p_ctx->tracks_num; i++)
1310 p_ctx->tracks[i]->priv->module->chunk = chunk_state[i];
1311 SEEK(p_ctx, position);
1312 return status;
1313}
1314
1315/*****************************************************************************/
1316static VC_CONTAINER_STATUS_T avi_reader_close( VC_CONTAINER_T *p_ctx )
1317{
1318 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1319 unsigned int i;
1320
1321 for(i = 0; i < p_ctx->tracks_num; i++)
1322 vc_container_free_track(p_ctx, p_ctx->tracks[i]);
1323 p_ctx->tracks = NULL;
1324 p_ctx->tracks_num = 0;
1325 free(module);
1326 p_ctx->priv->module = 0;
1327 return VC_CONTAINER_SUCCESS;
1328}
1329
1330/*****************************************************************************/
1331VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T *p_ctx )
1332{
1333 VC_CONTAINER_MODULE_T *module = 0;
1334 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
1335 uint32_t chunk_size;
1336 uint32_t peek_buf[3];
1337 unsigned int i;
1338 uint32_t flags, num_streams;
1339 int64_t offset;
1340
1341 /* Check the RIFF chunk descriptor */
1342 if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 12) != 12)
1343 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1344 if( peek_buf[0] != VC_FOURCC('R','I','F','F') )
1345 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1346 if( peek_buf[2] != VC_FOURCC('A','V','I',' ') )
1347 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1348
1349 /*
1350 * We now know we are dealing with an AVI file
1351 */
1352 SKIP_FOURCC(p_ctx, "RIFF ID");
1353 SKIP_U32(p_ctx, "fileSize");
1354 SKIP_FOURCC(p_ctx, "fileType");
1355
1356 /* Look for the 'hdrl' LIST (sub)chunk */
1357 status = avi_find_list(p_ctx, VC_FOURCC('h','d','r','l'), &chunk_size);
1358 if (status != VC_CONTAINER_SUCCESS)
1359 {
1360 LOG_DEBUG(p_ctx, "'hdrl' LIST not found");
1361 return VC_CONTAINER_ERROR_FORMAT_INVALID;
1362 }
1363
1364 SKIP_FOURCC(p_ctx, "hdrl");
1365
1366 /* Now look for the 'avih' sub-chunk */
1367 status = avi_find_chunk(p_ctx, VC_FOURCC('a','v','i','h'), &chunk_size);
1368 if (status != VC_CONTAINER_SUCCESS)
1369 {
1370 LOG_DEBUG(p_ctx, "'avih' not found");
1371 return VC_CONTAINER_ERROR_FORMAT_INVALID;
1372 }
1373
1374 /* Parse the 'avih' sub-chunk */
1375 SKIP_U32(p_ctx, "dwMicroSecPerFrame");
1376 SKIP_U32(p_ctx, "dwMaxBytesPerSec");
1377 SKIP_U32(p_ctx, "dwPaddingGranularity");
1378 flags = READ_U32(p_ctx, "dwFlags");
1379 SKIP_U32(p_ctx, "dwTotalFrames");
1380 SKIP_U32(p_ctx, "dwInitialFrames");
1381 num_streams = READ_U32(p_ctx, "dwStreams");
1382 SKIP_U32(p_ctx, "dwSuggestedBufferSize");
1383 SKIP_U32(p_ctx, "dwWidth");
1384 SKIP_U32(p_ctx, "dwHeight");
1385 SKIP_U32(p_ctx, "dwReserved0");
1386 SKIP_U32(p_ctx, "dwReserved1");
1387 SKIP_U32(p_ctx, "dwReserved2");
1388 SKIP_U32(p_ctx, "dwReserved3");
1389
1390 if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
1391 goto error;
1392
1393 /* Allocate our context and tracks */
1394 if ((module = malloc(sizeof(*module))) == NULL)
1395 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1396 memset(module, 0, sizeof(*module));
1397 p_ctx->priv->module = module;
1398 p_ctx->tracks = module->tracks;
1399
1400 if (num_streams > AVI_TRACKS_MAX)
1401 {
1402 LOG_DEBUG(p_ctx, "cannot handle %u tracks, restricted to %d", num_streams, AVI_TRACKS_MAX);
1403 num_streams = AVI_TRACKS_MAX;
1404 }
1405
1406 for (p_ctx->tracks_num = 0; p_ctx->tracks_num != num_streams; p_ctx->tracks_num++)
1407 {
1408 p_ctx->tracks[p_ctx->tracks_num] = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
1409 if(!p_ctx->tracks[p_ctx->tracks_num]) break;
1410 }
1411 if(p_ctx->tracks_num != num_streams)
1412 { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
1413
1414 /* Try to read stream header list chunks ('strl') */
1415 for (i = 0; i != num_streams; ++i)
1416 {
1417 status = avi_read_stream_header_list(p_ctx, p_ctx->tracks[i], p_ctx->tracks[i]->priv->module);
1418 if(status != VC_CONTAINER_SUCCESS) goto error;
1419 }
1420
1421 /* Look for the 'movi' LIST (sub)chunk */
1422 status = avi_find_list(p_ctx, VC_FOURCC('m','o','v','i'), &chunk_size);
1423 if (status != VC_CONTAINER_SUCCESS)
1424 {
1425 LOG_DEBUG(p_ctx, "'movi' LIST not found");
1426 status = VC_CONTAINER_ERROR_FORMAT_INVALID;
1427 goto error;
1428 }
1429
1430 /* Store offset to the start and size of data (the 'movi' LIST) */
1431 module->data_offset = STREAM_POSITION(p_ctx);
1432 module->data_size = chunk_size;
1433
1434 p_ctx->priv->pf_close = avi_reader_close;
1435 p_ctx->priv->pf_read = avi_reader_read;
1436 p_ctx->priv->pf_seek = avi_reader_seek;
1437
1438 if (flags & AVIF_MUSTUSEINDEX)
1439 {
1440 LOG_DEBUG(p_ctx, "AVIF_MUSTUSEINDEX not supported, playback might not work properly");
1441 }
1442
1443 /* If the stream is seekable, see if we can find an index (for at
1444 least one of the tracks); even if we cannot find an index now,
1445 one might become available later (e.g. when the stream grows
1446 run-time), in that case we might want to report that we can seek
1447 and re-search for the index again if or when we're requested to
1448 seek. */
1449 if(STREAM_SEEKABLE(p_ctx))
1450 {
1451 p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
1452 p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK;
1453
1454 for(i = 0; i < p_ctx->tracks_num; i++)
1455 if(p_ctx->tracks[i]->priv->module->index_offset) break;
1456
1457 if (i < p_ctx->tracks_num)
1458 {
1459 p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX;
1460 if (flags & AVIF_TRUSTCKTYPE)
1461 p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG;
1462 }
1463 else
1464 {
1465 /* Skip data first */
1466 AVI_SKIP_CHUNK(p_ctx, module->data_size);
1467 /* Now search for the index */
1468 status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size);
1469 if (status == VC_CONTAINER_SUCCESS)
1470 {
1471 LOG_DEBUG(p_ctx, "'idx1' found");
1472 /* Store offset to index data */
1473 module->index_offset = STREAM_POSITION(p_ctx);
1474 module->index_size = chunk_size;
1475 p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX;
1476 p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG;
1477 }
1478
1479 /* Seek back to the start of the data */
1480 SEEK(p_ctx, module->data_offset);
1481 }
1482 }
1483
1484 SKIP_FOURCC(p_ctx, "movi");
1485
1486 for (i = 0; i != num_streams; i++)
1487 {
1488 p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->priv->module->state;
1489 }
1490 p_ctx->priv->module->state.data_offset = STREAM_POSITION(p_ctx);
1491
1492 /* Update the tracks to set their data offsets. This help with bad
1493 interleaving, for example when there is all the video tracks followed
1494 by all the audio tracks. It means we don't have to read through the
1495 tracks we are not interested in when forcing a read from a given track,
1496 as could be the case in the above example. If this fails we will fall
1497 back to skipping track data. */
1498 offset = INT64_C(0);
1499 avi_reader_seek(p_ctx, &offset, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_PRECISE);
1500
1501 if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
1502 return VC_CONTAINER_SUCCESS;
1503
1504error:
1505 LOG_DEBUG(p_ctx, "error opening stream (%i)", status);
1506 for(i = 0; i < p_ctx->tracks_num; i++)
1507 vc_container_free_track(p_ctx, p_ctx->tracks[i]);
1508 p_ctx->tracks = NULL;
1509 p_ctx->tracks_num = 0;
1510 if (module) free(module);
1511 p_ctx->priv->module = NULL;
1512 return status;
1513}
1514
1515/********************************************************************************
1516 Entrypoint function
1517 ********************************************************************************/
1518
1519#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
1520# pragma weak reader_open avi_reader_open
1521#endif
1522