Bug 631058 - Part 2 - Only decode metadata for preload=metadata. r=cpearce, a=blocking-fennec
authorWes Johnston <wjohnston@mozilla.com>
Mon, 14 Feb 2011 17:01:03 -0800
changeset 62543 a8d213604ca686cd17a56cefa8997b1e5f872e9a
parent 62542 0a7448775fa0c2168bec4b9cefed8f91e74f93a2
child 62544 2e49069ac1adfe3ce988ba4eb14d52577ccee1b5
child 62545 6f2cd5629101e432a1c5f50c2a707491e9cb3b5b
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, blocking-fennec
bugs631058
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 631058 - Part 2 - Only decode metadata for preload=metadata. r=cpearce, a=blocking-fennec
content/base/test/file_mozfiledataurl_inner.html
content/base/test/test_mozfiledataurl.html
content/html/content/public/nsHTMLMediaElement.h
content/media/nsBuiltinDecoder.cpp
content/media/nsBuiltinDecoder.h
content/media/nsBuiltinDecoderStateMachine.cpp
--- a/content/base/test/file_mozfiledataurl_inner.html
+++ b/content/base/test/file_mozfiledataurl_inner.html
@@ -57,17 +57,17 @@ function iframeNotifyParent(e) {
 }
 
 onload = function() {
   img = document.getElementById('img');
   img.onerror = img.onload = imgNotifyParent;
   iframe = document.getElementById('iframe');
   iframe.onerror = iframe.onload = iframeNotifyParent;
   audio = document.getElementById('audio');
-  audio.onerror = audio.oncanplay = audioNotifyParent;
+  audio.onerror = audio.onloadedmetadata = audioNotifyParent;
 }
 
 </script>
 <body>
 <img id=img>
 <audio id=audio>
 <iframe id=iframe></iframe>
 </html>
--- a/content/base/test/test_mozfiledataurl.html
+++ b/content/base/test/test_mozfiledataurl.html
@@ -7,17 +7,17 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body onload="gen.next()">
 <p id="display">
 <iframe id=inner></iframe>
 <iframe id=iframe></iframe>
 <img id=img onload="gen.send(event);">
-<audio id=audio oncanplay="gen.send(event);">
+<audio id=audio onloadedmetadata="gen.send(event);">
 <input type=file id=fileList>
 </p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.8">
 
