1/*
2 * Copyright (c) 2008, Stephan A��mus <superstippi@gmx.de>
3 * Copyright (c) 2007, Marcus Overhagen
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without modification,
7 * are permitted provided that the following conditions are met:
8 *
9 *  * Redistributions of source code must retain the above copyright notice,
10 *    this list of conditions and the following disclaimer.
11 *  * Redistributions in binary form must reproduce the above copyright notice,
12 *    this list of conditions and the following disclaimer in the documentation
13 *    and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
24 * OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26#include <SupportDefs.h>
27#include <DataIO.h>
28#include <math.h>
29#include <stdio.h>
30#include <new>
31
32#include "StandardIndex.h"
33
34
35//#define TRACE_START_INDEX
36#ifdef TRACE_START_INDEX
37  #define TRACE printf
38#else
39  #define TRACE(a...)
40#endif
41
42#define ERROR(a...) fprintf(stderr, a)
43
44struct chunk {
45	uint32 chunk_id;
46	uint32 size;
47};
48
49
50StandardIndex::StandardIndex(BPositionIO *source, OpenDMLParser *parser)
51 :	Index(source, parser)
52 ,	fIndex(NULL)
53 ,	fIndexSize(0)
54 ,	fDataOffset(parser->MovieListStart() - 4)
55{
56}
57
58
59StandardIndex::~StandardIndex()
60{
61	delete [] fIndex;
62}
63
64
65status_t
66StandardIndex::Init()
67{
68	TRACE("Constructing a Standard Index\n");
69	uint32 indexBytes = fParser->StandardIndexSize();
70	fIndexSize = indexBytes / sizeof(avi_standard_index_entry);
71	indexBytes = fIndexSize * sizeof(avi_standard_index_entry);
72
73	if (indexBytes > 0x1900000) { // 25 MB
74		ERROR("libOpenDML: StandardIndex::Init index is way too big\n");
75		return B_NO_MEMORY;
76	}
77
78	if (fIndexSize == 0) {
79		printf("StandardIndex::Init index is empty\n");
80		return B_NO_MEMORY;
81	}
82
83	fIndex = new (std::nothrow) avi_standard_index_entry[fIndexSize];
84	if (fIndex == NULL) {
85		ERROR("libOpenDML: StandardIndex::Init out of memory\n");
86		return B_NO_MEMORY;
87	}
88
89	if ((int32)indexBytes != fSource->ReadAt(fParser->StandardIndexStart(),
90			fIndex, indexBytes)) {
91		ERROR("libOpenDML: StandardIndex::Init file reading failed\n");
92		delete [] fIndex;
93		fIndex = NULL;
94		return B_IO_ERROR;
95	}
96
97	int stream_index;
98	int64 position;
99	uint32 size;
100	uint64 frame[fStreamCount];
101	uint64 frame_no;
102	bigtime_t pts = 0;
103	bool keyframe;
104	uint32 sample_size;
105
106	const OpenDMLStream *stream;
107
108	for (uint32 i=0;i < fStreamCount; i++) {
109		frame[i] = 0;
110	}
111
112	for (uint32 i = 0; i < fIndexSize; i++) {
113
114		if (fIndex[i].chunk_id == 0) {
115			printf("Corrupt Index entry at %ld/%ld\n",i,fIndexSize);
116			continue;
117		}
118
119		stream_index = ((fIndex[i].chunk_id & 0xff) - '0') * 10;
120		stream_index += ((fIndex[i].chunk_id >> 8) & 0xff) - '0';
121
122		stream = fParser->StreamInfo(stream_index);
123
124		if (stream == NULL) {
125			printf("No Stream Info for stream %d\n",stream_index);
126			continue;
127		}
128
129        keyframe = (fIndex[i].flags >> AVIIF_KEYFRAME_SHIFT) & 1;
130        size = fIndex[i].chunk_length;
131
132        // Some muxers write chunk_offset as non-relative.  So we test if the first index actually points to a chunk
133        if (i == 0) {
134        	off_t here = fSource->Position();
135
136        	// first try and validate position as relative to the data chunk
137	        position = fDataOffset + fIndex[i].chunk_offset;
138	        TRACE("Validating chunk position %Ld as relative\n",position);
139			if (!IsValidChunk(position,size)) {
140
141	        	// then try and validate position as an absolute position that points to a chunk
142				fDataOffset = 0;
143		        position = fDataOffset + fIndex[i].chunk_offset;
144		        TRACE("Validating chunk position %Ld as absolute\n",position);
145				if (!IsValidChunk(position,size)) {
146					ERROR("Index is invalid, chunk offset does not point to a chunk\n");
147					return B_ERROR;
148				}
149			}
150
151			fSource->Seek(here, SEEK_SET);
152        }
153
154        position = fDataOffset + fIndex[i].chunk_offset;
155		frame_no = frame[stream_index];
156
157		if (stream->is_video) {
158			// Video is easy enough, it is always 1 frame = 1 index
159			pts = frame[stream_index] * 1000000LL * stream->frames_per_sec_scale / stream->frames_per_sec_rate;
160			frame[stream_index]++;
161		} else if (stream->is_audio) {
162
163			pts = frame[stream_index] * 1000000LL / stream->audio_format->frames_per_sec;
164
165			// Audio varies based on many different hacks over the years
166			// The simplest is chunk size / sample size = no of samples in the chunk for uncompressed audio
167			// ABR Compressed audio is more difficult and VBR Compressed audio is even harder
168			// What follows is what I have worked out from various sources across the internet.
169			if (stream->audio_format->format_tag != 0x0001) {
170				// VBR audio is detected as having a block_align >= 960
171				if (stream->audio_format->block_align >= 960) {
172					// VBR Audio so block_align is the largest no of samples in a chunk
173					// scale is the no of samples in a frame
174					// rate is the sample rate
175					// but we must round up when calculating no of frames in a chunk
176					frame[stream_index] += (uint64)(ceil((double)size / (double)stream->audio_format->block_align)) * stream->frames_per_sec_scale;
177				} else {
178					// ABR Audio so use Chunk Size and average bytes per second to determine how many samples there are in the chunk
179					frame[stream_index] += (uint64)(ceil((double)size / (double)stream->audio_format->block_align)) * stream->audio_format->frames_per_sec / stream->audio_format->avg_bytes_per_sec;
180				}
181			} else {
182				// sample size can be corrupt
183				if (stream->stream_header.sample_size > 0) {
184					sample_size = stream->stream_header.sample_size;
185				} else {
186					// compute sample size
187					sample_size = stream->audio_format->bits_per_sample * stream->audio_format->channels / 8;
188				}
189				frame[stream_index] += size / stream->stream_header.sample_size;
190			}
191		}
192
193        AddIndex(stream_index, position, size, frame_no, pts, keyframe);
194	}
195
196	return B_OK;
197}
198
199bool
200StandardIndex::IsValidChunk(off_t position, uint32 size) {
201
202	chunk aChunk;
203
204	if ((int32)8 != fSource->ReadAt(position, &aChunk, 8)) {
205		return false;
206	}
207
208	return (aChunk.size == size);
209}
210