1/* 2 * Copyright (c) 2005, David McPaul 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 22 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 * OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 27#include "ASFFileReader.h" 28 29#include <DataIO.h> 30#include <SupportKit.h> 31 32#include <iostream> 33 34 35ASFFileReader::ASFFileReader(BPositionIO *pStream) 36{ 37 theStream = pStream; 38 39 // Find Size of Stream, need to rethink this for non seekable streams 40 theStream->Seek(0,SEEK_END); 41 StreamSize = theStream->Position(); 42 theStream->Seek(0,SEEK_SET); 43 packet = asf_packet_create(); 44} 45 46 47ASFFileReader::~ASFFileReader() 48{ 49 if (packet) { 50 asf_packet_destroy(packet); 51 packet = NULL; 52 } 53 54 if (asfFile) { 55 asf_close(asfFile); 56 asfFile = NULL; 57 } 58 59 theStream = NULL; 60 streams.clear(); 61} 62 63status_t 64ASFFileReader::ParseFile() 65{ 66 asf_iostream_t ioStream; 67 ioStream.opaque = theStream; 68 ioStream.read = &ASFFileReader::read; 69 ioStream.write = &ASFFileReader::write; 70 ioStream.seek = &ASFFileReader::seek; 71 72 asfFile = asf_open_cb(&ioStream); 73 74 int result = asf_init(asfFile); 75 76 if (result != 0) { 77 printf("error initialising asf asf_init returned %d\n",result); 78 return B_ERROR; 79 } 80 81 asf_metadata_t *metadata = asf_header_get_metadata(asfFile); 82 83 if (metadata) { 84 printf("Title %s\n",metadata->title); 85 printf("Artist %s\n",metadata->artist); 86 printf("Copyright %s\n",metadata->copyright); 87 printf("Description %s\n",metadata->description); 88 printf("Rating %s\n",metadata->rating); 89 printf("Additional Entries %d\n",metadata->extended_count); 90 91 asf_metadata_destroy(metadata); 92 } 93 94 uint32 totalStreams = getStreamCount(); 95 StreamEntry streamEntry; 96 97 for (uint32 i=0;i < totalStreams;i++) { 98 streamEntry.streamIndex = i; 99 streams.push_back(streamEntry); 100 } 101 102 ParseIndex(); 103 104 // load the first packet 105 if (asf_get_packet(asfFile, packet) < 0) { 106 printf("Could not get first packet\n"); 107 return B_ERROR; 108 } 109 110 return B_OK; 111} 112 113 114bool 115ASFFileReader::IsEndOfData(off_t pPosition) 116{ 117 return true; 118} 119 120 121bool 122ASFFileReader::IsEndOfFile(off_t position) 123{ 124 return (position >= StreamSize); 125} 126 127 128bool 129ASFFileReader::IsEndOfFile() 130{ 131 return theStream->Position() >= StreamSize; 132} 133 134uint32 135ASFFileReader::getStreamCount() 136{ 137 return asf_get_stream_count(asfFile) + 1; 138} 139 140bool 141ASFFileReader::getAudioFormat(uint32 streamIndex, ASFAudioFormat *format) 142{ 143 asf_waveformatex_t *audioHeader; 144 asf_stream_t *stream; 145 146 if (IsAudio(streamIndex)) { 147 stream = asf_get_stream(asfFile, streamIndex); 148 149 if (stream) { 150 audioHeader = (asf_waveformatex_t *)(stream->properties); 151 format->Compression = audioHeader->wFormatTag; 152 format->NoChannels = audioHeader->nChannels; 153 format->SamplesPerSec = audioHeader->nSamplesPerSec; 154 format->AvgBytesPerSec = audioHeader->nAvgBytesPerSec; 155 format->BlockAlign = audioHeader->nBlockAlign; 156 format->BitsPerSample = audioHeader->wBitsPerSample; 157 format->extraDataSize = audioHeader->cbSize; 158 format->extraData = audioHeader->data; 159 160 return true; 161 } 162 } 163 164 return false; 165} 166 167bool 168ASFFileReader::getVideoFormat(uint32 streamIndex, ASFVideoFormat *format) 169{ 170 asf_bitmapinfoheader_t *videoHeader; 171 asf_stream_t *stream; 172 173 if (IsVideo(streamIndex)) { 174 stream = asf_get_stream(asfFile, streamIndex); 175 176 if (stream) { 177 videoHeader = (asf_bitmapinfoheader_t *)(stream->properties); 178 format->Compression = videoHeader->biCompression; 179 format->VideoWidth = videoHeader->biWidth; 180 format->VideoHeight = videoHeader->biHeight; 181 format->Planes = videoHeader->biPlanes; 182 format->BitCount = videoHeader->biBitCount; 183 format->extraDataSize = videoHeader->biSize - 40; 184 format->extraData = videoHeader->data; 185 186 if (stream->flags & ASF_STREAM_FLAG_EXTENDED) { 187 format->FrameScale = stream->extended_properties->avg_time_per_frame; 188 format->FrameRate = 10000000L; 189 printf("num avg time per frame for video %Ld\n",stream->extended_properties->avg_time_per_frame); 190 } 191 192 return true; 193 } 194 } 195 196 return false; 197} 198 199 200bigtime_t 201ASFFileReader::getStreamDuration(uint32 streamIndex) 202{ 203 if (streamIndex < streams.size()) { 204 return streams[streamIndex].getDuration(); 205 } 206 207 asf_stream_t *stream; 208 209 stream = asf_get_stream(asfFile, streamIndex); 210 211 if (stream) { 212 if (stream->flags & ASF_STREAM_FLAG_EXTENDED) { 213 printf("STREAM %ld end time %Ld, start time %Ld\n",streamIndex, stream->extended_properties->end_time, stream->extended_properties->start_time); 214 if (stream->extended_properties->end_time - stream->extended_properties->start_time > 0) { 215 return stream->extended_properties->end_time - stream->extended_properties->start_time; 216 } 217 } 218 } 219 220 return asf_get_duration(asfFile) / 10L; 221} 222 223uint32 224ASFFileReader::getFrameCount(uint32 streamIndex) 225{ 226 if (streamIndex < streams.size()) { 227 return streams[streamIndex].getFrameCount(); 228 } 229 230 return 0; 231} 232 233bool 234ASFFileReader::IsVideo(uint32 streamIndex) 235{ 236 asf_stream_t *stream; 237 238 stream = asf_get_stream(asfFile, streamIndex); 239 240 if (stream) { 241 return ((stream->type == ASF_STREAM_TYPE_VIDEO) && (stream->flags & ASF_STREAM_FLAG_AVAILABLE)); 242 } 243 244 return false; 245} 246 247 248bool 249ASFFileReader::IsAudio(uint32 streamIndex) 250{ 251 asf_stream_t *stream; 252 253 stream = asf_get_stream(asfFile, streamIndex); 254 255 if (stream) { 256 return ((stream->type == ASF_STREAM_TYPE_AUDIO) && (stream->flags & ASF_STREAM_FLAG_AVAILABLE)); 257 } 258 259 return false; 260} 261 262IndexEntry 263ASFFileReader::GetIndex(uint32 streamIndex, uint32 frameNo) 264{ 265 return streams[streamIndex].GetIndex(frameNo); 266} 267 268bool 269ASFFileReader::HasIndex(uint32 streamIndex, uint32 frameNo) 270{ 271 if (streamIndex < streams.size()) { 272 return streams[streamIndex].HasIndex(frameNo); 273 } 274 275 return false; 276} 277 278uint32 279ASFFileReader::GetFrameForTime(uint32 streamIndex, bigtime_t time) 280{ 281 if (streamIndex < streams.size()) { 282 return streams[streamIndex].GetIndex(time).frameNo; 283 } 284 285 return 0; 286} 287 288void 289ASFFileReader::ParseIndex() { 290 // Try to build some sort of useful index 291 // packet->send_time seems to be a better presentation time stamp than pts though 292 293 if (asf_seek_to_msec(asfFile,0) < 0) { 294 printf("Seek to start of stream failed\n"); 295 } 296 297 asf_payload_t *payload; 298 299 while (asf_get_packet(asfFile, packet) > 0) { 300 for (int i=0;i<packet->payload_count;i++) { 301 payload = (asf_payload_t *)(&packet->payloads[i]); 302// printf("Payload %d Stream %d Keyframe %d send time %Ld pts %Ld id %d size %d\n",i+1,payload->stream_number,payload->key_frame, 1000L * bigtime_t(packet->send_time), 1000L * bigtime_t(payload->pts), payload->media_object_number, payload->datalen); 303 if (payload->stream_number < streams.size()) { 304 streams[payload->stream_number].AddPayload(payload->media_object_number, payload->key_frame, 1000L * payload->pts, payload->datalen, false); 305 } 306 } 307 } 308 309 for (uint32 i=0;i<streams.size();i++) { 310 streams[i].AddPayload(0, false, 0, 0, true); 311 streams[i].setDuration(1000L * (packet->send_time + packet->duration)); 312 } 313 314 if (asf_seek_to_msec(asfFile,0) < 0) { 315 printf("Seek to start of stream failed\n"); 316 } 317} 318 319bool 320ASFFileReader::GetNextChunkInfo(uint32 streamIndex, uint32 pFrameNo, 321 char **buffer, uint32 *size, bool *keyframe, bigtime_t *pts) 322{ 323 // Ok, Need to join payloads together that have the same payload->media_object_number 324 asf_payload_t *payload; 325 int64_t seekResult; 326 int packetSize; 327 328 IndexEntry indexEntry = GetIndex(streamIndex, pFrameNo); 329 330 if (indexEntry.noPayloads == 0) { 331 // No index entry 332 printf("No Index entry for frame %ld\n",pFrameNo); 333 return false; 334 } 335 336// printf("Stream %ld need pts %Ld, packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration)); 337 338 if (1000LL * packet->send_time > indexEntry.pts || 1000LL * (packet->send_time + packet->duration) < indexEntry.pts) { 339 seekResult = asf_seek_to_msec(asfFile, indexEntry.pts/1000); 340 if (seekResult >= 0) { 341// printf("Stream %ld seeked to %Ld got %Ld\n",streamIndex,indexEntry.pts, 1000L * seekResult); 342 packetSize = asf_get_packet(asfFile, packet); 343 if (packetSize <= 0) { 344 printf("Failed to Get Packet after seek result (%d)\n",packetSize); 345 return false; 346 } 347 } else if (seekResult == ASF_ERROR_SEEKABLE) { 348 // Stream not seekeable. Is what we want forward in the stream, if so seek using Get Packet 349 if (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) { 350 while (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) { 351 packetSize = asf_get_packet(asfFile, packet); 352 if (packetSize <= 0) { 353 printf("Failed to Seek using Get Packet result (%d)\n",packetSize); 354 return false; 355 } 356// printf("Stream %ld searching forward for pts %Ld, got packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration)); 357 } 358 } else { 359 // seek to 0 and read forward, going to be a killer on performance 360 seekResult = asf_seek_to_msec(asfFile, 0); 361 while (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) { 362 packetSize = asf_get_packet(asfFile, packet); 363 if (packetSize <= 0) { 364 printf("Failed to Seek using Get Packet result (%d)\n",packetSize); 365 return false; 366 } 367// printf("Stream %ld searching forward from 0 for pts %Ld, got packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration)); 368 } 369 } 370 } else { 371 printf("Seek failed\n"); 372 return false; 373 } 374 } 375 376 // fillin some details 377 *size = indexEntry.dataSize; 378 *keyframe = indexEntry.keyFrame; 379 *pts = indexEntry.pts; 380 381 uint32 expectedPayloads = indexEntry.noPayloads; 382 uint32 offset = 0; 383 384 for (int i=0;i<packet->payload_count;i++) { 385 payload = (asf_payload_t *)(&packet->payloads[i]); 386 // find the first payload matching the id we want and then 387 // combine the next x payloads where x is the noPayloads in indexEntry 388 if (payload->media_object_number == indexEntry.id && payload->stream_number == streamIndex) { 389 // copy data to buffer 390 memcpy(*buffer + offset, payload->data, payload->datalen); 391 offset += payload->datalen; 392 expectedPayloads--; 393 394 if (expectedPayloads == 0) { 395 return true; 396 } 397 } 398 } 399 400 // combine packets into a single buffer 401 packetSize = asf_get_packet(asfFile, packet); 402 while ((packetSize > 0) && (expectedPayloads > 0)) { 403 for (int i=0;i<packet->payload_count;i++) { 404 payload = (asf_payload_t *)(&packet->payloads[i]); 405 // find the first payload matching the id we want and then 406 // combine the next x payloads where x is the noPayloads in indexEntry 407 if (payload->media_object_number == indexEntry.id && payload->stream_number == streamIndex) { 408 // copy data to buffer 409 memcpy(*buffer + offset, payload->data, payload->datalen); 410 offset += payload->datalen; 411 expectedPayloads--; 412 if (expectedPayloads == 0) { 413 return true; 414 } 415 } 416 } 417 packetSize = asf_get_packet(asfFile, packet); 418 } 419 420 if (packetSize == ASF_ERROR_EOF) { 421 printf("Unexpected EOF file truncated?\n"); 422 } else { 423 printf("EOF? %ld,%d\n",expectedPayloads, packetSize); 424 } 425 426 return false; 427} 428 429 430/* static */ 431bool 432ASFFileReader::IsSupported(BPositionIO *source) 433{ 434 // Read first 4 bytes and if they match 30 26 b2 75 we have a asf file 435 uint8 header[4]; 436 bool supported = false; 437 438 off_t position = source->Position(); 439 440 if (source->Read(&header[0],4) == 4) { 441 supported = header[0] == 0x30 && 442 header[1] == 0x26 && 443 header[2] == 0xb2 && 444 header[3] == 0x75; 445 } 446 447 source->Seek(position,SEEK_SET); 448 449 return supported; 450} 451 452/* static */ 453int32_t 454ASFFileReader::read(void *opaque, void *buffer, int32_t size) 455{ 456 // opaque is the BPositionIO 457 return reinterpret_cast<BPositionIO *>(opaque)->Read(buffer, size); 458} 459 460/* static */ 461int32_t 462ASFFileReader::write(void *opaque, void *buffer, int32_t size) 463{ 464 return reinterpret_cast<BPositionIO *>(opaque)->Write(buffer, size); 465} 466 467/* static */ 468int64_t 469ASFFileReader::seek(void *opaque, int64_t offset) 470{ 471 return reinterpret_cast<BPositionIO *>(opaque)->Seek(offset, SEEK_SET); 472} 473