Bug 480376 - Implement mozHasAudio to indicate when there's no audio track available. r=cpearce
authorPaul ADENOT <paul@paul.cx>
Sat, 28 Apr 2012 11:01:10 -0400
changeset 92661 b579fa03e84a0f29b997376deac3f5718581a78d
parent 92660 f2c2a457f9cc39fb9840001df597d474983738b9
child 92662 89cea655477a01765ddaf95eb5744a8d3f7b1c68
push id8805
push userryanvm@gmail.com
push dateSat, 28 Apr 2012 15:20:16 +0000
treeherdermozilla-inbound@4f0b589036e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs480376
milestone15.0a1
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 480376 - Implement mozHasAudio to indicate when there's no audio track available. r=cpearce
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLAudioElement.cpp
content/html/content/src/nsHTMLMediaElement.cpp
content/html/content/src/nsHTMLVideoElement.cpp
content/media/nsBuiltinDecoder.cpp
content/media/nsBuiltinDecoder.h
content/media/nsBuiltinDecoderStateMachine.cpp
content/media/test/Makefile.in
content/media/test/manifest.js
content/media/test/test_mozHasAudio.html
dom/interfaces/html/nsIDOMHTMLVideoElement.idl
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -127,17 +127,17 @@ public:
    * Call this to reevaluate whether we should start/stop due to our owner
    * document being active or inactive.
    */
   void NotifyOwnerDocumentActivityChanged();
 
   // Called by the video decoder object, on the main thread,
   // when it has read the metadata containing video dimensions,
   // etc.
-  void MetadataLoaded(PRUint32 aChannels, PRUint32 aRate);
+  void MetadataLoaded(PRUint32 aChannels, PRUint32 aRate, bool aHasAudio);
 
   // Called by the video decoder object, on the main thread,
   // when it has read the first frame of the video
   // aResourceFullyLoaded should be true if the resource has been
   // fully loaded and the caller will call ResourceLoaded next.
   void FirstFrameLoaded(bool aResourceFullyLoaded);
 
   // Called by the video decoder object, on the main thread,
@@ -789,11 +789,14 @@ protected:
   // stored in mPreloadURI.
   bool mLoadIsSuspended;
 
   // True if a same-origin check has been done for the media element and resource.
   bool mMediaSecurityVerified;
 
   // The CORS mode when loading the media element
   mozilla::CORSMode mCORSMode;
+
+  // True if the media has an audio track
+  bool mHasAudio;
 };
 
 #endif
--- a/content/html/content/src/nsHTMLAudioElement.cpp
+++ b/content/html/content/src/nsHTMLAudioElement.cpp
@@ -172,17 +172,17 @@ nsHTMLAudioElement::MozSetup(PRUint32 aC
   nsresult rv = mAudioStream->Init(aChannels, aRate,
                                    nsAudioStream::FORMAT_FLOAT32);
   if (NS_FAILED(rv)) {
     mAudioStream->Shutdown();
     mAudioStream = nsnull;
     return rv;
   }
 
-  MetadataLoaded(aChannels, aRate);
+  MetadataLoaded(aChannels, aRate, true);
   mAudioStream->SetVolume(mVolume);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLAudioElement::MozWriteAudio(const JS::Value& aData, JSContext* aCx, PRUint32* aRetVal)
 {
   if (!mAudioStream) {
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -1454,17 +1454,18 @@ nsHTMLMediaElement::nsHTMLMediaElement(a
     mHaveQueuedSelectResource(false),
     mSuspendedAfterFirstFrame(false),
     mAllowSuspendAfterFirstFrame(true),
     mHasPlayedOrSeeked(false),
     mHasSelfReference(false),
     mShuttingDown(false),
     mLoadIsSuspended(false),
     mMediaSecurityVerified(false),
-    mCORSMode(CORS_NONE)
+    mCORSMode(CORS_NONE),
+    mHasAudio(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaElementLog) {
     gMediaElementLog = PR_NewLogModule("nsMediaElement");
   }
   if (!gMediaElementEventsLog) {
     gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents");
   }
@@ -2253,20 +2254,21 @@ void nsHTMLMediaElement::ProcessMediaFra
     }
   }
   if (start > 0.0) {
     SetCurrentTime(start);
     mFragmentStart = start;
   }
 }
 
-void nsHTMLMediaElement::MetadataLoaded(PRUint32 aChannels, PRUint32 aRate)
+void nsHTMLMediaElement::MetadataLoaded(PRUint32 aChannels, PRUint32 aRate, bool aHasAudio)
 {
   mChannels = aChannels;
   mRate = aRate;
+  mHasAudio = aHasAudio;
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
   DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
   if (mDecoder && mDecoder->IsSeekable()) {
     ProcessMediaFragmentURI();
     mDecoder->SetEndTime(mFragmentEnd);
   }
 }
--- a/content/html/content/src/nsHTMLVideoElement.cpp
+++ b/content/html/content/src/nsHTMLVideoElement.cpp
@@ -214,8 +214,16 @@ NS_IMETHODIMP nsHTMLVideoElement::GetMoz
 }
 
 NS_IMETHODIMP nsHTMLVideoElement::GetMozFrameDelay(double *aMozFrameDelay) {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   VideoFrameContainer* container = GetVideoFrameContainer();
   *aMozFrameDelay = container ?  container->GetFrameDelay() : 0;
   return NS_OK;
 }
