content/media/apple/AppleMP3Reader.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "AppleMP3Reader.h"
michael@0 6
michael@0 7 #include "nsISeekableStream.h"
michael@0 8 #include "MediaDecoder.h"
michael@0 9
michael@0 10 // Number of bytes we will read and pass to the audio parser in each
michael@0 11 // |DecodeAudioData| call.
michael@0 12 #define AUDIO_READ_BYTES 4096
michael@0 13
michael@0 14 // Maximum number of audio frames we will accept from the audio decoder in one
michael@0 15 // go. Carefully select this to work well with both the mp3 1152 max frames
michael@0 16 // per block and power-of-2 allocation sizes. Since we must pre-allocate the
michael@0 17 // buffer we cannot use AudioCompactor without paying for an additional
michael@0 18 // allocation and copy. Therefore, choosing a value that divides exactly into
michael@0 19 // 1152 is most memory efficient.
michael@0 20 #define MAX_AUDIO_FRAMES 128
michael@0 21
michael@0 22 namespace mozilla {
michael@0 23
michael@0 24 #ifdef PR_LOGGING
michael@0 25 extern PRLogModuleInfo* gMediaDecoderLog;
michael@0 26 #define LOGE(...) PR_LOG(gMediaDecoderLog, PR_LOG_ERROR, (__VA_ARGS__))
michael@0 27 #define LOGW(...) PR_LOG(gMediaDecoderLog, PR_LOG_WARNING, (__VA_ARGS__))
michael@0 28 #define LOGD(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__))
michael@0 29 #else
michael@0 30 #define LOGE(...)
michael@0 31 #define LOGW(...)
michael@0 32 #define LOGD(...)
michael@0 33 #endif
michael@0 34
michael@0 35 #define PROPERTY_ID_FORMAT "%c%c%c%c"
michael@0 36 #define PROPERTY_ID_PRINT(x) ((x) >> 24), \
michael@0 37 ((x) >> 16) & 0xff, \
michael@0 38 ((x) >> 8) & 0xff, \
michael@0 39 (x) & 0xff
michael@0 40
michael@0 41 AppleMP3Reader::AppleMP3Reader(AbstractMediaDecoder *aDecoder)
michael@0 42 : MediaDecoderReader(aDecoder)
michael@0 43 , mStreamReady(false)
michael@0 44 , mAudioFramesPerCompressedPacket(0)
michael@0 45 , mCurrentAudioFrame(0)
michael@0 46 , mAudioChannels(0)
michael@0 47 , mAudioSampleRate(0)
michael@0 48 , mAudioFileStream(nullptr)
michael@0 49 , mAudioConverter(nullptr)
michael@0 50 , mMP3FrameParser(mDecoder->GetResource()->GetLength())
michael@0 51 {
michael@0 52 MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread");
michael@0 53 }
michael@0 54
michael@0 55 AppleMP3Reader::~AppleMP3Reader()
michael@0 56 {
michael@0 57 MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread");
michael@0 58 }
michael@0 59
michael@0 60
michael@0 61 /*
michael@0 62 * The Apple audio decoding APIs are very callback-happy. When the parser has
michael@0 63 * some metadata, it will call back to here.
michael@0 64 */
michael@0 65 static void _AudioMetadataCallback(void *aThis,
michael@0 66 AudioFileStreamID aFileStream,
michael@0 67 AudioFileStreamPropertyID aPropertyID,
michael@0 68 UInt32 *aFlags)
michael@0 69 {
michael@0 70 ((AppleMP3Reader*)aThis)->AudioMetadataCallback(aFileStream, aPropertyID,
michael@0 71 aFlags);
michael@0 72 }
michael@0 73
michael@0 74 /*
michael@0 75 * Similar to above, this is called when the parser has enough data to parse
michael@0 76 * one or more samples.
michael@0 77 */
michael@0 78 static void _AudioSampleCallback(void *aThis,
michael@0 79 UInt32 aNumBytes, UInt32 aNumPackets,
michael@0 80 const void *aData,
michael@0 81 AudioStreamPacketDescription *aPackets)
michael@0 82 {
michael@0 83 ((AppleMP3Reader*)aThis)->AudioSampleCallback(aNumBytes, aNumPackets,
michael@0 84 aData, aPackets);
michael@0 85 }
michael@0 86
michael@0 87
michael@0 88 /*
michael@0 89 * If we're not at end of stream, read |aNumBytes| from the media resource,
michael@0 90 * put it in |aData|, and return true.
michael@0 91 * Otherwise, put as much data as is left into |aData|, set |aNumBytes| to the
michael@0 92 * amount of data we have left, and return false.
michael@0 93 */
michael@0 94 nsresult
michael@0 95 AppleMP3Reader::Read(uint32_t *aNumBytes, char *aData)
michael@0 96 {
michael@0 97 MediaResource *resource = mDecoder->GetResource();
michael@0 98
michael@0 99 // Loop until we have all the data asked for, or we've reached EOS
michael@0 100 uint32_t totalBytes = 0;
michael@0 101 uint32_t numBytes;
michael@0 102 do {
michael@0 103 uint32_t bytesWanted = *aNumBytes - totalBytes;
michael@0 104 nsresult rv = resource->Read(aData + totalBytes, bytesWanted, &numBytes);
michael@0 105 totalBytes += numBytes;
michael@0 106
michael@0 107 if (NS_FAILED(rv)) {
michael@0 108 *aNumBytes = 0;
michael@0 109 return NS_ERROR_FAILURE;
michael@0 110 }
michael@0 111 } while(totalBytes < *aNumBytes && numBytes);
michael@0 112
michael@0 113 *aNumBytes = totalBytes;
michael@0 114
michael@0 115 // We will have read some data in the last iteration iff we filled the buffer.
michael@0 116 // XXX Maybe return a better value than NS_ERROR_FAILURE?
michael@0 117 return numBytes ? NS_OK : NS_ERROR_FAILURE;
michael@0 118 }
michael@0 119
michael@0 120 nsresult
michael@0 121 AppleMP3Reader::Init(MediaDecoderReader* aCloneDonor)
michael@0 122 {
michael@0 123 AudioFileTypeID fileType = kAudioFileMP3Type;
michael@0 124
michael@0 125 OSStatus rv = AudioFileStreamOpen(this,
michael@0 126 _AudioMetadataCallback,
michael@0 127 _AudioSampleCallback,
michael@0 128 fileType,
michael@0 129 &mAudioFileStream);
michael@0 130
michael@0 131 if (rv) {
michael@0 132 return NS_ERROR_FAILURE;
michael@0 133 }
michael@0 134
michael@0 135 return NS_OK;
michael@0 136 }
michael@0 137
michael@0 138
michael@0 139 struct PassthroughUserData {
michael@0 140 AppleMP3Reader *mReader;
michael@0 141 UInt32 mNumPackets;
michael@0 142 UInt32 mDataSize;
michael@0 143 const void *mData;
michael@0 144 AudioStreamPacketDescription *mPacketDesc;
michael@0 145 bool mDone;
michael@0 146 };
michael@0 147
michael@0 148 // Error value we pass through the decoder to signal that nothing has gone wrong
michael@0 149 // during decoding, but more data is needed.
michael@0 150 const UInt32 kNeedMoreData = 'MOAR';
michael@0 151
michael@0 152 /*
michael@0 153 * This function is called from |AudioConverterFillComplexBuffer|, which is
michael@0 154 * called from |AudioSampleCallback| below, which in turn is called by
michael@0 155 * |AudioFileStreamParseBytes|, which is called by |DecodeAudioData|.
michael@0 156 *
michael@0 157 * Mercifully, this is all synchronous.
michael@0 158 *
michael@0 159 * This callback is run when the AudioConverter (decoder) wants more MP3 packets
michael@0 160 * to decode.
michael@0 161 */
michael@0 162 /* static */ OSStatus
michael@0 163 AppleMP3Reader::PassthroughInputDataCallback(AudioConverterRef aAudioConverter,
michael@0 164 UInt32 *aNumDataPackets /* in/out */,
michael@0 165 AudioBufferList *aData /* in/out */,
michael@0 166 AudioStreamPacketDescription **aPacketDesc,
michael@0 167 void *aUserData)
michael@0 168 {
michael@0 169 PassthroughUserData *userData = (PassthroughUserData *)aUserData;
michael@0 170 if (userData->mDone) {
michael@0 171 // We make sure this callback is run _once_, with all the data we received
michael@0 172 // from |AudioFileStreamParseBytes|. When we return an error, the decoder
michael@0 173 // simply passes the return value on to the calling method,
michael@0 174 // |AudioSampleCallback|; and flushes all of the audio frames it had
michael@0 175 // buffered. It does not change the decoder's state.
michael@0 176 LOGD("requested too much data; returning\n");
michael@0 177 *aNumDataPackets = 0;
michael@0 178 return kNeedMoreData;
michael@0 179 }
michael@0 180
michael@0 181 userData->mDone = true;
michael@0 182
michael@0 183 LOGD("AudioConverter wants %u packets of audio data\n", *aNumDataPackets);
michael@0 184
michael@0 185 *aNumDataPackets = userData->mNumPackets;
michael@0 186 *aPacketDesc = userData->mPacketDesc;
michael@0 187
michael@0 188 aData->mBuffers[0].mNumberChannels = userData->mReader->mAudioChannels;
michael@0 189 aData->mBuffers[0].mDataByteSize = userData->mDataSize;
michael@0 190 aData->mBuffers[0].mData = const_cast<void *>(userData->mData);
michael@0 191
michael@0 192 return 0;
michael@0 193 }
michael@0 194
michael@0 195 /*
michael@0 196 * This callback is called when |AudioFileStreamParseBytes| has enough data to
michael@0 197 * extract one or more MP3 packets.
michael@0 198 */
michael@0 199 void
michael@0 200 AppleMP3Reader::AudioSampleCallback(UInt32 aNumBytes,
michael@0 201 UInt32 aNumPackets,
michael@0 202 const void *aData,
michael@0 203 AudioStreamPacketDescription *aPackets)
michael@0 204 {
michael@0 205 LOGD("got %u bytes, %u packets\n", aNumBytes, aNumPackets);
michael@0 206
michael@0 207 // 1 frame per packet * num channels * 32-bit float
michael@0 208 uint32_t decodedSize = MAX_AUDIO_FRAMES * mAudioChannels *
michael@0 209 sizeof(AudioDataValue);
michael@0 210
michael@0 211 // descriptions for _decompressed_ audio packets. ignored.
michael@0 212 nsAutoArrayPtr<AudioStreamPacketDescription>
michael@0 213 packets(new AudioStreamPacketDescription[MAX_AUDIO_FRAMES]);
michael@0 214
michael@0 215 // This API insists on having MP3 packets spoon-fed to it from a callback.
michael@0 216 // This structure exists only to pass our state and the result of the parser
michael@0 217 // on to the callback above.
michael@0 218 PassthroughUserData userData = { this, aNumPackets, aNumBytes, aData, aPackets, false };
michael@0 219
michael@0 220 do {
michael@0 221 // Decompressed audio buffer
michael@0 222 nsAutoArrayPtr<uint8_t> decoded(new uint8_t[decodedSize]);
michael@0 223
michael@0 224 AudioBufferList decBuffer;
michael@0 225 decBuffer.mNumberBuffers = 1;
michael@0 226 decBuffer.mBuffers[0].mNumberChannels = mAudioChannels;
michael@0 227 decBuffer.mBuffers[0].mDataByteSize = decodedSize;
michael@0 228 decBuffer.mBuffers[0].mData = decoded.get();
michael@0 229
michael@0 230 // in: the max number of packets we can handle from the decoder.
michael@0 231 // out: the number of packets the decoder is actually returning.
michael@0 232 UInt32 numFrames = MAX_AUDIO_FRAMES;
michael@0 233
michael@0 234 OSStatus rv = AudioConverterFillComplexBuffer(mAudioConverter,
michael@0 235 PassthroughInputDataCallback,
michael@0 236 &userData,
michael@0 237 &numFrames /* in/out */,
michael@0 238 &decBuffer,
michael@0 239 packets.get());
michael@0 240
michael@0 241 if (rv && rv != kNeedMoreData) {
michael@0 242 LOGE("Error decoding audio stream: %x\n", rv);
michael@0 243 break;
michael@0 244 }
michael@0 245
michael@0 246 // If we decoded zero frames then AudiOConverterFillComplexBuffer is out
michael@0 247 // of data to provide. We drained its internal buffer completely on the
michael@0 248 // last pass.
michael@0 249 if (numFrames == 0 && rv == kNeedMoreData) {
michael@0 250 LOGD("FillComplexBuffer out of data exactly\n");
michael@0 251 break;
michael@0 252 }
michael@0 253
michael@0 254 int64_t time = FramesToUsecs(mCurrentAudioFrame, mAudioSampleRate).value();
michael@0 255 int64_t duration = FramesToUsecs(numFrames, mAudioSampleRate).value();
michael@0 256
michael@0 257 LOGD("pushed audio at time %lfs; duration %lfs\n",
michael@0 258 (double)time / USECS_PER_S, (double)duration / USECS_PER_S);
michael@0 259
michael@0 260 AudioData *audio = new AudioData(mDecoder->GetResource()->Tell(),
michael@0 261 time, duration, numFrames,
michael@0 262 reinterpret_cast<AudioDataValue *>(decoded.forget()),
michael@0 263 mAudioChannels);
michael@0 264 mAudioQueue.Push(audio);
michael@0 265
michael@0 266 mCurrentAudioFrame += numFrames;
michael@0 267
michael@0 268 if (rv == kNeedMoreData) {
michael@0 269 // No error; we just need more data.
michael@0 270 LOGD("FillComplexBuffer out of data\n");
michael@0 271 break;
michael@0 272 }
michael@0 273 } while (true);
michael@0 274 }
michael@0 275
michael@0 276 bool
michael@0 277 AppleMP3Reader::DecodeAudioData()
michael@0 278 {
michael@0 279 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
michael@0 280
michael@0 281 // Read AUDIO_READ_BYTES if we can
michael@0 282 char bytes[AUDIO_READ_BYTES];
michael@0 283 uint32_t numBytes = AUDIO_READ_BYTES;
michael@0 284
michael@0 285 nsresult readrv = Read(&numBytes, bytes);
michael@0 286
michael@0 287 // This function calls |AudioSampleCallback| above, synchronously, when it
michael@0 288 // finds compressed MP3 frame.
michael@0 289 OSStatus rv = AudioFileStreamParseBytes(mAudioFileStream,
michael@0 290 numBytes,
michael@0 291 bytes,
michael@0 292 0 /* flags */);
michael@0 293
michael@0 294 if (NS_FAILED(readrv)) {
michael@0 295 mAudioQueue.Finish();
michael@0 296 return false;
michael@0 297 }
michael@0 298
michael@0 299 // DataUnavailable just means there wasn't enough data to demux anything.
michael@0 300 // We should have more to push into the demuxer next time we're called.
michael@0 301 if (rv && rv != kAudioFileStreamError_DataUnavailable) {
michael@0 302 LOGE("AudioFileStreamParseBytes returned unknown error %x", rv);
michael@0 303 return false;
michael@0 304 }
michael@0 305
michael@0 306 return true;
michael@0 307 }
michael@0 308
michael@0 309 bool
michael@0 310 AppleMP3Reader::DecodeVideoFrame(bool &aKeyframeSkip,
michael@0 311 int64_t aTimeThreshold)
michael@0 312 {
michael@0 313 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
michael@0 314 return false;
michael@0 315 }
michael@0 316
michael@0 317
michael@0 318 bool
michael@0 319 AppleMP3Reader::HasAudio()
michael@0 320 {
michael@0 321 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
michael@0 322 return mStreamReady;
michael@0 323 }
michael@0 324
michael@0 325 bool
michael@0 326 AppleMP3Reader::HasVideo()
michael@0 327 {
michael@0 328 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
michael@0 329 return false;
michael@0 330 }
michael@0 331
michael@0 332
michael@0 333 /*
michael@0 334 * Query the MP3 parser for a piece of metadata.
michael@0 335 */
michael@0 336 static nsresult
michael@0 337 GetProperty(AudioFileStreamID aAudioFileStream,
michael@0 338 AudioFileStreamPropertyID aPropertyID, void *aData)
michael@0 339 {
michael@0 340 UInt32 size;
michael@0 341 Boolean writeable;
michael@0 342 OSStatus rv = AudioFileStreamGetPropertyInfo(aAudioFileStream, aPropertyID,
michael@0 343 &size, &writeable);
michael@0 344
michael@0 345 if (rv) {
michael@0 346 LOGW("Couldn't get property " PROPERTY_ID_FORMAT "\n",
michael@0 347 PROPERTY_ID_PRINT(aPropertyID));
michael@0 348 return NS_ERROR_FAILURE;
michael@0 349 }
michael@0 350
michael@0 351 rv = AudioFileStreamGetProperty(aAudioFileStream, aPropertyID,
michael@0 352 &size, aData);
michael@0 353
michael@0 354 return NS_OK;
michael@0 355 }
michael@0 356
michael@0 357
michael@0 358 nsresult
michael@0 359 AppleMP3Reader::ReadMetadata(MediaInfo* aInfo,
michael@0 360 MetadataTags** aTags)
michael@0 361 {
michael@0 362 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
michael@0 363
michael@0 364 *aTags = nullptr;
michael@0 365
michael@0 366 /*
michael@0 367 * Feed bytes into the parser until we have all the metadata we need to
michael@0 368 * set up the decoder. When the parser has enough data, it will
michael@0 369 * synchronously call back to |AudioMetadataCallback| below.
michael@0 370 */
michael@0 371 OSStatus rv;
michael@0 372 nsresult readrv;
michael@0 373 uint32_t offset = 0;
michael@0 374 do {
michael@0 375 char bytes[AUDIO_READ_BYTES];
michael@0 376 uint32_t numBytes = AUDIO_READ_BYTES;
michael@0 377 readrv = Read(&numBytes, bytes);
michael@0 378
michael@0 379 rv = AudioFileStreamParseBytes(mAudioFileStream,
michael@0 380 numBytes,
michael@0 381 bytes,
michael@0 382 0 /* flags */);
michael@0 383
michael@0 384 mMP3FrameParser.Parse(bytes, numBytes, offset);
michael@0 385
michael@0 386 offset += numBytes;
michael@0 387
michael@0 388 // We have to do our decoder setup from the callback. When it's done it will
michael@0 389 // set mStreamReady.
michael@0 390 } while (!mStreamReady && !rv && NS_SUCCEEDED(readrv));
michael@0 391
michael@0 392 if (rv) {
michael@0 393 LOGE("Error decoding audio stream metadata\n");
michael@0 394 return NS_ERROR_FAILURE;
michael@0 395 }
michael@0 396
michael@0 397 if (!mAudioConverter) {
michael@0 398 LOGE("Failed to setup the AudioToolbox audio decoder\n");
michael@0 399 return NS_ERROR_FAILURE;
michael@0 400 }
michael@0 401
michael@0 402 if (!mMP3FrameParser.IsMP3()) {
michael@0 403 LOGE("Frame parser failed to parse MP3 stream\n");
michael@0 404 return NS_ERROR_FAILURE;
michael@0 405 }
michael@0 406
michael@0 407 aInfo->mAudio.mRate = mAudioSampleRate;
michael@0 408 aInfo->mAudio.mChannels = mAudioChannels;
michael@0 409 aInfo->mAudio.mHasAudio = mStreamReady;
michael@0 410
michael@0 411 {
michael@0 412 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
michael@0 413 mDuration = mMP3FrameParser.GetDuration();
michael@0 414 mDecoder->SetMediaDuration(mDuration);
michael@0 415 }
michael@0 416
michael@0 417 return NS_OK;
michael@0 418 }
michael@0 419
michael@0 420
michael@0 421 void
michael@0 422 AppleMP3Reader::AudioMetadataCallback(AudioFileStreamID aFileStream,
michael@0 423 AudioFileStreamPropertyID aPropertyID,
michael@0 424 UInt32 *aFlags)
michael@0 425 {
michael@0 426 if (aPropertyID == kAudioFileStreamProperty_ReadyToProducePackets) {
michael@0 427 /*
michael@0 428 * The parser is ready to send us packets of MP3 audio.
michael@0 429 *
michael@0 430 * We need to set the decoder up here, because if
michael@0 431 * |AudioFileStreamParseBytes| has enough audio data, then it will call
michael@0 432 * |AudioSampleCallback| before we get back to |ReadMetadata|.
michael@0 433 */
michael@0 434 SetupDecoder();
michael@0 435 mStreamReady = true;
michael@0 436 }
michael@0 437 }
michael@0 438
michael@0 439
michael@0 440 void
michael@0 441 AppleMP3Reader::SetupDecoder()
michael@0 442 {
michael@0 443 // Get input format description from demuxer
michael@0 444 AudioStreamBasicDescription inputFormat, outputFormat;
michael@0 445 GetProperty(mAudioFileStream, kAudioFileStreamProperty_DataFormat, &inputFormat);
michael@0 446
michael@0 447 memset(&outputFormat, 0, sizeof(outputFormat));
michael@0 448
michael@0 449 // Set output format
michael@0 450 #if defined(MOZ_SAMPLE_TYPE_FLOAT32)
michael@0 451 outputFormat.mBitsPerChannel = 32;
michael@0 452 outputFormat.mFormatFlags =
michael@0 453 kLinearPCMFormatFlagIsFloat |
michael@0 454 0;
michael@0 455 #else
michael@0 456 #error Unknown audio sample type
michael@0 457 #endif
michael@0 458
michael@0 459 mAudioSampleRate = outputFormat.mSampleRate = inputFormat.mSampleRate;
michael@0 460 mAudioChannels
michael@0 461 = outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
michael@0 462 mAudioFramesPerCompressedPacket = inputFormat.mFramesPerPacket;
michael@0 463
michael@0 464 outputFormat.mFormatID = kAudioFormatLinearPCM;
michael@0 465
michael@0 466 // Set up the decoder so it gives us one sample per frame; this way, it will
michael@0 467 // pass us all the samples it has in one go. Also makes it much easier to
michael@0 468 // deinterlace.
michael@0 469 outputFormat.mFramesPerPacket = 1;
michael@0 470 outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame
michael@0 471 = outputFormat.mChannelsPerFrame * outputFormat.mBitsPerChannel / 8;
michael@0 472
michael@0 473 OSStatus rv = AudioConverterNew(&inputFormat,
michael@0 474 &outputFormat,
michael@0 475 &mAudioConverter);
michael@0 476
michael@0 477 if (rv) {
michael@0 478 LOGE("Error constructing audio format converter: %x\n", rv);
michael@0 479 mAudioConverter = nullptr;
michael@0 480 return;
michael@0 481 }
michael@0 482 }
michael@0 483
michael@0 484
michael@0 485 nsresult
michael@0 486 AppleMP3Reader::Seek(int64_t aTime,
michael@0 487 int64_t aStartTime,
michael@0 488 int64_t aEndTime,
michael@0 489 int64_t aCurrentTime)
michael@0 490 {
michael@0 491 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
michael@0 492 NS_ASSERTION(aStartTime < aEndTime,
michael@0 493 "Seeking should happen over a positive range");
michael@0 494
michael@0 495 // Find the exact frame/packet that contains |aTime|.
michael@0 496 mCurrentAudioFrame = aTime * mAudioSampleRate / USECS_PER_S;
michael@0 497 SInt64 packet = mCurrentAudioFrame / mAudioFramesPerCompressedPacket;
michael@0 498
michael@0 499 // |AudioFileStreamSeek| will pass back through |byteOffset| the byte offset
michael@0 500 // into the stream it expects next time it reads.
michael@0 501 SInt64 byteOffset;
michael@0 502 UInt32 flags = 0;
michael@0 503
michael@0 504 OSStatus rv = AudioFileStreamSeek(mAudioFileStream,
michael@0 505 packet,
michael@0 506 &byteOffset,
michael@0 507 &flags);
michael@0 508
michael@0 509 if (rv) {
michael@0 510 LOGE("Couldn't seek demuxer. Error code %x\n", rv);
michael@0 511 return NS_ERROR_FAILURE;
michael@0 512 }
michael@0 513
michael@0 514 LOGD("computed byte offset = %lld; estimated = %s\n",
michael@0 515 byteOffset,
michael@0 516 (flags & kAudioFileStreamSeekFlag_OffsetIsEstimated) ? "YES" : "NO");
michael@0 517
michael@0 518 mDecoder->GetResource()->Seek(nsISeekableStream::NS_SEEK_SET, byteOffset);
michael@0 519
michael@0 520 ResetDecode();
michael@0 521
michael@0 522 return NS_OK;
michael@0 523 }
michael@0 524
michael@0 525 void
michael@0 526 AppleMP3Reader::NotifyDataArrived(const char* aBuffer,
michael@0 527 uint32_t aLength,
michael@0 528 int64_t aOffset)
michael@0 529 {
michael@0 530 MOZ_ASSERT(NS_IsMainThread());
michael@0 531 if (!mMP3FrameParser.NeedsData()) {
michael@0 532 return;
michael@0 533 }
michael@0 534
michael@0 535 mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
michael@0 536
michael@0 537 uint64_t duration = mMP3FrameParser.GetDuration();
michael@0 538 if (duration != mDuration) {
michael@0 539 LOGD("Updating media duration to %lluus\n", duration);
michael@0 540 mDuration = duration;
michael@0 541 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
michael@0 542 mDecoder->UpdateEstimatedMediaDuration(duration);
michael@0 543 }
michael@0 544 }
michael@0 545
michael@0 546 } // namespace mozilla

mercurial