@@ -85,21 +85,21 @@ function runTest() {
   isnot(res.width, 120, "correct error width");
   isnot(res.height, 90, "correct error height");
 
   // Attempt to load an audio in this document
   var file = getFile("file_mozfiledataurl_audio.ogg");
   var fileurl = URL.createObjectURL(file);
   audio.src = fileurl;
   var e = (yield);
-  is(e.type, "canplay", "loaded successfully");
+  is(e.type, "loadedmetadata", "loaded successfully");
 
   // Revoke url and attempt to load a audio in this document
   audio.src = "file_mozfiledataurl_audio.ogg";
-  is((yield).type, "canplay", "successfully reset audio");
+  is((yield).type, "loadedmetadata", "successfully reset audio");
   URL.revokeObjectURL(fileurl);
   todo(false, "urls need to act like 404s, not fail to parse");
 /*  img.src = fileurl;
   var e = (yield);
   is(e.type, "error", "failed successfully");
   isnot(img.width, 120, "correct error width");
   isnot(img.height, 90, "correct error height");
 */
@@ -108,17 +108,17 @@ function runTest() {
   var fileurl = URL.createObjectURL(file);
   isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice");
 
   // Attempt to load an audio in a different same-origin document
   inner.src = innerSameSiteURI;
   yield;
   inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*");
   var res = (yield);
-  is(res.type, "canplay", "loaded successfully");
+  is(res.type, "loadedmetadata", "loaded successfully");
   
   // Attempt to load an audio in a different cross-origin document
   inner.src = innerCrossSiteURI;
   yield;
   inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*");
   var res = (yield);
   is(res.type, "error", "failed successfully");
 
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -323,16 +323,29 @@ public:
   already_AddRefed<nsILoadGroup> GetDocumentLoadGroup();
 
   /**
    * Returns PR_TRUE if the media has played or completed a seek.
    * Used by video frame to determine whether to paint the poster.
    */
   PRBool GetPlayedOrSeeked() const { return mHasPlayedOrSeeked; }
 
+  /**
+   * The preloading action to perform. These dictate how we react to the 
+   * preload attribute. See mPreloadAction.
+   */
+  enum PreloadAction {
+    PRELOAD_UNDEFINED = 0, // not determined - used only for initialization
+    PRELOAD_NONE = 1,      // do not preload
+    PRELOAD_METADATA = 2,  // preload only the metadata (and first frame)
+    PRELOAD_ENOUGH = 3     // preload enough data to allow uninterrupted
+                           // playback
+  };
+  PreloadAction GetPreloadAction() const { return mPreloadAction; }
+
   nsresult CopyInnerTo(nsGenericElement* aDest) const;
 
   /**
    * Sets the Accept header on the HTTP channel to the required
    * video or audio MIME types.
    */
   virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) = 0;
 
@@ -474,28 +487,16 @@ protected:
   enum PreloadAttrValue {
     PRELOAD_ATTR_EMPTY,    // set to ""
     PRELOAD_ATTR_NONE,     // set to "none"
     PRELOAD_ATTR_METADATA, // set to "metadata"
     PRELOAD_ATTR_AUTO      // set to "auto"
   };
 
   /**
-   * The preloading action to perform. These dictate how we react to the 
-   * preload attribute. See mPreloadAction.
-   */
-  enum PreloadAction {
-    PRELOAD_UNDEFINED = 0, // not determined - used only for initialization
-    PRELOAD_NONE = 1,      // do not preload
-    PRELOAD_METADATA = 2,  // preload only the metadata (and first frame)
-    PRELOAD_ENOUGH = 3     // preload enough data to allow uninterrupted
-                           // playback
-  };
-
-  /**
    * Suspends the load of resource at aURI, so that it can be resumed later
    * by ResumeLoad(). This is called when we have a media with a 'preload'
    * attribute value of 'none', during the resource selection algorithm.
    */
   void SuspendLoad(nsIURI* aURI);
 
   /**
    * Resumes a previously suspended load (suspended by SuspendLoad(uri)).
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -850,16 +850,20 @@ void nsBuiltinDecoder::Resume(PRBool aFo
     mStream->Resume();
   }
   if (aForceBuffering) {
     MonitorAutoEnter mon(mMonitor);
     mDecoderStateMachine->StartBuffering();
   }
 }
 
+nsHTMLMediaElement::PreloadAction nsBuiltinDecoder::GetPreloadAction() {
+  return mElement->GetPreloadAction();
+}
+
 void nsBuiltinDecoder::StopProgressUpdates()
 {
   NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread.");
   mIgnoreProgressData = PR_TRUE;
   if (mStream) {
     mStream->SetReadMode(nsMediaCacheStream::MODE_METADATA);
   }
 }
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -445,16 +445,19 @@ class nsBuiltinDecoder : public nsMediaD
     return mDecoderStateMachine->GetBuffered(aBuffered);
   }
 
   virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) {
     return mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset);
   }
 
  public:
+  // Return the preloadAction
+  nsHTMLMediaElement::PreloadAction GetPreloadAction();
+
   // Return the current state. Can be called on any thread. If called from
   // a non-main thread, the decoder monitor must be held.
   PlayState GetState() {
     return mPlayState;
   }
 
   // Stop updating the bytes downloaded for progress notifications. Called
   // when seeking to prevent wild changes to the progress notification.
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -982,21 +982,24 @@ nsresult nsBuiltinDecoderStateMachine::R
           continue;
         }
 
         VideoData* videoData = FindStartTime();
         if (videoData) {
           MonitorAutoExit exitMon(mDecoder->GetMonitor());
           RenderVideoFrame(videoData);
         }
-
-        // Start the decode threads, so that we can pre buffer the streams.
-        // and calculate the start time in order to determine the duration.
-        if (NS_FAILED(StartDecodeThreads())) {
-          continue;
+        if (mDecoder->GetPreloadAction() == nsHTMLMediaElement::PRELOAD_ENOUGH ||
+            mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING)
+        {
+          // Start the decode threads, so that we can pre buffer the streams
+          // and calculate the start time in order to determine the duration.
+          if (NS_FAILED(StartDecodeThreads())) {
+            continue;
+          }
         }
 
         NS_ASSERTION(mStartTime != -1, "Must have start time");
         NS_ASSERTION((!HasVideo() && !HasAudio()) ||
                      !mSeekable || mEndTime != -1,
                      "Active seekable media should have end time");
         NS_ASSERTION(!mSeekable || GetDuration() != -1, "Seekable media should have duration");
         LOG(PR_LOG_DEBUG, ("%p Media goes from %lldms to %lldms (duration %lldms) seekable=%d",
@@ -1018,22 +1021,46 @@ nsresult nsBuiltinDecoderStateMachine::R
         if (HasAudio()) {
           mEventManager.Init(info.mAudioChannels, info.mAudioRate);
           mDecoder->RequestFrameBufferLength(frameBufferLength);
         }
 
         if (mState == DECODER_STATE_DECODING_METADATA) {
           LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder));
           mState = DECODER_STATE_DECODING;
-        }
 
-        // Start playback.
-        if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
-          if (!IsPlaying()) {
-            StartPlayback();
+          // Start playback.
+          if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
+            if (!IsPlaying()) {
+              StartPlayback();
+            }
+          } else if (mDecoder->GetPreloadAction() != nsHTMLMediaElement::PRELOAD_ENOUGH) {
+            nsMediaStream* stream = mDecoder->GetCurrentStream();
+            if (mReader) {
+              // Clear any frames queued up during FindStartTime() and reset
+              // to the start of the stream  to save memory, particularly on mobile.
+              MonitorAutoExit exitMon(mDecoder->GetMonitor());
+              mReader->ResetDecode();
+              stream->Seek(nsISeekableStream::NS_SEEK_SET, mReader->GetInfo().mDataOffset);
+            }
+            // Note state can change when we release the decoder monitor to
+            // call ResetDecode() above, so we must re-verify the state here.
+            if (mState != DECODER_STATE_DECODING ||
+                mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING)
+            {
+              continue;
+            }
+
+            // Shutdown the state machine thread, in order to save
+            // memory on thread stacks, particuarly on Linux.
+            nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mDecoder->mStateMachineThread);
+            NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+            mDecoder->mStateMachineThread = nsnull;
+
+            return NS_OK;
           }
         }
       }
       break;
 
     case DECODER_STATE_DECODING:
       {
         if (NS_FAILED(StartDecodeThreads())) {