+
+
+/* readonly attribute bool mozHasAudio */
+NS_IMETHODIMP nsHTMLVideoElement::GetMozHasAudio(bool *aHasAudio) {
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  *aHasAudio = mHasAudio;
+  return NS_OK;
+}
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -403,17 +403,18 @@ void nsBuiltinDecoder::AudioAvailable(fl
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mShuttingDown || !mElement) {
     return;
   }
   mElement->NotifyAudioAvailable(frameBuffer.forget(), aFrameBufferLength, aTime);
 }
 
 void nsBuiltinDecoder::MetadataLoaded(PRUint32 aChannels,
-                                      PRUint32 aRate)
+                                      PRUint32 aRate,
+                                      bool aHasAudio)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mShuttingDown) {
     return;
   }
 
   // Only inform the element of MetadataLoaded if not doing a load() in order
   // to fulfill a seek, otherwise we'll get multiple metadataloaded events.
@@ -430,17 +431,17 @@ void nsBuiltinDecoder::MetadataLoaded(PR
   if (mDuration == -1) {
     SetInfinite(true);
   }
 
   if (mElement && notifyElement) {
     // Make sure the element and the frame (if any) are told about
     // our new size.
     Invalidate();
-    mElement->MetadataLoaded(aChannels, aRate);
+    mElement->MetadataLoaded(aChannels, aRate, aHasAudio);
   }
 
   if (!mResourceLoaded) {
     StartProgress();
   } else if (mElement) {
     // Resource was loaded during metadata loading, when progress
     // events are being ignored. Fire the final progress event.
     mElement->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -550,17 +550,18 @@ public:
   // Change to a new play state. This updates the mState variable and
   // notifies any thread blocking on this object's monitor of the
   // change. Call on the main thread only.
   void ChangeState(PlayState aState);
 
   // Called when the metadata from the media file has been read.
   // Call on the main thread only.
   void MetadataLoaded(PRUint32 aChannels,
-                      PRUint32 aRate);
+                      PRUint32 aRate,
+                      bool aHasAudio);
 
   // Called when the first frame has been loaded.
   // Call on the main thread only.
   void FirstFrameLoaded();
 
   // Called when the video has completed playing.
   // Call on the main thread only.
   void PlaybackEnded();
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -147,31 +147,33 @@ static PRInt64 DurationToUsecs(TimeDurat
 }
 
 class nsAudioMetadataEventRunner : public nsRunnable
 {
 private:
   nsCOMPtr<nsBuiltinDecoder> mDecoder;
 public:
   nsAudioMetadataEventRunner(nsBuiltinDecoder* aDecoder, PRUint32 aChannels,
-                             PRUint32 aRate) :
+                             PRUint32 aRate, bool aHasAudio) :
     mDecoder(aDecoder),
     mChannels(aChannels),
-    mRate(aRate)
+    mRate(aRate),
+    mHasAudio(aHasAudio)
   {
   }
 
   NS_IMETHOD Run()
   {
-    mDecoder->MetadataLoaded(mChannels, mRate);
+    mDecoder->MetadataLoaded(mChannels, mRate, mHasAudio);
     return NS_OK;
   }
 
   const PRUint32 mChannels;
   const PRUint32 mRate;
+  const bool mHasAudio;
 };
 
 // Owns the global state machine thread and counts of
 // state machine and decoder threads. There should
 // only be one instance of this class.
 class StateMachineTracker
 {
 private:
@@ -1510,17 +1512,17 @@ nsresult nsBuiltinDecoderStateMachine::D
     mEventManager.Init(mInfo.mAudioChannels, mInfo.mAudioRate);
     // Set the buffer length at the decoder level to be able, to be able
     // to retrive the value via media element method. The RequestFrameBufferLength
     // will call the nsBuiltinDecoderStateMachine::SetFrameBufferLength().
     PRUint32 frameBufferLength = mInfo.mAudioChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL;
     mDecoder->RequestFrameBufferLength(frameBufferLength);
   }
   nsCOMPtr<nsIRunnable> metadataLoadedEvent =
