Bug 480058. Handle non-square Theora pixel-aspect-ratio property. r=doublec
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 19 May 2009 11:07:19 +1200
changeset 28572 12878454c5d57c4ce9bc7dc9b13e92960088718d
parent 28571 eb43d572dbb914d982c5e6f1407d8f3975a1c66a
child 28573 62acac67ad816aaf27cea2f1a95d95605bcbb7cf
push id7118
push userrocallahan@mozilla.com
push dateTue, 19 May 2009 10:02:10 +0000
treeherdermozilla-central@409416c625bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdoublec
bugs480058
milestone1.9.2a1pre
Bug 480058. Handle non-square Theora pixel-aspect-ratio property. r=doublec
content/media/video/public/nsMediaDecoder.h
content/media/video/src/nsMediaDecoder.cpp
content/media/video/src/nsOggDecoder.cpp
--- a/content/media/video/public/nsMediaDecoder.h
+++ b/content/media/video/public/nsMediaDecoder.h
@@ -239,22 +239,24 @@ public:
 protected:
 
   // Start timer to update download progress information.
   nsresult StartProgress();
 
   // Stop progress information timer.
   nsresult StopProgress();
 
-  // Set the RGB width, height and framerate. Ownership of the passed RGB
-  // buffer is transferred to the decoder.  This is the only nsMediaDecoder
-  // method that may be called from threads other than the main thread.
+  // Set the RGB width, height, pixel aspect ratio, and framerate.
+  // Ownership of the passed RGB buffer is transferred to the decoder.
+  // This is the only nsMediaDecoder method that may be called from
+  // threads other than the main thread.
   void SetRGBData(PRInt32 aWidth,
                   PRInt32 aHeight,
                   float aFramerate,
+                  float aAspectRatio,
                   unsigned char* aRGBBuffer);
 
 protected:
   // Timer used for updating progress events 
   nsCOMPtr<nsITimer> mProgressTimer;
 
   // The element is not reference counted. Instead the decoder is
   // notified when it is able to be used. It should only ever be
@@ -290,16 +292,19 @@ protected:
   // the video element does not use video data or sizes that are
   // in the midst of being changed.
   PRLock* mVideoUpdateLock;
 
   // Framerate of video being displayed in the element
   // expressed in numbers of frames per second.
   float mFramerate;
 
+  // Pixel aspect ratio (ratio of the pixel width to pixel height)
+  float mAspectRatio;
+
   // Has our size changed since the last repaint?
   PRPackedBool mSizeChanged;
 
   // True if the decoder is being shutdown. At this point all events that
   // are currently queued need to return immediately to prevent javascript
   // being run that operates on the element and decoder during shutdown.
   // Read/Write from the main thread only.
   PRPackedBool mShuttingDown;
--- a/content/media/video/src/nsMediaDecoder.cpp
+++ b/content/media/video/src/nsMediaDecoder.cpp
@@ -67,16 +67,17 @@ PRLogModuleInfo* gVideoDecoderLog = nsnu
 nsMediaDecoder::nsMediaDecoder() :
   mElement(0),
   mRGBWidth(-1),
   mRGBHeight(-1),
   mProgressTime(),
   mDataTime(),
   mVideoUpdateLock(nsnull),
   mFramerate(0.0),
+  mAspectRatio(1.0),
   mSizeChanged(PR_FALSE),
   mShuttingDown(PR_FALSE),
   mStopping(PR_FALSE)
 {
   MOZ_COUNT_CTOR(nsMediaDecoder);
 }
 
 nsMediaDecoder::~nsMediaDecoder()
@@ -109,27 +110,48 @@ nsHTMLMediaElement* nsMediaDecoder::GetM
 nsresult nsMediaDecoder::InitLogger() 
 {
 #ifdef PR_LOGGING
   gVideoDecoderLog = PR_NewLogModule("nsMediaDecoder");
 #endif
   return NS_OK;
 }
 
