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