Bug 846612 - r=kinetik, a=lsblakk
authorPaul Adenot <paul@paul.cx>
Mon, 04 Mar 2013 15:48:58 +0100
changeset 132355 2677c3f3ebbf20e67b29d7fbb7e3e64447310254
parent 132354 1c57ac1209645bce73268dda9531695ef9c208e4
child 132356 ce0be969f374b87a6a4a0db809c8165f94ec91b7
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik, lsblakk
bugs846612
milestone21.0a2
Bug 846612 - r=kinetik, a=lsblakk
content/html/content/src/nsHTMLMediaElement.cpp
content/media/AudioStream.cpp
content/media/AudioStream.h
content/media/MediaDecoderStateMachine.cpp
content/media/test/crashtests/846612.html
content/media/test/crashtests/crashtests.list
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -3548,17 +3548,19 @@ NS_IMETHODIMP nsHTMLMediaElement::SetDef
 NS_IMETHODIMP nsHTMLMediaElement::GetPlaybackRate(double* aPlaybackRate)
 {
   *aPlaybackRate = mPlaybackRate;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLMediaElement::SetPlaybackRate(double aPlaybackRate)
 {
-  if (aPlaybackRate < 0) {
+  // Changing the playback rate of a media that has more than two channels is
+  // not supported.
+  if (aPlaybackRate < 0 || (mChannels > 2 && aPlaybackRate != 1.0)) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   mPlaybackRate = ClampPlaybackRate(aPlaybackRate);
 
   if (!mMuted) {
     if (mPlaybackRate < 0 ||
         mPlaybackRate > THRESHOLD_HIGH_PLAYBACKRATE_AUDIO ||
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -218,38 +218,47 @@ void AudioStream::ShutdownLibrary()
   }
 #endif
 }
 
 AudioStream::~AudioStream()
 {
 }
 
-void AudioStream::EnsureTimeStretcherInitialized()
+nsresult AudioStream::EnsureTimeStretcherInitialized()
 {
   if (!mTimeStretcher) {
+    // SoundTouch does not support a number of channels > 2
+    if (mChannels > 2) {
+      return NS_ERROR_FAILURE;
+    }
     mTimeStretcher = new soundtouch::SoundTouch();
     mTimeStretcher->setSampleRate(mInRate);
     mTimeStretcher->setChannels(mChannels);
     mTimeStretcher->setPitch(1.0);
   }
+  return NS_OK;
 }
 
 nsresult AudioStream::SetPlaybackRate(double aPlaybackRate)
 {
   NS_ASSERTION(aPlaybackRate > 0.0,
                "Can't handle negative or null playbackrate in the AudioStream.");
   // Avoid instantiating the resampler if we are not changing the playback rate.
   if (aPlaybackRate == mAudioClock.GetPlaybackRate()) {
     return NS_OK;
   }
+
+  if (EnsureTimeStretcherInitialized() != NS_OK) {
+    return NS_ERROR_FAILURE;
+  }
+
   mAudioClock.SetPlaybackRate(aPlaybackRate);
   mOutRate = mInRate / aPlaybackRate;
 
-  EnsureTimeStretcherInitialized();
 
   if (mAudioClock.GetPreservesPitch()) {
     mTimeStretcher->setTempo(aPlaybackRate);
     mTimeStretcher->setRate(1.0f);
   } else {
     mTimeStretcher->setTempo(1.0f);
     mTimeStretcher->setRate(aPlaybackRate);
   }
@@ -258,17 +267,19 @@ nsresult AudioStream::SetPlaybackRate(do
 
 nsresult AudioStream::SetPreservesPitch(bool aPreservesPitch)
 {
   // Avoid instantiating the timestretcher instance if not needed.
   if (aPreservesPitch == mAudioClock.GetPreservesPitch()) {
     return NS_OK;
   }
 
-  EnsureTimeStretcherInitialized();
+  if (EnsureTimeStretcherInitialized() != NS_OK) {
+    return NS_ERROR_FAILURE;
+  }
 
   if (aPreservesPitch == true) {
     mTimeStretcher->setTempo(mAudioClock.GetPlaybackRate());
     mTimeStretcher->setRate(1.0f);
   } else {
     mTimeStretcher->setTempo(1.0f);
     mTimeStretcher->setRate(mAudioClock.GetPlaybackRate());
   }
@@ -369,17 +380,19 @@ nsresult NativeAudioStream::Write(const 
 
   if (mInError)
     return NS_ERROR_FAILURE;
 
   uint32_t samples = aFrames * mChannels;
   int32_t written = -1;
 
   if (mInRate != mOutRate) {
-    EnsureTimeStretcherInitialized();
+    if (EnsureTimeStretcherInitialized() != NS_OK) {
+      return NS_ERROR_FAILURE;
+    }
     mTimeStretcher->putSamples(aBuf, aFrames);
     uint32_t numFrames = mTimeStretcher->numSamples();
     uint32_t arraySize = numFrames * mChannels * sizeof(AudioDataValue);
     nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[arraySize]);
     uint32_t framesAvailable = mTimeStretcher->receiveSamples(data, numFrames);
     NS_ASSERTION(mTimeStretcher->numSamples() == 0,
                  "We did not get all the data from the SoundTouch pipeline.");
     // It is possible to have nothing to write: the data are in the processing
@@ -618,17 +631,17 @@ class BufferedAudioStream : public Audio
   int64_t GetPosition();
   int64_t GetPositionInFrames();
   int64_t GetPositionInFramesInternal();
   bool IsPaused();
   int32_t GetMinWriteSize();
   // This method acquires the monitor and forward the call to the base
   // class, to prevent a race on |mTimeStretcher|, in
   // |AudioStream::EnsureTimeStretcherInitialized|.
-  void EnsureTimeStretcherInitialized();
+  nsresult EnsureTimeStretcherInitialized();
 
 private:
   static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
   {
     return static_cast<BufferedAudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
   }
 
   static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
@@ -719,21 +732,21 @@ BufferedAudioStream::BufferedAudioStream
 {
 }
 
 BufferedAudioStream::~BufferedAudioStream()
 {
   Shutdown();
 }
 
-void
+nsresult
 BufferedAudioStream::EnsureTimeStretcherInitialized()
 {
   MonitorAutoLock mon(mMonitor);
-  AudioStream::EnsureTimeStretcherInitialized();
+  return AudioStream::EnsureTimeStretcherInitialized();
 }
 
 nsresult
 BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
                             const dom::AudioChannelType aAudioChannelType)
 {
   cubeb* cubebContext = GetCubebContext();
 
@@ -1011,17 +1024,19 @@ BufferedAudioStream::GetUnprocessed(void
 }
 
 long
 BufferedAudioStream::GetTimeStretched(void* aBuffer, long aFrames)
 {
   long processedFrames = 0;
 
   // We need to call the non-locking version, because we already have the lock.
-  AudioStream::EnsureTimeStretcherInitialized();
+  if (AudioStream::EnsureTimeStretcherInitialized() != NS_OK) {
+    return 0;
+  }
 
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
   double playbackRate = static_cast<double>(mInRate) / mOutRate;
   uint32_t toPopBytes = FramesToBytes(ceil(aFrames / playbackRate));
   uint32_t available = 0;
   bool lowOnBufferedData = false;
   do {
     // Check if we already have enough data in the time stretcher pipeline.
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -164,17 +164,17 @@ public:
   // Returns the minimum number of audio frames which must be written before
   // you can be sure that something will be played.
   virtual int32_t GetMinWriteSize() = 0;
 
   int GetRate() { return mOutRate; }
   int GetChannels() { return mChannels; }
 
   // This should be called before attempting to use the time stretcher.
-  virtual void EnsureTimeStretcherInitialized();
+  virtual nsresult EnsureTimeStretcherInitialized();
   // Set playback rate as a multiple of the intrinsic playback rate. This is to
   // be called only with aPlaybackRate > 0.0.
   virtual nsresult SetPlaybackRate(double aPlaybackRate);
   // Switch between resampling (if false) and time stretching (if true, default).
   virtual nsresult SetPreservesPitch(bool aPreservesPitch);
 
 protected:
   // Input rate in Hz (characteristic of the media being played)
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -1005,21 +1005,25 @@ void MediaDecoderStateMachine::AudioLoop
 
   {
     // AudioStream initialization can block for extended periods in unusual
     // circumstances, so we take care to drop the decoder monitor while
     // initializing.
     nsAutoPtr<AudioStream> audioStream(AudioStream::AllocateStream());
     audioStream->Init(channels, rate, audioChannelType);
     audioStream->SetVolume(volume);
-    audioStream->SetPreservesPitch(preservesPitch);
+    if (audioStream->SetPreservesPitch(preservesPitch) != NS_OK) {
+      NS_WARNING("Setting the pitch preservation failed at AudioLoop start.");
+    }
     if (playbackRate != 1.0) {
       NS_ASSERTION(playbackRate != 0,
                    "Don't set the playbackRate to 0 on an AudioStream.");
-      audioStream->SetPlaybackRate(playbackRate);
+      if (audioStream->SetPlaybackRate(playbackRate) != NS_OK) {
+        NS_WARNING("Setting the playback rate failed at AudioLoop start.");
+      }
     }
 
     {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
       mAudioStream = audioStream;
     }
   }
 
@@ -1071,20 +1075,24 @@ void MediaDecoderStateMachine::AudioLoop
     }
 
     if (setVolume) {
       mAudioStream->SetVolume(volume);
     }
     if (setPlaybackRate) {
       NS_ASSERTION(playbackRate != 0,
                    "Don't set the playbackRate to 0 in the AudioStreams");
-      mAudioStream->SetPlaybackRate(playbackRate);
+      if (mAudioStream->SetPlaybackRate(playbackRate) != NS_OK) {
+        NS_WARNING("Setting the playback rate failed in AudioLoop.");
+      }
     }
     if (setPreservesPitch) {
-      mAudioStream->SetPreservesPitch(preservesPitch);
+      if (mAudioStream->SetPreservesPitch(preservesPitch) != NS_OK) {
+        NS_WARNING("Setting the pitch preservation failed in AudioLoop.");
+      }
     }
     if (minWriteFrames == -1) {
       minWriteFrames = mAudioStream->GetMinWriteSize();
     }
     NS_ASSERTION(mReader->AudioQueue().GetSize() > 0,
                  "Should have data to play");
     // See if there's a gap in the audio. If there is, push silence into the
     // audio hardware, so we can play across the gap.
@@ -2753,16 +2761,22 @@ void MediaDecoderStateMachine::NotifyAud
 
 void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ASSERTION(aPlaybackRate != 0,
       "PlaybackRate == 0 should be handled before this function.");
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
+  // We don't currently support more than two channels when changing playback
+  // rate.
+  if (mAudioStream && mAudioStream->GetChannels() > 2) {
+    return;
+  }
+
   if (mPlaybackRate == aPlaybackRate) {
     return;
   }
 
   // Get position of the last time we changed the rate.
   if (!HasAudio()) {
     // mBasePosition is a position in the video stream, not an absolute time.
     if (mState == DECODER_STATE_SEEKING) {
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/846612.html
@@ -0,0 +1,8 @@
+<script>
+document.addEventListener("DOMContentLoaded", function() {
+  var a = document.querySelector("audio");
+  a.playbackRate = 2;
+  a.play();
+});
+</script>
+<audio src="495794-1.ogg"></audio>
--- a/content/media/test/crashtests/crashtests.list
+++ b/content/media/test/crashtests/crashtests.list
@@ -7,8 +7,9 @@ HTTP load 481136-1.html # needs to be HT
 load 493915-1.html
 skip-if(Android) load 495794-1.html
 load 492286-1.xhtml
 load 576612-1.html
 skip-if(Android) load 691096-1.html # Android sound API can't handle playing large number of sounds at once.
 load 752784-1.html
 skip-if(Android||B2G) HTTP load 795892-1.html # load failed, bug 833371 for B2G
 skip-if(Android||B2G) load 789075-1.html # load failed, bug 833371 for B2G
+load 846612.html