content/media/apple/AppleMP3Reader.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/apple/AppleMP3Reader.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,546 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "AppleMP3Reader.h"
     1.9 +
    1.10 +#include "nsISeekableStream.h"
    1.11 +#include "MediaDecoder.h"
    1.12 +
    1.13 +// Number of bytes we will read and pass to the audio parser in each
    1.14 +// |DecodeAudioData| call.
    1.15 +#define AUDIO_READ_BYTES 4096
    1.16 +
    1.17 +// Maximum number of audio frames we will accept from the audio decoder in one
    1.18 +// go.  Carefully select this to work well with both the mp3 1152 max frames
    1.19 +// per block and power-of-2 allocation sizes.  Since we must pre-allocate the
    1.20 +// buffer we cannot use AudioCompactor without paying for an additional
    1.21 +// allocation and copy.  Therefore, choosing a value that divides exactly into
    1.22 +// 1152 is most memory efficient.
    1.23 +#define MAX_AUDIO_FRAMES 128
    1.24 +
    1.25 +namespace mozilla {
    1.26 +
    1.27 +#ifdef PR_LOGGING
    1.28 +extern PRLogModuleInfo* gMediaDecoderLog;
    1.29 +#define LOGE(...) PR_LOG(gMediaDecoderLog, PR_LOG_ERROR, (__VA_ARGS__))
    1.30 +#define LOGW(...) PR_LOG(gMediaDecoderLog, PR_LOG_WARNING, (__VA_ARGS__))
    1.31 +#define LOGD(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__))
    1.32 +#else
    1.33 +#define LOGE(...)
    1.34 +#define LOGW(...)
    1.35 +#define LOGD(...)
    1.36 +#endif
    1.37 +
    1.38 +#define PROPERTY_ID_FORMAT "%c%c%c%c"
    1.39 +#define PROPERTY_ID_PRINT(x) ((x) >> 24), \
    1.40 +                             ((x) >> 16) & 0xff, \
    1.41 +                             ((x) >> 8) & 0xff, \
    1.42 +                             (x) & 0xff
    1.43 +
    1.44 +AppleMP3Reader::AppleMP3Reader(AbstractMediaDecoder *aDecoder)
    1.45 +  : MediaDecoderReader(aDecoder)
    1.46 +  , mStreamReady(false)
    1.47 +  , mAudioFramesPerCompressedPacket(0)
    1.48 +  , mCurrentAudioFrame(0)
    1.49 +  , mAudioChannels(0)
    1.50 +  , mAudioSampleRate(0)
    1.51 +  , mAudioFileStream(nullptr)
    1.52 +  , mAudioConverter(nullptr)
    1.53 +  , mMP3FrameParser(mDecoder->GetResource()->GetLength())
    1.54 +{
    1.55 +  MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread");
    1.56 +}
    1.57 +
    1.58 +AppleMP3Reader::~AppleMP3Reader()
    1.59 +{
    1.60 +  MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread");
    1.61 +}
    1.62 +
    1.63 +
    1.64 +/*
    1.65 + * The Apple audio decoding APIs are very callback-happy. When the parser has
    1.66 + * some metadata, it will call back to here.
    1.67 + */
    1.68 +static void _AudioMetadataCallback(void *aThis,
    1.69 +                                   AudioFileStreamID aFileStream,
    1.70 +                                   AudioFileStreamPropertyID aPropertyID,
    1.71 +                                   UInt32 *aFlags)
    1.72 +{
    1.73 +  ((AppleMP3Reader*)aThis)->AudioMetadataCallback(aFileStream, aPropertyID,
    1.74 +                                                  aFlags);
    1.75 +}
    1.76 +
    1.77 +/*
    1.78 + * Similar to above, this is called when the parser has enough data to parse
    1.79 + * one or more samples.
    1.80 + */
    1.81 +static void _AudioSampleCallback(void *aThis,
    1.82 +                                 UInt32 aNumBytes, UInt32 aNumPackets,
    1.83 +                                 const void *aData,
    1.84 +                                 AudioStreamPacketDescription *aPackets)
    1.85 +{
    1.86 +  ((AppleMP3Reader*)aThis)->AudioSampleCallback(aNumBytes, aNumPackets,
    1.87 +                                                aData, aPackets);
    1.88 +}
    1.89 +
    1.90 +
    1.91 +/*
    1.92 + * If we're not at end of stream, read |aNumBytes| from the media resource,
    1.93 + * put it in |aData|, and return true.
    1.94 + * Otherwise, put as much data as is left into |aData|, set |aNumBytes| to the
    1.95 + * amount of data we have left, and return false.
    1.96 + */
    1.97 +nsresult
    1.98 +AppleMP3Reader::Read(uint32_t *aNumBytes, char *aData)
    1.99 +{
   1.100 +  MediaResource *resource = mDecoder->GetResource();
   1.101 +
   1.102 +  // Loop until we have all the data asked for, or we've reached EOS
   1.103 +  uint32_t totalBytes = 0;
   1.104 +  uint32_t numBytes;
   1.105 +  do {
   1.106 +    uint32_t bytesWanted = *aNumBytes - totalBytes;
   1.107 +    nsresult rv = resource->Read(aData + totalBytes, bytesWanted, &numBytes);
   1.108 +    totalBytes += numBytes;
   1.109 +
   1.110 +    if (NS_FAILED(rv)) {
   1.111 +      *aNumBytes = 0;
   1.112 +      return NS_ERROR_FAILURE;
   1.113 +    }
   1.114 +  } while(totalBytes < *aNumBytes && numBytes);
   1.115 +
   1.116 +  *aNumBytes = totalBytes;
   1.117 +
   1.118 +  // We will have read some data in the last iteration iff we filled the buffer.
   1.119 +  // XXX Maybe return a better value than NS_ERROR_FAILURE?
   1.120 +  return numBytes ? NS_OK : NS_ERROR_FAILURE;
   1.121 +}
   1.122 +
   1.123 +nsresult
   1.124 +AppleMP3Reader::Init(MediaDecoderReader* aCloneDonor)
   1.125 +{
   1.126 +  AudioFileTypeID fileType = kAudioFileMP3Type;
   1.127 +
   1.128 +  OSStatus rv = AudioFileStreamOpen(this,
   1.129 +                                    _AudioMetadataCallback,
   1.130 +                                    _AudioSampleCallback,
   1.131 +                                    fileType,
   1.132 +                                    &mAudioFileStream);
   1.133 +
   1.134 +  if (rv) {
   1.135 +    return NS_ERROR_FAILURE;
   1.136 +  }
   1.137 +
   1.138 +  return NS_OK;
   1.139 +}
   1.140 +
   1.141 +
   1.142 +struct PassthroughUserData {
   1.143 +  AppleMP3Reader *mReader;
   1.144 +  UInt32 mNumPackets;
   1.145 +  UInt32 mDataSize;
   1.146 +  const void *mData;
   1.147 +  AudioStreamPacketDescription *mPacketDesc;
   1.148 +  bool mDone;
   1.149 +};
   1.150 +
   1.151 +// Error value we pass through the decoder to signal that nothing has gone wrong
   1.152 +// during decoding, but more data is needed.
   1.153 +const UInt32 kNeedMoreData = 'MOAR';
   1.154 +
   1.155 +/*
   1.156 + * This function is called from |AudioConverterFillComplexBuffer|, which is
   1.157 + * called from |AudioSampleCallback| below, which in turn is called by
   1.158 + * |AudioFileStreamParseBytes|, which is called by |DecodeAudioData|.
   1.159 + *
   1.160 + * Mercifully, this is all synchronous.
   1.161 + *
   1.162 + * This callback is run when the AudioConverter (decoder) wants more MP3 packets
   1.163 + * to decode.
   1.164 + */
   1.165 +/* static */ OSStatus
   1.166 +AppleMP3Reader::PassthroughInputDataCallback(AudioConverterRef aAudioConverter,
   1.167 +                                             UInt32 *aNumDataPackets /* in/out */,
   1.168 +                                             AudioBufferList *aData /* in/out */,
   1.169 +                                             AudioStreamPacketDescription **aPacketDesc,
   1.170 +                                             void *aUserData)
   1.171 +{
   1.172 +  PassthroughUserData *userData = (PassthroughUserData *)aUserData;
   1.173 +  if (userData->mDone) {
   1.174 +    // We make sure this callback is run _once_, with all the data we received
   1.175 +    // from |AudioFileStreamParseBytes|. When we return an error, the decoder
   1.176 +    // simply passes the return value on to the calling method,
   1.177 +    // |AudioSampleCallback|; and flushes all of the audio frames it had
   1.178 +    // buffered. It does not change the decoder's state.
   1.179 +    LOGD("requested too much data; returning\n");
   1.180 +    *aNumDataPackets = 0;
   1.181 +    return kNeedMoreData;
   1.182 +  }
   1.183 +
   1.184 +  userData->mDone = true;
   1.185 +
   1.186 +  LOGD("AudioConverter wants %u packets of audio data\n", *aNumDataPackets);
   1.187 +
   1.188 +  *aNumDataPackets = userData->mNumPackets;
   1.189 +  *aPacketDesc = userData->mPacketDesc;
   1.190 +
   1.191 +  aData->mBuffers[0].mNumberChannels = userData->mReader->mAudioChannels;
   1.192 +  aData->mBuffers[0].mDataByteSize = userData->mDataSize;
   1.193 +  aData->mBuffers[0].mData = const_cast<void *>(userData->mData);
   1.194 +
   1.195 +  return 0;
   1.196 +}
   1.197 +
   1.198 +/*
   1.199 + * This callback is called when |AudioFileStreamParseBytes| has enough data to
   1.200 + * extract one or more MP3 packets.
   1.201 + */
   1.202 +void
   1.203 +AppleMP3Reader::AudioSampleCallback(UInt32 aNumBytes,
   1.204 +                                    UInt32 aNumPackets,
   1.205 +                                    const void *aData,
   1.206 +                                    AudioStreamPacketDescription *aPackets)
   1.207 +{
   1.208 +  LOGD("got %u bytes, %u packets\n", aNumBytes, aNumPackets);
   1.209 +
   1.210 +  // 1 frame per packet * num channels * 32-bit float
   1.211 +  uint32_t decodedSize = MAX_AUDIO_FRAMES * mAudioChannels *
   1.212 +                         sizeof(AudioDataValue);
   1.213 +
   1.214 +  // descriptions for _decompressed_ audio packets. ignored.
   1.215 +  nsAutoArrayPtr<AudioStreamPacketDescription>
   1.216 +    packets(new AudioStreamPacketDescription[MAX_AUDIO_FRAMES]);
   1.217 +
   1.218 +  // This API insists on having MP3 packets spoon-fed to it from a callback.
   1.219 +  // This structure exists only to pass our state and the result of the parser
   1.220 +  // on to the callback above.
   1.221 +  PassthroughUserData userData = { this, aNumPackets, aNumBytes, aData, aPackets, false };
   1.222 +
   1.223 +  do {
   1.224 +    // Decompressed audio buffer
   1.225 +    nsAutoArrayPtr<uint8_t> decoded(new uint8_t[decodedSize]);
   1.226 +
   1.227 +    AudioBufferList decBuffer;
   1.228 +    decBuffer.mNumberBuffers = 1;
   1.229 +    decBuffer.mBuffers[0].mNumberChannels = mAudioChannels;
   1.230 +    decBuffer.mBuffers[0].mDataByteSize = decodedSize;
   1.231 +    decBuffer.mBuffers[0].mData = decoded.get();
   1.232 +
   1.233 +    // in: the max number of packets we can handle from the decoder.
   1.234 +    // out: the number of packets the decoder is actually returning.
   1.235 +    UInt32 numFrames = MAX_AUDIO_FRAMES;
   1.236 +
   1.237 +    OSStatus rv = AudioConverterFillComplexBuffer(mAudioConverter,
   1.238 +                                                  PassthroughInputDataCallback,
   1.239 +                                                  &userData,
   1.240 +                                                  &numFrames /* in/out */,
   1.241 +                                                  &decBuffer,
   1.242 +                                                  packets.get());
   1.243 +
   1.244 +    if (rv && rv != kNeedMoreData) {
   1.245 +      LOGE("Error decoding audio stream: %x\n", rv);
   1.246 +      break;
   1.247 +    }
   1.248 +
   1.249 +    // If we decoded zero frames then AudiOConverterFillComplexBuffer is out
   1.250 +    // of data to provide.  We drained its internal buffer completely on the
   1.251 +    // last pass.
   1.252 +    if (numFrames == 0 && rv == kNeedMoreData) {
   1.253 +      LOGD("FillComplexBuffer out of data exactly\n");
   1.254 +      break;
   1.255 +    }
   1.256 +
   1.257 +    int64_t time = FramesToUsecs(mCurrentAudioFrame, mAudioSampleRate).value();
   1.258 +    int64_t duration = FramesToUsecs(numFrames, mAudioSampleRate).value();
   1.259 +
   1.260 +    LOGD("pushed audio at time %lfs; duration %lfs\n",
   1.261 +         (double)time / USECS_PER_S, (double)duration / USECS_PER_S);
   1.262 +
   1.263 +    AudioData *audio = new AudioData(mDecoder->GetResource()->Tell(),
   1.264 +                                     time, duration, numFrames,
   1.265 +                                     reinterpret_cast<AudioDataValue *>(decoded.forget()),
   1.266 +                                     mAudioChannels);
   1.267 +    mAudioQueue.Push(audio);
   1.268 +
   1.269 +    mCurrentAudioFrame += numFrames;
   1.270 +
   1.271 +    if (rv == kNeedMoreData) {
   1.272 +      // No error; we just need more data.
   1.273 +      LOGD("FillComplexBuffer out of data\n");
   1.274 +      break;
   1.275 +    }
   1.276 +  } while (true);
   1.277 +}
   1.278 +
   1.279 +bool
   1.280 +AppleMP3Reader::DecodeAudioData()
   1.281 +{
   1.282 +  MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
   1.283 +
   1.284 +  // Read AUDIO_READ_BYTES if we can
   1.285 +  char bytes[AUDIO_READ_BYTES];
   1.286 +  uint32_t numBytes = AUDIO_READ_BYTES;
   1.287 +
   1.288 +  nsresult readrv = Read(&numBytes, bytes);
   1.289 +
   1.290 +  // This function calls |AudioSampleCallback| above, synchronously, when it
   1.291 +  // finds compressed MP3 frame.
   1.292 +  OSStatus rv = AudioFileStreamParseBytes(mAudioFileStream,
   1.293 +                                          numBytes,
   1.294 +                                          bytes,
   1.295 +                                          0 /* flags */);
   1.296 +
   1.297 +  if (NS_FAILED(readrv)) {
   1.298 +    mAudioQueue.Finish();
   1.299 +    return false;
   1.300 +  }
   1.301 +
   1.302 +  // DataUnavailable just means there wasn't enough data to demux anything.
   1.303 +  // We should have more to push into the demuxer next time we're called.
   1.304 +  if (rv && rv != kAudioFileStreamError_DataUnavailable) {
   1.305 +    LOGE("AudioFileStreamParseBytes returned unknown error %x", rv);
   1.306 +    return false;
   1.307 +  }
   1.308 +
   1.309 +  return true;
   1.310 +}
   1.311 +
   1.312 +bool
   1.313 +AppleMP3Reader::DecodeVideoFrame(bool &aKeyframeSkip,
   1.314 +                                 int64_t aTimeThreshold)
   1.315 +{
   1.316 +  MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
   1.317 +  return false;
   1.318 +}
   1.319 +
   1.320 +
   1.321 +bool
   1.322 +AppleMP3Reader::HasAudio()
   1.323 +{
   1.324 +  MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
   1.325 +  return mStreamReady;
   1.326 +}
   1.327 +
   1.328 +bool
   1.329 +AppleMP3Reader::HasVideo()
   1.330 +{
   1.331 +  MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
   1.332 +  return false;
   1.333 +}
   1.334 +
   1.335 +
   1.336 +/*
   1.337 + * Query the MP3 parser for a piece of metadata.
   1.338 + */
   1.339 +static nsresult
   1.340 +GetProperty(AudioFileStreamID aAudioFileStream,
   1.341 +            AudioFileStreamPropertyID aPropertyID, void *aData)
   1.342 +{
   1.343 +  UInt32 size;
   1.344 +  Boolean writeable;
   1.345 +  OSStatus rv = AudioFileStreamGetPropertyInfo(aAudioFileStream, aPropertyID,
   1.346 +                                               &size, &writeable);
   1.347 +
   1.348 +  if (rv) {
   1.349 +    LOGW("Couldn't get property " PROPERTY_ID_FORMAT "\n",
   1.350 +         PROPERTY_ID_PRINT(aPropertyID));
   1.351 +    return NS_ERROR_FAILURE;
   1.352 +  }
   1.353 +
   1.354 +  rv = AudioFileStreamGetProperty(aAudioFileStream, aPropertyID,
   1.355 +                                  &size, aData);
   1.356 +
   1.357 +  return NS_OK;
   1.358 +}
   1.359 +
   1.360 +
   1.361 +nsresult
   1.362 +AppleMP3Reader::ReadMetadata(MediaInfo* aInfo,
   1.363 +                             MetadataTags** aTags)
   1.364 +{
   1.365 +  MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
   1.366 +
   1.367 +  *aTags = nullptr;
   1.368 +
   1.369 +  /*
   1.370 +   * Feed bytes into the parser until we have all the metadata we need to
   1.371 +   * set up the decoder. When the parser has enough data, it will
   1.372 +   * synchronously call back to |AudioMetadataCallback| below.
   1.373 +   */
   1.374 +  OSStatus rv;
   1.375 +  nsresult readrv;
   1.376 +  uint32_t offset = 0;
   1.377 +  do {
   1.378 +    char bytes[AUDIO_READ_BYTES];
   1.379 +    uint32_t numBytes = AUDIO_READ_BYTES;
   1.380 +    readrv = Read(&numBytes, bytes);
   1.381 +
   1.382 +    rv = AudioFileStreamParseBytes(mAudioFileStream,
   1.383 +                                   numBytes,
   1.384 +                                   bytes,
   1.385 +                                   0 /* flags */);
   1.386 +
   1.387 +    mMP3FrameParser.Parse(bytes, numBytes, offset);
   1.388 +
   1.389 +    offset += numBytes;
   1.390 +
   1.391 +    // We have to do our decoder setup from the callback. When it's done it will
   1.392 +    // set mStreamReady.
   1.393 +  } while (!mStreamReady && !rv && NS_SUCCEEDED(readrv));
   1.394 +
   1.395 +  if (rv) {
   1.396 +    LOGE("Error decoding audio stream metadata\n");
   1.397 +    return NS_ERROR_FAILURE;
   1.398 +  }
   1.399 +
   1.400 +  if (!mAudioConverter) {
   1.401 +    LOGE("Failed to setup the AudioToolbox audio decoder\n");
   1.402 +    return NS_ERROR_FAILURE;
   1.403 +  }
   1.404 +
   1.405 +  if (!mMP3FrameParser.IsMP3()) {
   1.406 +    LOGE("Frame parser failed to parse MP3 stream\n");
   1.407 +    return NS_ERROR_FAILURE;
   1.408 +  }
   1.409 +
   1.410 +  aInfo->mAudio.mRate = mAudioSampleRate;
   1.411 +  aInfo->mAudio.mChannels = mAudioChannels;
   1.412 +  aInfo->mAudio.mHasAudio = mStreamReady;
   1.413 +
   1.414 +  {
   1.415 +    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   1.416 +    mDuration = mMP3FrameParser.GetDuration();
   1.417 +    mDecoder->SetMediaDuration(mDuration);
   1.418 +  }
   1.419 +
   1.420 +  return NS_OK;
   1.421 +}
   1.422 +
   1.423 +
   1.424 +void
   1.425 +AppleMP3Reader::AudioMetadataCallback(AudioFileStreamID aFileStream,
   1.426 +                                      AudioFileStreamPropertyID aPropertyID,
   1.427 +                                      UInt32 *aFlags)
   1.428 +{
   1.429 +  if (aPropertyID == kAudioFileStreamProperty_ReadyToProducePackets) {
   1.430 +    /*
   1.431 +     * The parser is ready to send us packets of MP3 audio.
   1.432 +     *
   1.433 +     * We need to set the decoder up here, because if
   1.434 +     * |AudioFileStreamParseBytes| has enough audio data, then it will call
   1.435 +     * |AudioSampleCallback| before we get back to |ReadMetadata|.
   1.436 +     */
   1.437 +    SetupDecoder();
   1.438 +    mStreamReady = true;
   1.439 +  }
   1.440 +}
   1.441 +
   1.442 +
   1.443 +void
   1.444 +AppleMP3Reader::SetupDecoder()
   1.445 +{
   1.446 +  // Get input format description from demuxer
   1.447 +  AudioStreamBasicDescription inputFormat, outputFormat;
   1.448 +  GetProperty(mAudioFileStream, kAudioFileStreamProperty_DataFormat, &inputFormat);
   1.449 +
   1.450 +  memset(&outputFormat, 0, sizeof(outputFormat));
   1.451 +
   1.452 +  // Set output format
   1.453 +#if defined(MOZ_SAMPLE_TYPE_FLOAT32)
   1.454 +  outputFormat.mBitsPerChannel = 32;
   1.455 +  outputFormat.mFormatFlags =
   1.456 +    kLinearPCMFormatFlagIsFloat |
   1.457 +    0;
   1.458 +#else
   1.459 +#error Unknown audio sample type
   1.460 +#endif
   1.461 +
   1.462 +  mAudioSampleRate = outputFormat.mSampleRate = inputFormat.mSampleRate;
   1.463 +  mAudioChannels
   1.464 +    = outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
   1.465 +  mAudioFramesPerCompressedPacket = inputFormat.mFramesPerPacket;
   1.466 +
   1.467 +  outputFormat.mFormatID = kAudioFormatLinearPCM;
   1.468 +
   1.469 +  // Set up the decoder so it gives us one sample per frame; this way, it will
   1.470 +  // pass us all the samples it has in one go. Also makes it much easier to
   1.471 +  // deinterlace.
   1.472 +  outputFormat.mFramesPerPacket = 1;
   1.473 +  outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame
   1.474 +    = outputFormat.mChannelsPerFrame * outputFormat.mBitsPerChannel / 8;
   1.475 +
   1.476 +  OSStatus rv = AudioConverterNew(&inputFormat,
   1.477 +                                  &outputFormat,
   1.478 +                                  &mAudioConverter);
   1.479 +
   1.480 +  if (rv) {
   1.481 +    LOGE("Error constructing audio format converter: %x\n", rv);
   1.482 +    mAudioConverter = nullptr;
   1.483 +    return;
   1.484 +  }
   1.485 +}
   1.486 +
   1.487 +
   1.488 +nsresult
   1.489 +AppleMP3Reader::Seek(int64_t aTime,
   1.490 +                     int64_t aStartTime,
   1.491 +                     int64_t aEndTime,
   1.492 +                     int64_t aCurrentTime)
   1.493 +{
   1.494 +  MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
   1.495 +  NS_ASSERTION(aStartTime < aEndTime,
   1.496 +               "Seeking should happen over a positive range");
   1.497 +
   1.498 +  // Find the exact frame/packet that contains |aTime|.
   1.499 +  mCurrentAudioFrame = aTime * mAudioSampleRate / USECS_PER_S;
   1.500 +  SInt64 packet = mCurrentAudioFrame / mAudioFramesPerCompressedPacket;
   1.501 +
   1.502 +  // |AudioFileStreamSeek| will pass back through |byteOffset| the byte offset
   1.503 +  // into the stream it expects next time it reads.
   1.504 +  SInt64 byteOffset;
   1.505 +  UInt32 flags = 0;
   1.506 +
   1.507 +  OSStatus rv = AudioFileStreamSeek(mAudioFileStream,
   1.508 +                                    packet,
   1.509 +                                    &byteOffset,
   1.510 +                                    &flags);
   1.511 +
   1.512 +  if (rv) {
   1.513 +    LOGE("Couldn't seek demuxer. Error code %x\n", rv);
   1.514 +    return NS_ERROR_FAILURE;
   1.515 +  }
   1.516 +
   1.517 +  LOGD("computed byte offset = %lld; estimated = %s\n",
   1.518 +       byteOffset,
   1.519 +       (flags & kAudioFileStreamSeekFlag_OffsetIsEstimated) ? "YES" : "NO");
   1.520 +
   1.521 +  mDecoder->GetResource()->Seek(nsISeekableStream::NS_SEEK_SET, byteOffset);
   1.522 +
   1.523 +  ResetDecode();
   1.524 +
   1.525 +  return NS_OK;
   1.526 +}
   1.527 +
   1.528 +void
   1.529 +AppleMP3Reader::NotifyDataArrived(const char* aBuffer,
   1.530 +                                  uint32_t aLength,
   1.531 +                                  int64_t aOffset)
   1.532 +{
   1.533 +  MOZ_ASSERT(NS_IsMainThread());
   1.534 +  if (!mMP3FrameParser.NeedsData()) {
   1.535 +    return;
   1.536 +  }
   1.537 +
   1.538 +  mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
   1.539 +
   1.540 +  uint64_t duration = mMP3FrameParser.GetDuration();
   1.541 +  if (duration != mDuration) {
   1.542 +    LOGD("Updating media duration to %lluus\n", duration);
   1.543 +    mDuration = duration;
   1.544 +    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   1.545 +    mDecoder->UpdateEstimatedMediaDuration(duration);
   1.546 +  }
   1.547 +}
   1.548 +
   1.549 +} // namespace mozilla

mercurial