+static PRInt32 ConditionDimension(float aValue, PRInt32 aDefault)
+{
+  // This will exclude NaNs and infinities
+  if (aValue >= 1.0 && aValue <= 10000.0)
+    return PRInt32(NS_round(aValue));
+  return aDefault;
+}
+
 void nsMediaDecoder::Invalidate()
 {
   if (!mElement)
     return;
 
   nsIFrame* frame = mElement->GetPrimaryFrame();
   
   {
     nsAutoLock lock(mVideoUpdateLock);
     if (mSizeChanged) {
-      mElement->UpdateMediaSize(nsIntSize(mRGBWidth, mRGBHeight));
+      nsIntSize scaledSize(mRGBWidth, mRGBHeight);
+      // Apply the aspect ratio to produce the intrinsic size we report
+      // to the element.
+      if (mAspectRatio > 1.0) {
+        // Increase the intrinsic width
+        scaledSize.width =
+          ConditionDimension(mAspectRatio*scaledSize.width, scaledSize.width);
+      } else {
+        // Increase the intrinsic height
+        scaledSize.height =
+          ConditionDimension(scaledSize.height/mAspectRatio, scaledSize.height);
+      }
+      mElement->UpdateMediaSize(scaledSize);
+
       mSizeChanged = PR_FALSE;
       if (frame) {
         nsPresContext* presContext = frame->PresContext();      
         nsIPresShell *presShell = presContext->PresShell();
         presShell->FrameNeedsReflow(frame, 
                                     nsIPresShell::eStyleChange,
                                     NS_FRAME_IS_DIRTY);
       }
@@ -195,23 +217,26 @@ nsresult nsMediaDecoder::StopProgress()
     return NS_OK;
 
   nsresult rv = mProgressTimer->Cancel();
   mProgressTimer = nsnull;
 
   return rv;
 }
 
-void nsMediaDecoder::SetRGBData(PRInt32 aWidth, PRInt32 aHeight, float aFramerate, unsigned char* aRGBBuffer)
+void nsMediaDecoder::SetRGBData(PRInt32 aWidth, PRInt32 aHeight, float aFramerate,
+                                float aAspectRatio, unsigned char* aRGBBuffer)
 {
   nsAutoLock lock(mVideoUpdateLock);
 
-  if (mRGBWidth != aWidth || mRGBHeight != aHeight) {
+  if (mRGBWidth != aWidth || mRGBHeight != aHeight ||
+      mAspectRatio != aAspectRatio) {
     mRGBWidth = aWidth;
     mRGBHeight = aHeight;
+    mAspectRatio = aAspectRatio;
     mSizeChanged = PR_TRUE;
   }
   mFramerate = aFramerate;
   mRGB = aRGBBuffer;
 }
 
 void nsMediaDecoder::Paint(gfxContext* aContext,
                            gfxPattern::GraphicsFilter aFilter,
--- a/content/media/video/src/nsOggDecoder.cpp
+++ b/content/media/video/src/nsOggDecoder.cpp
@@ -468,16 +468,17 @@ private:
   // Number of seconds of data video/audio data held in a frame.
   // Accessed only via the decoder thread.
   float mCallbackPeriod;
 
   // Video data. These are initially set when the metadata is loaded.
   // They are only accessed from the decoder thread.
   PRInt32 mVideoTrack;
   float   mFramerate;
+  float   mAspectRatio;
 
   // Audio data. These are initially set when the metadata is loaded.
   // They are only accessed from the decoder thread.
   PRInt32 mAudioRate;
   PRInt32 mAudioChannels;
   PRInt32 mAudioTrack;
 
   // Time that buffering started. Used for buffering timeout and only
@@ -655,16 +656,17 @@ nsOggDecodeStateMachine::nsOggDecodeStat
   mPlayer(0),
   mPlayStartTime(),
   mPauseStartTime(),
   mPauseDuration(0),
   mPlaying(PR_FALSE),
   mCallbackPeriod(1.0),
   mVideoTrack(-1),
   mFramerate(0.0),
+  mAspectRatio(1.0),
   mAudioRate(0),
   mAudioChannels(0),
   mAudioTrack(-1),
   mBufferingStart(),
   mBufferingEndOffset(0),
   mLastFrameTime(0),
   mLastFramePosition(-1),
   mState(DECODER_STATE_DECODING_METADATA),
@@ -928,17 +930,18 @@ void nsOggDecodeStateMachine::PlayVideo(
 
     OggPlayRGBChannels rgb;
     rgb.ptro = buffer;
     rgb.rgb_width = aFrame->mVideoWidth;
     rgb.rgb_height = aFrame->mVideoHeight;
 
     oggplay_yuv2bgra(&yuv, &rgb);
 
-    mDecoder->SetRGBData(aFrame->mVideoWidth, aFrame->mVideoHeight, mFramerate, buffer.forget());
+    mDecoder->SetRGBData(aFrame->mVideoWidth, aFrame->mVideoHeight,
+                         mFramerate, mAspectRatio, buffer.forget());
   }
 }
 
 void nsOggDecodeStateMachine::PlayAudio(FrameData* aFrame)
 {
   //  NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "PlayAudio() called without acquiring decoder monitor");
   if (!mAudioStream)
     return;
@@ -1545,20 +1548,28 @@ void nsOggDecodeStateMachine::LoadOggHea
         mVideoTrack = i;
 
         int fpsd, fpsn;
         oggplay_get_video_fps(mPlayer, i, &fpsd, &fpsn);
         mFramerate = fpsd == 0 ? 0.0 : float(fpsn)/float(fpsd);
         mCallbackPeriod = 1.0 / mFramerate;
         LOG(PR_LOG_DEBUG, ("Frame rate: %f", mFramerate));
 
+        int aspectd, aspectn;
+        // this can return E_OGGPLAY_UNINITIALIZED if the video has
+        // no aspect ratio data. We assume 1.0 in that case.
+        OggPlayErrorCode r =
+          oggplay_get_video_aspect_ratio(mPlayer, i, &aspectd, &aspectn);
+        mAspectRatio = r == E_OGGPLAY_OK && aspectd > 0 ?
+            float(aspectn)/float(aspectd) : 1.0;
+
         int y_width;
         int y_height;
         oggplay_get_video_y_size(mPlayer, i, &y_width, &y_height);
-        mDecoder->SetRGBData(y_width, y_height, mFramerate, nsnull);
+        mDecoder->SetRGBData(y_width, y_height, mFramerate, mAspectRatio, nsnull);
       }
       else if (mAudioTrack == -1 && oggplay_get_track_type(mPlayer, i) == OGGZ_CONTENT_VORBIS) {
         mAudioTrack = i;
         oggplay_set_offset(mPlayer, i, OGGPLAY_AUDIO_OFFSET);
         oggplay_get_audio_samplerate(mPlayer, i, &mAudioRate);
         oggplay_get_audio_channels(mPlayer, i, &mAudioChannels);
         LOG(PR_LOG_DEBUG, ("samplerate: %d, channels: %d", mAudioRate, mAudioChannels));
       }