Bug 823646 - Handle dynamic format changes and video frame display apperature attributes in WMFReader. r=padenot

This commit is contained in:
Chris Pearce 2013-01-21 14:38:47 +13:00
Родитель aac86acc69
Коммит 8edb1916a3
3 изменённых файлов: 138 добавлений и 36 удалений

Просмотреть файл

@ -100,14 +100,14 @@ bool IsWindows7OrLater()
return versionInfo.dwMajorVersion > 6 ||
(versionInfo.dwMajorVersion == 6 && versionInfo.dwMinorVersion >= 1);
}
/* static */
bool
WMFDecoder::IsEnabled()
{
// We only use WMF on Windows 7 and up, until we can properly test Vista
// and how it responds with and without the Platform Update installed.
return IsWindows7OrLater() &&
// We only use WMF on Windows 7 and up, until we can properly test Vista
// and how it responds with and without the Platform Update installed.
return IsWindows7OrLater() &&
Preferences::GetBool("media.windows-media-foundation.enabled");
}

Просмотреть файл

@ -34,7 +34,6 @@ WMFReader::WMFReader(MediaDecoder* aDecoder)
mAudioChannels(0),
mAudioBytesPerSample(0),
mAudioRate(0),
mVideoWidth(0),
mVideoHeight(0),
mVideoStride(0),
mHasAudio(false),
@ -234,6 +233,113 @@ GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
return hr;
}
static int32_t
MFOffsetToInt32(const MFOffset& aOffset)
{
return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
}
// Gets the sub-region of the video frame that should be displayed.
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
static HRESULT
GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion)
{
// Determine if "pan and scan" is enabled for this media. If it is, we
// only display a region of the video frame, not the entire frame.
BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);
// If pan and scan mode is enabled. Try to get the display region.
HRESULT hr = E_FAIL;
MFVideoArea videoArea;
memset(&videoArea, 0, sizeof(MFVideoArea));
if (panScan) {
hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE,
(UINT8*)&videoArea,
sizeof(MFVideoArea),
NULL);
}
// If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
// check for a minimimum display aperture.
if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
(UINT8*)&videoArea,
sizeof(MFVideoArea),
NULL);
}
if (hr == MF_E_ATTRIBUTENOTFOUND) {
// Minimum display aperture is not set, for "backward compatibility with
// some components", check for a geometric aperture.
hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
(UINT8*)&videoArea,
sizeof(MFVideoArea),
NULL);
}
if (SUCCEEDED(hr)) {
// The media specified a picture region, return it.
aOutPictureRegion = nsIntRect(MFOffsetToInt32(videoArea.OffsetX),
MFOffsetToInt32(videoArea.OffsetY),
videoArea.Area.cx,
videoArea.Area.cy);
return S_OK;
}
// No picture region defined, fall back to using the entire video area.
UINT32 width = 0, height = 0;
hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
aOutPictureRegion = nsIntRect(0, 0, width, height);
return S_OK;
}
HRESULT
WMFReader::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType)
{
NS_ENSURE_TRUE(aMediaType != nullptr, E_POINTER);
nsIntRect pictureRegion;
HRESULT hr = GetPictureRegion(aMediaType, pictureRegion);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
UINT32 width = 0, height = 0;
hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
uint32_t aspectNum = 0, aspectDenom = 0;
hr = MFGetAttributeRatio(aMediaType,
MF_MT_PIXEL_ASPECT_RATIO,
&aspectNum,
&aspectDenom);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Calculate and validate the picture region and frame dimensions after
// scaling by the pixel aspect ratio.
nsIntSize frameSize = nsIntSize(width, height);
nsIntSize displaySize = nsIntSize(pictureRegion.width, pictureRegion.height);
ScaleDisplayByAspectRatio(displaySize, float(aspectNum) / float(aspectDenom));
if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRegion, displaySize)) {
// Video track's frame sizes will overflow. Ignore the video track.
return E_FAIL;
}
// Success! Save state.
mInfo.mDisplay = displaySize;
GetDefaultStride(aMediaType, &mVideoStride);
mVideoHeight = height;
mPictureRegion = pictureRegion;
LOG("WMFReader frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d) display=(%d,%d) PAR=%d:%d",
width, height,
mVideoStride,
mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height,
displaySize.width, displaySize.height,
aspectNum, aspectDenom);
return S_OK;
}
void
WMFReader::ConfigureVideoDecoder()
{
@ -266,35 +372,11 @@ WMFReader::ConfigureVideoDecoder()
return;
}
if (FAILED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &mVideoWidth, &mVideoHeight))) {
NS_WARNING("WMF video decoder failed to get frame dimensions!");
if (FAILED(ConfigureVideoFrameGeometry(mediaType))) {
NS_WARNING("Failed configured video frame dimensions");
return;
}
LOG("Video frame %u x %u", mVideoWidth, mVideoHeight);
uint32_t aspectNum = 0, aspectDenom = 0;
if (FAILED(MFGetAttributeRatio(mediaType,
MF_MT_PIXEL_ASPECT_RATIO,
&aspectNum,
&aspectDenom))) {
NS_WARNING("WMF video decoder failed to get pixel aspect ratio!");
return;
}
LOG("Video aspect ratio %u x %u", aspectNum, aspectDenom);
GetDefaultStride(mediaType, &mVideoStride);
// Calculate and validate the frame size.
nsIntSize frameSize = nsIntSize(mVideoWidth, mVideoHeight);
nsIntRect pictureRegion = nsIntRect(0, 0, mVideoWidth, mVideoHeight);
nsIntSize displaySize = frameSize;
ScaleDisplayByAspectRatio(displaySize, float(aspectNum)/float(aspectDenom));
if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRegion, displaySize)) {
// Video track's frame sizes will overflow. Ignore the video track.
return;
}
mInfo.mDisplay = displaySize;
LOG("Successfully configured video stream");
mHasVideo = mInfo.mHasVideo = true;
@ -406,7 +488,10 @@ WMFReader::DecodeAudioData()
if (FAILED(hr) ||
(flags & MF_SOURCE_READERF_ERROR) ||
(flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
(flags & MF_SOURCE_READERF_ENDOFSTREAM) ||
(flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) {
LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x flags=0x%x",
hr, flags);
// End of stream.
mAudioQueue.Finish();
return false;
@ -491,6 +576,19 @@ WMFReader::DecodeVideoFrame(bool &aKeyframeSkip,
return true;
}
if ((flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) {
LOG("WMFReader: Video media type changed!");
RefPtr<IMFMediaType> mediaType;
hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
byRef(mediaType));
if (FAILED(hr) ||
FAILED(ConfigureVideoFrameGeometry(mediaType))) {
NS_WARNING("Failed to reconfigure video media type");
mVideoQueue.Finish();
return false;
}
}
int64_t timestamp = HNsToUsecs(timestampHns);
if (timestamp < aTimeThreshold) {
return true;
@ -570,7 +668,7 @@ WMFReader::DecodeVideoFrame(bool &aKeyframeSkip,
b,
false,
-1,
nsIntRect(0, 0, mVideoWidth, mVideoHeight));
mPictureRegion);
if (twoDBuffer) {
twoDBuffer->Unlock2D();
} else {
@ -586,8 +684,8 @@ WMFReader::DecodeVideoFrame(bool &aKeyframeSkip,
mVideoQueue.Push(v);
#ifdef LOG_SAMPLE_DECODE
LOG("Decoded video sample timestamp=%lld duration=%lld stride=%d width=%u height=%u flags=%u",
timestamp, duration, stride, mVideoWidth, mVideoHeight, flags);
LOG("Decoded video sample timestamp=%lld duration=%lld stride=%d height=%u flags=%u",
timestamp, duration, stride, mVideoHeight, flags);
#endif
if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) {

Просмотреть файл

@ -49,15 +49,19 @@ private:
void ConfigureAudioDecoder();
void ConfigureVideoDecoder();
HRESULT ConfigureVideoFrameGeometry(IMFMediaType* aMediaType);
RefPtr<IMFSourceReader> mSourceReader;
RefPtr<WMFByteStream> mByteStream;
// Region inside the video frame that makes up the picture. Pixels outside
// of this region should not be rendered.
nsIntRect mPictureRegion;
uint32_t mAudioChannels;
uint32_t mAudioBytesPerSample;
uint32_t mAudioRate;
uint32_t mVideoWidth;
uint32_t mVideoHeight;
uint32_t mVideoStride;