Bug 480058. Handle non-square Theora pixel-aspect-ratio property. r=doublec
--- 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));
}