-    new nsAudioMetadataEventRunner(mDecoder, mInfo.mAudioChannels, mInfo.mAudioRate);
+    new nsAudioMetadataEventRunner(mDecoder, mInfo.mAudioChannels, mInfo.mAudioRate, HasAudio());
   NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
 
   if (mState == DECODER_STATE_DECODING_METADATA) {
     LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder.get()));
     StartDecoding();
   }
 
   if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED) &&
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -151,16 +151,17 @@ include $(topsrcdir)/config/rules.mk
 		test_source_write.html \
 		test_standalone.html \
 		test_timeupdate_small_files.html \
 		test_too_many_elements.html \
 		test_volume.html \
 		test_video_to_canvas.html \
 		use_large_cache.js \
 		test_audiowrite.html \
+		test_mozHasAudio.html \
 		$(NULL)
 
 # Don't run in suite
 ifndef MOZ_SUITE
 _TEST_FILES += test_play_twice.html
 else
 $(warning test_play_twice.html is disabled pending investigation. Bug 598252)
 endif
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -42,16 +42,25 @@ var gReplayTests = gSmallTests.concat([
 
 // Used by test_paused_after_ended. Need one test file per decoder backend, plus
 // anything for testing bugs that occur when replying a played file.
 var gPausedAfterEndedTests = gSmallTests.concat([
   { name:"r11025_u8_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"small-shot.ogg", type:"video/ogg", duration:0.276 }
 ]);
 
+// Test the mozHasAudio property
+var gMozHasAudioTests = [
+  { name:"big.wav", type:"audio/x-wav", duration:9.278981, size:102444, hasAudio:undefined },
+  { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233, size:28942, hasAudio:false },
+  { name:"short-video.ogv", type:"video/ogg", duration:1.081, hasAudio:true },
+  { name:"seek.webm", type:"video/webm", duration:3.966, size:215529, hasAudio:false },
+  { name:"bogus.duh", type:"bogus/duh" }
+];
+
 // These are files that we want to make sure we can play through.  We can
 // also check metadata.  Put files of the same type together in this list so if
 // something crashes we have some idea of which backend is responsible.
 // Used by test_playback, which expects no error event and one ended event.
 var gPlayTests = [
   // 8-bit samples
   { name:"r11025_u8_c1.wav", type:"audio/x-wav", duration:1.0 },
   // 8-bit samples, file is truncated
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_mozHasAudio.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test playback of media files that should play OK</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var manager = new MediaTestManager;
+
+function onloadedmetadata(e) {
+  var t = e.target;
+  is(t.mozHasAudio, t.hasAudio, "The element reports the wrong audio property." + t.token);
+  manager.finished(t.token);
+}
+
+function startTest(test, token) {
+  var elemType = /^audio/.test(test.type) ? "audio" : "video";
+  var element = document.createElement(elemType);
+
+  element.token = token;
+  manager.started(token);
+
+  element.src = test.name;
+  element.name = test.name;
+  element.hasAudio = test.hasAudio;
+  element.addEventListener("loadedmetadata", onloadedmetadata, false);
+
+  element.load();
+}
+
+manager.runTests(gMozHasAudioTests, startTest);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
@@ -43,17 +43,17 @@
  * <video> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#video
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[scriptable, uuid(E3763903-928B-47C4-AC3B-C47EB43884CA)]
+[scriptable, uuid(0a1c8b44-12a4-4316-b39f-feeee48475f8)]
 interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
 {
            attribute long width; 
            attribute long height;
   readonly attribute unsigned long videoWidth;
   readonly attribute unsigned long videoHeight;
            attribute DOMString poster;
            
@@ -70,10 +70,13 @@ interface nsIDOMHTMLVideoElement : nsIDO
   // pipeline. We may drop frames if they arrive late at the renderer.
   readonly attribute unsigned long mozPresentedFrames;
 
   // Number of presented frames which were painted on screen.
   readonly attribute unsigned long mozPaintedFrames;
 
   // Time which the last painted video frame was late by, in seconds.
   readonly attribute double mozFrameDelay;
+
+  // True if the video has an audio track available.
+  readonly attribute bool mozHasAudio;
 };