1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/wmf/WMFReader.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,917 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "WMFReader.h" 1.11 +#include "WMFDecoder.h" 1.12 +#include "WMFUtils.h" 1.13 +#include "WMFByteStream.h" 1.14 +#include "WMFSourceReaderCallback.h" 1.15 +#include "mozilla/ArrayUtils.h" 1.16 +#include "mozilla/dom/TimeRanges.h" 1.17 +#include "mozilla/dom/HTMLMediaElement.h" 1.18 +#include "mozilla/Preferences.h" 1.19 +#include "DXVA2Manager.h" 1.20 +#include "ImageContainer.h" 1.21 +#include "Layers.h" 1.22 +#include "mozilla/layers/LayersTypes.h" 1.23 + 1.24 +#ifndef MOZ_SAMPLE_TYPE_FLOAT32 1.25 +#error We expect 32bit float audio samples on desktop for the Windows Media Foundation media backend. 1.26 +#endif 1.27 + 1.28 +#include "MediaDecoder.h" 1.29 +#include "VideoUtils.h" 1.30 +#include "gfx2DGlue.h" 1.31 + 1.32 +using namespace mozilla::gfx; 1.33 +using mozilla::layers::Image; 1.34 +using mozilla::layers::LayerManager; 1.35 +using mozilla::layers::LayersBackend; 1.36 + 1.37 +namespace mozilla { 1.38 + 1.39 +#ifdef PR_LOGGING 1.40 +extern PRLogModuleInfo* gMediaDecoderLog; 1.41 +#define DECODER_LOG(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__)) 1.42 +#else 1.43 +#define DECODER_LOG(...) 1.44 +#endif 1.45 + 1.46 +// Uncomment to enable verbose per-sample logging. 1.47 +//#define LOG_SAMPLE_DECODE 1 1.48 + 1.49 +WMFReader::WMFReader(AbstractMediaDecoder* aDecoder) 1.50 + : MediaDecoderReader(aDecoder), 1.51 + mSourceReader(nullptr), 1.52 + mAudioChannels(0), 1.53 + mAudioBytesPerSample(0), 1.54 + mAudioRate(0), 1.55 + mVideoWidth(0), 1.56 + mVideoHeight(0), 1.57 + mVideoStride(0), 1.58 + mAudioFrameSum(0), 1.59 + mAudioFrameOffset(0), 1.60 + mHasAudio(false), 1.61 + mHasVideo(false), 1.62 + mUseHwAccel(false), 1.63 + mMustRecaptureAudioPosition(true), 1.64 + mIsMP3Enabled(WMFDecoder::IsMP3Supported()), 1.65 + mCOMInitialized(false) 1.66 +{ 1.67 + NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); 1.68 + MOZ_COUNT_CTOR(WMFReader); 1.69 +} 1.70 + 1.71 +WMFReader::~WMFReader() 1.72 +{ 1.73 + NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); 1.74 + 1.75 + // Note: We must shutdown the byte stream before calling MFShutdown, else we 1.76 + // get assertion failures when unlocking the byte stream's work queue. 1.77 + if (mByteStream) { 1.78 + DebugOnly<nsresult> rv = mByteStream->Shutdown(); 1.79 + NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown WMFByteStream"); 1.80 + } 1.81 + DebugOnly<HRESULT> hr = wmf::MFShutdown(); 1.82 + NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed"); 1.83 + MOZ_COUNT_DTOR(WMFReader); 1.84 +} 1.85 + 1.86 +bool 1.87 +WMFReader::InitializeDXVA() 1.88 +{ 1.89 + if (!Preferences::GetBool("media.windows-media-foundation.use-dxva", false)) { 1.90 + return false; 1.91 + } 1.92 + MOZ_ASSERT(mDecoder->GetImageContainer()); 1.93 + 1.94 + // Extract the layer manager backend type so that we can determine 1.95 + // whether it's worthwhile using DXVA. If we're not running with a D3D 1.96 + // layer manager then the readback of decoded video frames from GPU to 1.97 + // CPU memory grinds painting to a halt, and makes playback performance 1.98 + // *worse*. 1.99 + MediaDecoderOwner* owner = mDecoder->GetOwner(); 1.100 + NS_ENSURE_TRUE(owner, false); 1.101 + 1.102 + dom::HTMLMediaElement* element = owner->GetMediaElement(); 1.103 + NS_ENSURE_TRUE(element, false); 1.104 + 1.105 + nsRefPtr<LayerManager> layerManager = 1.106 + nsContentUtils::LayerManagerForDocument(element->OwnerDoc()); 1.107 + NS_ENSURE_TRUE(layerManager, false); 1.108 + 1.109 + LayersBackend backend = layerManager->GetCompositorBackendType(); 1.110 + if (backend != LayersBackend::LAYERS_D3D9 && 1.111 + backend != LayersBackend::LAYERS_D3D10 && 1.112 + backend != LayersBackend::LAYERS_D3D11) { 1.113 + return false; 1.114 + } 1.115 + 1.116 + mDXVA2Manager = DXVA2Manager::Create(); 1.117 + 1.118 + return mDXVA2Manager != nullptr; 1.119 +} 1.120 + 1.121 +nsresult 1.122 +WMFReader::Init(MediaDecoderReader* aCloneDonor) 1.123 +{ 1.124 + NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); 1.125 + 1.126 + nsresult rv = WMFDecoder::LoadDLLs(); 1.127 + NS_ENSURE_SUCCESS(rv, rv); 1.128 + 1.129 + if (FAILED(wmf::MFStartup())) { 1.130 + NS_WARNING("Failed to initialize Windows Media Foundation"); 1.131 + return NS_ERROR_FAILURE; 1.132 + } 1.133 + 1.134 + mSourceReaderCallback = new WMFSourceReaderCallback(); 1.135 + 1.136 + // Must be created on main thread. 1.137 + mByteStream = new WMFByteStream(mDecoder->GetResource(), mSourceReaderCallback); 1.138 + rv = mByteStream->Init(); 1.139 + NS_ENSURE_SUCCESS(rv, rv); 1.140 + 1.141 + if (mDecoder->GetImageContainer() != nullptr && 1.142 + IsVideoContentType(mDecoder->GetResource()->GetContentType())) { 1.143 + mUseHwAccel = InitializeDXVA(); 1.144 + } else { 1.145 + mUseHwAccel = false; 1.146 + } 1.147 + 1.148 + return NS_OK; 1.149 +} 1.150 + 1.151 +bool 1.152 +WMFReader::HasAudio() 1.153 +{ 1.154 + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); 1.155 + return mHasAudio; 1.156 +} 1.157 + 1.158 +bool 1.159 +WMFReader::HasVideo() 1.160 +{ 1.161 + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); 1.162 + return mHasVideo; 1.163 +} 1.164 + 1.165 +static HRESULT 1.166 +ConfigureSourceReaderStream(IMFSourceReader *aReader, 1.167 + const DWORD aStreamIndex, 1.168 + const GUID& aOutputSubType, 1.169 + const GUID* aAllowedInSubTypes, 1.170 + const uint32_t aNumAllowedInSubTypes) 1.171 +{ 1.172 + NS_ENSURE_TRUE(aReader, E_POINTER); 1.173 + NS_ENSURE_TRUE(aAllowedInSubTypes, E_POINTER); 1.174 + 1.175 + RefPtr<IMFMediaType> nativeType; 1.176 + RefPtr<IMFMediaType> type; 1.177 + HRESULT hr; 1.178 + 1.179 + // Find the native format of the stream. 1.180 + hr = aReader->GetNativeMediaType(aStreamIndex, 0, byRef(nativeType)); 1.181 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.182 + 1.183 + // Get the native output subtype of the stream. This denotes the uncompressed 1.184 + // type. 1.185 + GUID subType; 1.186 + hr = nativeType->GetGUID(MF_MT_SUBTYPE, &subType); 1.187 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.188 + 1.189 + // Ensure the input type of the media is in the allowed formats list. 1.190 + bool isSubTypeAllowed = false; 1.191 + for (uint32_t i = 0; i < aNumAllowedInSubTypes; i++) { 1.192 + if (aAllowedInSubTypes[i] == subType) { 1.193 + isSubTypeAllowed = true; 1.194 + break; 1.195 + } 1.196 + } 1.197 + if (!isSubTypeAllowed) { 1.198 + nsCString name = GetGUIDName(subType); 1.199 + DECODER_LOG("ConfigureSourceReaderStream subType=%s is not allowed to be decoded", name.get()); 1.200 + return E_FAIL; 1.201 + } 1.202 + 1.203 + // Find the major type. 1.204 + GUID majorType; 1.205 + hr = nativeType->GetGUID(MF_MT_MAJOR_TYPE, &majorType); 1.206 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.207 + 1.208 + // Define the output type. 1.209 + hr = wmf::MFCreateMediaType(byRef(type)); 1.210 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.211 + 1.212 + hr = type->SetGUID(MF_MT_MAJOR_TYPE, majorType); 1.213 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.214 + 1.215 + hr = type->SetGUID(MF_MT_SUBTYPE, aOutputSubType); 1.216 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.217 + 1.218 + // Set the uncompressed format. This can fail if the decoder can't produce 1.219 + // that type. 1.220 + return aReader->SetCurrentMediaType(aStreamIndex, nullptr, type); 1.221 +} 1.222 + 1.223 +// Returns the duration of the resource, in microseconds. 1.224 +HRESULT 1.225 +GetSourceReaderDuration(IMFSourceReader *aReader, 1.226 + int64_t& aOutDuration) 1.227 +{ 1.228 + AutoPropVar var; 1.229 + HRESULT hr = aReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, 1.230 + MF_PD_DURATION, 1.231 + &var); 1.232 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.233 + 1.234 + // WMF stores duration in hundred nanosecond units. 1.235 + int64_t duration_hns = 0; 1.236 + hr = wmf::PropVariantToInt64(var, &duration_hns); 1.237 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.238 + 1.239 + aOutDuration = HNsToUsecs(duration_hns); 1.240 + 1.241 + return S_OK; 1.242 +} 1.243 + 1.244 +HRESULT 1.245 +GetSourceReaderCanSeek(IMFSourceReader* aReader, bool& aOutCanSeek) 1.246 +{ 1.247 + NS_ENSURE_TRUE(aReader, E_FAIL); 1.248 + 1.249 + HRESULT hr; 1.250 + AutoPropVar var; 1.251 + hr = aReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, 1.252 + MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, 1.253 + &var); 1.254 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.255 + 1.256 + ULONG flags = 0; 1.257 + hr = wmf::PropVariantToUInt32(var, &flags); 1.258 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.259 + 1.260 + aOutCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK); 1.261 + 1.262 + return S_OK; 1.263 +} 1.264 + 1.265 +HRESULT 1.266 +WMFReader::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType) 1.267 +{ 1.268 + NS_ENSURE_TRUE(aMediaType != nullptr, E_POINTER); 1.269 + HRESULT hr; 1.270 + 1.271 + // Verify that the video subtype is what we expect it to be. 1.272 + // When using hardware acceleration/DXVA2 the video format should 1.273 + // be NV12, which is DXVA2's preferred format. For software decoding 1.274 + // we use YV12, as that's easier for us to stick into our rendering 1.275 + // pipeline than NV12. NV12 has interleaved UV samples, whereas YV12 1.276 + // is a planar format. 1.277 + GUID videoFormat; 1.278 + hr = aMediaType->GetGUID(MF_MT_SUBTYPE, &videoFormat); 1.279 + NS_ENSURE_TRUE(videoFormat == MFVideoFormat_NV12 || !mUseHwAccel, E_FAIL); 1.280 + NS_ENSURE_TRUE(videoFormat == MFVideoFormat_YV12 || mUseHwAccel, E_FAIL); 1.281 + 1.282 + nsIntRect pictureRegion; 1.283 + hr = GetPictureRegion(aMediaType, pictureRegion); 1.284 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.285 + 1.286 + UINT32 width = 0, height = 0; 1.287 + hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height); 1.288 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.289 + 1.290 + uint32_t aspectNum = 0, aspectDenom = 0; 1.291 + hr = MFGetAttributeRatio(aMediaType, 1.292 + MF_MT_PIXEL_ASPECT_RATIO, 1.293 + &aspectNum, 1.294 + &aspectDenom); 1.295 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.296 + 1.297 + // Calculate and validate the picture region and frame dimensions after 1.298 + // scaling by the pixel aspect ratio. 1.299 + nsIntSize frameSize = nsIntSize(width, height); 1.300 + nsIntSize displaySize = nsIntSize(pictureRegion.width, pictureRegion.height); 1.301 + ScaleDisplayByAspectRatio(displaySize, float(aspectNum) / float(aspectDenom)); 1.302 + if (!IsValidVideoRegion(frameSize, pictureRegion, displaySize)) { 1.303 + // Video track's frame sizes will overflow. Ignore the video track. 1.304 + return E_FAIL; 1.305 + } 1.306 + 1.307 + // Success! Save state. 1.308 + mInfo.mVideo.mDisplay = displaySize; 1.309 + GetDefaultStride(aMediaType, &mVideoStride); 1.310 + mVideoWidth = width; 1.311 + mVideoHeight = height; 1.312 + mPictureRegion = pictureRegion; 1.313 + 1.314 + DECODER_LOG("WMFReader frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d) display=(%d,%d) PAR=%d:%d", 1.315 + width, height, 1.316 + mVideoStride, 1.317 + mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height, 1.318 + displaySize.width, displaySize.height, 1.319 + aspectNum, aspectDenom); 1.320 + 1.321 + return S_OK; 1.322 +} 1.323 + 1.324 +HRESULT 1.325 +WMFReader::ConfigureVideoDecoder() 1.326 +{ 1.327 + NS_ASSERTION(mSourceReader, "Must have a SourceReader before configuring decoders!"); 1.328 + 1.329 + // Determine if we have video. 1.330 + if (!mSourceReader || 1.331 + !SourceReaderHasStream(mSourceReader, MF_SOURCE_READER_FIRST_VIDEO_STREAM)) { 1.332 + // No stream, no error. 1.333 + return S_OK; 1.334 + } 1.335 + 1.336 + if (!mDecoder->GetImageContainer()) { 1.337 + // We can't display the video, so don't bother to decode; disable the stream. 1.338 + return mSourceReader->SetStreamSelection(MF_SOURCE_READER_FIRST_VIDEO_STREAM, FALSE); 1.339 + } 1.340 + 1.341 + static const GUID MP4VideoTypes[] = { 1.342 + MFVideoFormat_H264 1.343 + }; 1.344 + HRESULT hr = ConfigureSourceReaderStream(mSourceReader, 1.345 + MF_SOURCE_READER_FIRST_VIDEO_STREAM, 1.346 + mUseHwAccel ? MFVideoFormat_NV12 : MFVideoFormat_YV12, 1.347 + MP4VideoTypes, 1.348 + ArrayLength(MP4VideoTypes)); 1.349 + if (FAILED(hr)) { 1.350 + DECODER_LOG("Failed to configured video output"); 1.351 + return hr; 1.352 + } 1.353 + 1.354 + RefPtr<IMFMediaType> mediaType; 1.355 + hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 1.356 + byRef(mediaType)); 1.357 + if (FAILED(hr)) { 1.358 + NS_WARNING("Failed to get configured video media type"); 1.359 + return hr; 1.360 + } 1.361 + 1.362 + if (FAILED(ConfigureVideoFrameGeometry(mediaType))) { 1.363 + NS_WARNING("Failed configured video frame dimensions"); 1.364 + return hr; 1.365 + } 1.366 + 1.367 + DECODER_LOG("Successfully configured video stream"); 1.368 + 1.369 + mHasVideo = mInfo.mVideo.mHasVideo = true; 1.370 + 1.371 + return S_OK; 1.372 +} 1.373 + 1.374 +void 1.375 +WMFReader::GetSupportedAudioCodecs(const GUID** aCodecs, uint32_t* aNumCodecs) 1.376 +{ 1.377 + MOZ_ASSERT(aCodecs); 1.378 + MOZ_ASSERT(aNumCodecs); 1.379 + 1.380 + if (mIsMP3Enabled) { 1.381 + GUID aacOrMp3 = MFMPEG4Format_Base; 1.382 + aacOrMp3.Data1 = 0x6D703461;// FOURCC('m','p','4','a'); 1.383 + static const GUID codecs[] = { 1.384 + MFAudioFormat_AAC, 1.385 + MFAudioFormat_MP3, 1.386 + aacOrMp3 1.387 + }; 1.388 + *aCodecs = codecs; 1.389 + *aNumCodecs = ArrayLength(codecs); 1.390 + } else { 1.391 + static const GUID codecs[] = { 1.392 + MFAudioFormat_AAC 1.393 + }; 1.394 + *aCodecs = codecs; 1.395 + *aNumCodecs = ArrayLength(codecs); 1.396 + } 1.397 +} 1.398 + 1.399 +HRESULT 1.400 +WMFReader::ConfigureAudioDecoder() 1.401 +{ 1.402 + NS_ASSERTION(mSourceReader, "Must have a SourceReader before configuring decoders!"); 1.403 + 1.404 + if (!mSourceReader || 1.405 + !SourceReaderHasStream(mSourceReader, MF_SOURCE_READER_FIRST_AUDIO_STREAM)) { 1.406 + // No stream, no error. 1.407 + return S_OK; 1.408 + } 1.409 + 1.410 + const GUID* codecs; 1.411 + uint32_t numCodecs = 0; 1.412 + GetSupportedAudioCodecs(&codecs, &numCodecs); 1.413 + 1.414 + HRESULT hr = ConfigureSourceReaderStream(mSourceReader, 1.415 + MF_SOURCE_READER_FIRST_AUDIO_STREAM, 1.416 + MFAudioFormat_Float, 1.417 + codecs, 1.418 + numCodecs); 1.419 + if (FAILED(hr)) { 1.420 + NS_WARNING("Failed to configure WMF Audio decoder for PCM output"); 1.421 + return hr; 1.422 + } 1.423 + 1.424 + RefPtr<IMFMediaType> mediaType; 1.425 + hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 1.426 + byRef(mediaType)); 1.427 + if (FAILED(hr)) { 1.428 + NS_WARNING("Failed to get configured audio media type"); 1.429 + return hr; 1.430 + } 1.431 + 1.432 + mAudioRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0); 1.433 + mAudioChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0); 1.434 + mAudioBytesPerSample = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16) / 8; 1.435 + 1.436 + mInfo.mAudio.mChannels = mAudioChannels; 1.437 + mInfo.mAudio.mRate = mAudioRate; 1.438 + mHasAudio = mInfo.mAudio.mHasAudio = true; 1.439 + 1.440 + DECODER_LOG("Successfully configured audio stream. rate=%u channels=%u bitsPerSample=%u", 1.441 + mAudioRate, mAudioChannels, mAudioBytesPerSample); 1.442 + 1.443 + return S_OK; 1.444 +} 1.445 + 1.446 +HRESULT 1.447 +WMFReader::CreateSourceReader() 1.448 +{ 1.449 + HRESULT hr; 1.450 + 1.451 + RefPtr<IMFAttributes> attr; 1.452 + hr = wmf::MFCreateAttributes(byRef(attr), 1); 1.453 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.454 + 1.455 + hr = attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, mSourceReaderCallback); 1.456 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.457 + 1.458 + if (mUseHwAccel) { 1.459 + hr = attr->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, 1.460 + mDXVA2Manager->GetDXVADeviceManager()); 1.461 + if (FAILED(hr)) { 1.462 + DECODER_LOG("Failed to set DXVA2 D3D Device manager on source reader attributes"); 1.463 + mUseHwAccel = false; 1.464 + } 1.465 + } 1.466 + 1.467 + hr = wmf::MFCreateSourceReaderFromByteStream(mByteStream, attr, byRef(mSourceReader)); 1.468 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.469 + 1.470 + hr = ConfigureVideoDecoder(); 1.471 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.472 + 1.473 + hr = ConfigureAudioDecoder(); 1.474 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.475 + 1.476 + if (mUseHwAccel && mInfo.mVideo.mHasVideo) { 1.477 + RefPtr<IMFTransform> videoDecoder; 1.478 + hr = mSourceReader->GetServiceForStream(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 1.479 + GUID_NULL, 1.480 + IID_IMFTransform, 1.481 + (void**)(IMFTransform**)(byRef(videoDecoder))); 1.482 + 1.483 + if (SUCCEEDED(hr)) { 1.484 + ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager()); 1.485 + hr = videoDecoder->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, 1.486 + manager); 1.487 + if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { 1.488 + // Ignore MF_E_TRANSFORM_TYPE_NOT_SET. Vista returns this here 1.489 + // on some, perhaps all, video cards. This may be because activating 1.490 + // DXVA changes the available output types. It seems to be safe to 1.491 + // ignore this error. 1.492 + hr = S_OK; 1.493 + } 1.494 + } 1.495 + if (FAILED(hr)) { 1.496 + DECODER_LOG("Failed to set DXVA2 D3D Device manager on decoder hr=0x%x", hr); 1.497 + mUseHwAccel = false; 1.498 + } 1.499 + } 1.500 + return hr; 1.501 +} 1.502 + 1.503 +nsresult 1.504 +WMFReader::ReadMetadata(MediaInfo* aInfo, 1.505 + MetadataTags** aTags) 1.506 +{ 1.507 + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); 1.508 + 1.509 + DECODER_LOG("WMFReader::ReadMetadata()"); 1.510 + HRESULT hr; 1.511 + 1.512 + const bool triedToInitDXVA = mUseHwAccel; 1.513 + if (FAILED(CreateSourceReader())) { 1.514 + mSourceReader = nullptr; 1.515 + if (triedToInitDXVA && !mUseHwAccel) { 1.516 + // We tried to initialize DXVA and failed. Try again to create the 1.517 + // IMFSourceReader but this time we won't use DXVA. Note that we 1.518 + // must recreate the IMFSourceReader from scratch, as on some systems 1.519 + // (AMD Radeon 3000) we cannot successfully reconfigure an existing 1.520 + // reader to not use DXVA after we've failed to configure DXVA. 1.521 + // See bug 987127. 1.522 + if (FAILED(CreateSourceReader())) { 1.523 + mSourceReader = nullptr; 1.524 + } 1.525 + } 1.526 + } 1.527 + 1.528 + if (!mSourceReader) { 1.529 + NS_WARNING("Failed to create IMFSourceReader"); 1.530 + return NS_ERROR_FAILURE; 1.531 + } 1.532 + 1.533 + if (mInfo.HasVideo()) { 1.534 + DECODER_LOG("Using DXVA: %s", (mUseHwAccel ? "Yes" : "No")); 1.535 + } 1.536 + 1.537 + // Abort if both video and audio failed to initialize. 1.538 + NS_ENSURE_TRUE(mInfo.HasValidMedia(), NS_ERROR_FAILURE); 1.539 + 1.540 + // Get the duration, and report it to the decoder if we have it. 1.541 + int64_t duration = 0; 1.542 + hr = GetSourceReaderDuration(mSourceReader, duration); 1.543 + if (SUCCEEDED(hr)) { 1.544 + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); 1.545 + mDecoder->SetMediaEndTime(duration); 1.546 + } 1.547 + // We can seek if we get a duration *and* the reader reports that it's 1.548 + // seekable. 1.549 + bool canSeek = false; 1.550 + if (FAILED(hr) || 1.551 + FAILED(GetSourceReaderCanSeek(mSourceReader, canSeek)) || 1.552 + !canSeek) { 1.553 + mDecoder->SetMediaSeekable(false); 1.554 + } 1.555 + 1.556 + *aInfo = mInfo; 1.557 + *aTags = nullptr; 1.558 + // aTags can be retrieved using techniques like used here: 1.559 + // http://blogs.msdn.com/b/mf/archive/2010/01/12/mfmediapropdump.aspx 1.560 + 1.561 + return NS_OK; 1.562 +} 1.563 + 1.564 +bool 1.565 +WMFReader::DecodeAudioData() 1.566 +{ 1.567 + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); 1.568 + 1.569 + HRESULT hr; 1.570 + hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 1.571 + 0, // control flags 1.572 + 0, // read stream index 1.573 + nullptr, 1.574 + nullptr, 1.575 + nullptr); 1.576 + 1.577 + if (FAILED(hr)) { 1.578 + DECODER_LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x", hr); 1.579 + // End the stream. 1.580 + return false; 1.581 + } 1.582 + 1.583 + DWORD flags = 0; 1.584 + LONGLONG timestampHns = 0; 1.585 + RefPtr<IMFSample> sample; 1.586 + hr = mSourceReaderCallback->Wait(&flags, ×tampHns, byRef(sample)); 1.587 + if (FAILED(hr) || 1.588 + (flags & MF_SOURCE_READERF_ERROR) || 1.589 + (flags & MF_SOURCE_READERF_ENDOFSTREAM) || 1.590 + (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) { 1.591 + DECODER_LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x flags=0x%x", 1.592 + hr, flags); 1.593 + // End the stream. 1.594 + return false; 1.595 + } 1.596 + 1.597 + if (!sample) { 1.598 + // Not enough data? Try again... 1.599 + return true; 1.600 + } 1.601 + 1.602 + RefPtr<IMFMediaBuffer> buffer; 1.603 + hr = sample->ConvertToContiguousBuffer(byRef(buffer)); 1.604 + NS_ENSURE_TRUE(SUCCEEDED(hr), false); 1.605 + 1.606 + BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it. 1.607 + DWORD maxLength = 0, currentLength = 0; 1.608 + hr = buffer->Lock(&data, &maxLength, ¤tLength); 1.609 + NS_ENSURE_TRUE(SUCCEEDED(hr), false); 1.610 + 1.611 + uint32_t numFrames = currentLength / mAudioBytesPerSample / mAudioChannels; 1.612 + NS_ASSERTION(sizeof(AudioDataValue) == mAudioBytesPerSample, "Size calculation is wrong"); 1.613 + nsAutoArrayPtr<AudioDataValue> pcmSamples(new AudioDataValue[numFrames * mAudioChannels]); 1.614 + memcpy(pcmSamples.get(), data, currentLength); 1.615 + buffer->Unlock(); 1.616 + 1.617 + // We calculate the timestamp and the duration based on the number of audio 1.618 + // frames we've already played. We don't trust the timestamp stored on the 1.619 + // IMFSample, as sometimes it's wrong, possibly due to buggy encoders? 1.620 + 1.621 + // If this sample block comes after a discontinuity (i.e. a gap or seek) 1.622 + // reset the frame counters, and capture the timestamp. Future timestamps 1.623 + // will be offset from this block's timestamp. 1.624 + UINT32 discontinuity = false; 1.625 + sample->GetUINT32(MFSampleExtension_Discontinuity, &discontinuity); 1.626 + if (mMustRecaptureAudioPosition || discontinuity) { 1.627 + mAudioFrameSum = 0; 1.628 + hr = HNsToFrames(timestampHns, mAudioRate, &mAudioFrameOffset); 1.629 + NS_ENSURE_TRUE(SUCCEEDED(hr), false); 1.630 + mMustRecaptureAudioPosition = false; 1.631 + } 1.632 + 1.633 + int64_t timestamp; 1.634 + hr = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, mAudioRate, ×tamp); 1.635 + NS_ENSURE_TRUE(SUCCEEDED(hr), false); 1.636 + 1.637 + mAudioFrameSum += numFrames; 1.638 + 1.639 + int64_t duration; 1.640 + hr = FramesToUsecs(numFrames, mAudioRate, &duration); 1.641 + NS_ENSURE_TRUE(SUCCEEDED(hr), false); 1.642 + 1.643 + mAudioQueue.Push(new AudioData(mDecoder->GetResource()->Tell(), 1.644 + timestamp, 1.645 + duration, 1.646 + numFrames, 1.647 + pcmSamples.forget(), 1.648 + mAudioChannels)); 1.649 + 1.650 + #ifdef LOG_SAMPLE_DECODE 1.651 + DECODER_LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u", 1.652 + timestamp, duration, currentLength); 1.653 + #endif 1.654 + 1.655 + return true; 1.656 +} 1.657 + 1.658 +HRESULT 1.659 +WMFReader::CreateBasicVideoFrame(IMFSample* aSample, 1.660 + int64_t aTimestampUsecs, 1.661 + int64_t aDurationUsecs, 1.662 + int64_t aOffsetBytes, 1.663 + VideoData** aOutVideoData) 1.664 +{ 1.665 + NS_ENSURE_TRUE(aSample, E_POINTER); 1.666 + NS_ENSURE_TRUE(aOutVideoData, E_POINTER); 1.667 + 1.668 + *aOutVideoData = nullptr; 1.669 + 1.670 + HRESULT hr; 1.671 + RefPtr<IMFMediaBuffer> buffer; 1.672 + 1.673 + // Must convert to contiguous buffer to use IMD2DBuffer interface. 1.674 + hr = aSample->ConvertToContiguousBuffer(byRef(buffer)); 1.675 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.676 + 1.677 + // Try and use the IMF2DBuffer interface if available, otherwise fallback 1.678 + // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient, 1.679 + // but only some systems (Windows 8?) support it. 1.680 + BYTE* data = nullptr; 1.681 + LONG stride = 0; 1.682 + RefPtr<IMF2DBuffer> twoDBuffer; 1.683 + hr = buffer->QueryInterface(static_cast<IMF2DBuffer**>(byRef(twoDBuffer))); 1.684 + if (SUCCEEDED(hr)) { 1.685 + hr = twoDBuffer->Lock2D(&data, &stride); 1.686 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.687 + } else { 1.688 + hr = buffer->Lock(&data, nullptr, nullptr); 1.689 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.690 + stride = mVideoStride; 1.691 + } 1.692 + 1.693 + // YV12, planar format: [YYYY....][VVVV....][UUUU....] 1.694 + // i.e., Y, then V, then U. 1.695 + VideoData::YCbCrBuffer b; 1.696 + 1.697 + // Y (Y') plane 1.698 + b.mPlanes[0].mData = data; 1.699 + b.mPlanes[0].mStride = stride; 1.700 + b.mPlanes[0].mHeight = mVideoHeight; 1.701 + b.mPlanes[0].mWidth = mVideoWidth; 1.702 + b.mPlanes[0].mOffset = 0; 1.703 + b.mPlanes[0].mSkip = 0; 1.704 + 1.705 + // The V and U planes are stored 16-row-aligned, so we need to add padding 1.706 + // to the row heights to ensure the Y'CbCr planes are referenced properly. 1.707 + uint32_t padding = 0; 1.708 + if (mVideoHeight % 16 != 0) { 1.709 + padding = 16 - (mVideoHeight % 16); 1.710 + } 1.711 + uint32_t y_size = stride * (mVideoHeight + padding); 1.712 + uint32_t v_size = stride * (mVideoHeight + padding) / 4; 1.713 + uint32_t halfStride = (stride + 1) / 2; 1.714 + uint32_t halfHeight = (mVideoHeight + 1) / 2; 1.715 + uint32_t halfWidth = (mVideoWidth + 1) / 2; 1.716 + 1.717 + // U plane (Cb) 1.718 + b.mPlanes[1].mData = data + y_size + v_size; 1.719 + b.mPlanes[1].mStride = halfStride; 1.720 + b.mPlanes[1].mHeight = halfHeight; 1.721 + b.mPlanes[1].mWidth = halfWidth; 1.722 + b.mPlanes[1].mOffset = 0; 1.723 + b.mPlanes[1].mSkip = 0; 1.724 + 1.725 + // V plane (Cr) 1.726 + b.mPlanes[2].mData = data + y_size; 1.727 + b.mPlanes[2].mStride = halfStride; 1.728 + b.mPlanes[2].mHeight = halfHeight; 1.729 + b.mPlanes[2].mWidth = halfWidth; 1.730 + b.mPlanes[2].mOffset = 0; 1.731 + b.mPlanes[2].mSkip = 0; 1.732 + 1.733 + VideoData *v = VideoData::Create(mInfo.mVideo, 1.734 + mDecoder->GetImageContainer(), 1.735 + aOffsetBytes, 1.736 + aTimestampUsecs, 1.737 + aDurationUsecs, 1.738 + b, 1.739 + false, 1.740 + -1, 1.741 + ToIntRect(mPictureRegion)); 1.742 + if (twoDBuffer) { 1.743 + twoDBuffer->Unlock2D(); 1.744 + } else { 1.745 + buffer->Unlock(); 1.746 + } 1.747 + 1.748 + *aOutVideoData = v; 1.749 + 1.750 + return S_OK; 1.751 +} 1.752 + 1.753 +HRESULT 1.754 +WMFReader::CreateD3DVideoFrame(IMFSample* aSample, 1.755 + int64_t aTimestampUsecs, 1.756 + int64_t aDurationUsecs, 1.757 + int64_t aOffsetBytes, 1.758 + VideoData** aOutVideoData) 1.759 +{ 1.760 + NS_ENSURE_TRUE(aSample, E_POINTER); 1.761 + NS_ENSURE_TRUE(aOutVideoData, E_POINTER); 1.762 + NS_ENSURE_TRUE(mDXVA2Manager, E_ABORT); 1.763 + NS_ENSURE_TRUE(mUseHwAccel, E_ABORT); 1.764 + 1.765 + *aOutVideoData = nullptr; 1.766 + HRESULT hr; 1.767 + 1.768 + nsRefPtr<Image> image; 1.769 + hr = mDXVA2Manager->CopyToImage(aSample, 1.770 + mPictureRegion, 1.771 + mDecoder->GetImageContainer(), 1.772 + getter_AddRefs(image)); 1.773 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.774 + NS_ENSURE_TRUE(image, E_FAIL); 1.775 + 1.776 + VideoData *v = VideoData::CreateFromImage(mInfo.mVideo, 1.777 + mDecoder->GetImageContainer(), 1.778 + aOffsetBytes, 1.779 + aTimestampUsecs, 1.780 + aDurationUsecs, 1.781 + image.forget(), 1.782 + false, 1.783 + -1, 1.784 + ToIntRect(mPictureRegion)); 1.785 + 1.786 + NS_ENSURE_TRUE(v, E_FAIL); 1.787 + *aOutVideoData = v; 1.788 + 1.789 + return S_OK; 1.790 +} 1.791 + 1.792 +bool 1.793 +WMFReader::DecodeVideoFrame(bool &aKeyframeSkip, 1.794 + int64_t aTimeThreshold) 1.795 +{ 1.796 + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); 1.797 + 1.798 + // Record number of frames decoded and parsed. Automatically update the 1.799 + // stats counters using the AutoNotifyDecoded stack-based class. 1.800 + uint32_t parsed = 0, decoded = 0; 1.801 + AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded); 1.802 + 1.803 + HRESULT hr; 1.804 + 1.805 + hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 1.806 + 0, // control flags 1.807 + 0, // read stream index 1.808 + nullptr, 1.809 + nullptr, 1.810 + nullptr); 1.811 + if (FAILED(hr)) { 1.812 + DECODER_LOG("WMFReader::DecodeVideoData() ReadSample failed with hr=0x%x", hr); 1.813 + return false; 1.814 + } 1.815 + 1.816 + DWORD flags = 0; 1.817 + LONGLONG timestampHns = 0; 1.818 + RefPtr<IMFSample> sample; 1.819 + hr = mSourceReaderCallback->Wait(&flags, ×tampHns, byRef(sample)); 1.820 + 1.821 + if (flags & MF_SOURCE_READERF_ERROR) { 1.822 + NS_WARNING("WMFReader: Catastrophic failure reading video sample"); 1.823 + // Future ReadSample() calls will fail, so give up and report end of stream. 1.824 + return false; 1.825 + } 1.826 + 1.827 + if (FAILED(hr)) { 1.828 + // Unknown failure, ask caller to try again? 1.829 + return true; 1.830 + } 1.831 + 1.832 + if (!sample) { 1.833 + if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) { 1.834 + DECODER_LOG("WMFReader; Null sample after video decode, at end of stream"); 1.835 + return false; 1.836 + } 1.837 + DECODER_LOG("WMFReader; Null sample after video decode. Maybe insufficient data..."); 1.838 + return true; 1.839 + } 1.840 + 1.841 + if ((flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) { 1.842 + DECODER_LOG("WMFReader: Video media type changed!"); 1.843 + RefPtr<IMFMediaType> mediaType; 1.844 + hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 1.845 + byRef(mediaType)); 1.846 + if (FAILED(hr) || 1.847 + FAILED(ConfigureVideoFrameGeometry(mediaType))) { 1.848 + NS_WARNING("Failed to reconfigure video media type"); 1.849 + return false; 1.850 + } 1.851 + } 1.852 + 1.853 + int64_t timestamp = HNsToUsecs(timestampHns); 1.854 + if (timestamp < aTimeThreshold) { 1.855 + return true; 1.856 + } 1.857 + int64_t offset = mDecoder->GetResource()->Tell(); 1.858 + int64_t duration = GetSampleDuration(sample); 1.859 + 1.860 + VideoData* v = nullptr; 1.861 + if (mUseHwAccel) { 1.862 + hr = CreateD3DVideoFrame(sample, timestamp, duration, offset, &v); 1.863 + } else { 1.864 + hr = CreateBasicVideoFrame(sample, timestamp, duration, offset, &v); 1.865 + } 1.866 + NS_ENSURE_TRUE(SUCCEEDED(hr) && v, false); 1.867 + 1.868 + parsed++; 1.869 + decoded++; 1.870 + mVideoQueue.Push(v); 1.871 + 1.872 + #ifdef LOG_SAMPLE_DECODE 1.873 + DECODER_LOG("Decoded video sample timestamp=%lld duration=%lld stride=%d height=%u flags=%u", 1.874 + timestamp, duration, mVideoStride, mVideoHeight, flags); 1.875 + #endif 1.876 + 1.877 + if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) { 1.878 + // End of stream. 1.879 + DECODER_LOG("End of video stream"); 1.880 + return false; 1.881 + } 1.882 + 1.883 + return true; 1.884 +} 1.885 + 1.886 +nsresult 1.887 +WMFReader::Seek(int64_t aTargetUs, 1.888 + int64_t aStartTime, 1.889 + int64_t aEndTime, 1.890 + int64_t aCurrentTime) 1.891 +{ 1.892 + DECODER_LOG("WMFReader::Seek() %lld", aTargetUs); 1.893 + 1.894 + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); 1.895 +#ifdef DEBUG 1.896 + bool canSeek = false; 1.897 + GetSourceReaderCanSeek(mSourceReader, canSeek); 1.898 + NS_ASSERTION(canSeek, "WMFReader::Seek() should only be called if we can seek!"); 1.899 +#endif 1.900 + 1.901 + nsresult rv = ResetDecode(); 1.902 + NS_ENSURE_SUCCESS(rv, rv); 1.903 + 1.904 + // Mark that we must recapture the audio frame count from the next sample. 1.905 + // WMF doesn't set a discontinuity marker when we seek to time 0, so we 1.906 + // must remember to recapture the audio frame offset and reset the frame 1.907 + // sum on the next audio packet we decode. 1.908 + mMustRecaptureAudioPosition = true; 1.909 + 1.910 + AutoPropVar var; 1.911 + HRESULT hr = InitPropVariantFromInt64(UsecsToHNs(aTargetUs), &var); 1.912 + NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); 1.913 + 1.914 + hr = mSourceReader->SetCurrentPosition(GUID_NULL, var); 1.915 + NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); 1.916 + 1.917 + return NS_OK; 1.918 +} 1.919 + 1.920 +} // namespace mozilla