michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WMF.h" michael@0: #include "WMFDecoder.h" michael@0: #include "WMFReader.h" michael@0: #include "WMFUtils.h" michael@0: #include "MediaDecoderStateMachine.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: #include "nsCharSeparatedTokenizer.h" michael@0: michael@0: #ifdef MOZ_DIRECTSHOW michael@0: #include "DirectShowDecoder.h" michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: michael@0: MediaDecoderStateMachine* WMFDecoder::CreateStateMachine() michael@0: { michael@0: return new MediaDecoderStateMachine(this, new WMFReader(this)); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: WMFDecoder::IsMP3Supported() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); michael@0: if (!MediaDecoder::IsWMFEnabled()) { michael@0: return false; michael@0: } michael@0: // MP3 works fine in WMF on Windows Vista and Windows 8. michael@0: if (!IsWin7OrLater()) { michael@0: return true; michael@0: } michael@0: // MP3 support is disabled if we're on Windows 7 and no service packs are michael@0: // installed, since it's crashy. Win7 with service packs is not so crashy, michael@0: // so we enable it there. Note we prefer DirectShow for MP3 playback, but michael@0: // we still support MP3 in MP4 via WMF, or MP3 in WMF if DirectShow is michael@0: // disabled. michael@0: return IsWin7SP1OrLater(); michael@0: } michael@0: michael@0: static bool michael@0: IsSupportedH264Codec(const nsAString& aCodec) michael@0: { michael@0: // According to the WMF documentation: michael@0: // http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx michael@0: // "The Media Foundation H.264 video decoder is a Media Foundation Transform michael@0: // that supports decoding of Baseline, Main, and High profiles, up to level michael@0: // 5.1.". We also report that we can play Extended profile, as there are michael@0: // bitstreams that are Extended compliant that are also Baseline compliant. michael@0: michael@0: // H.264 codecs parameters have a type defined as avc1.PPCCLL, where michael@0: // PP = profile_idc, CC = constraint_set flags, LL = level_idc. michael@0: // We ignore the constraint_set flags, as it's not clear from the WMF michael@0: // documentation what constraints the WMF H.264 decoder supports. michael@0: // See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html michael@0: // for more details. michael@0: if (aCodec.Length() != strlen("avc1.PPCCLL")) { michael@0: return false; michael@0: } michael@0: michael@0: // Verify the codec starts with "avc1.". michael@0: const nsAString& sample = Substring(aCodec, 0, 5); michael@0: if (!sample.EqualsASCII("avc1.")) { michael@0: return false; michael@0: } michael@0: michael@0: // Extract the profile_idc and level_idc. Note: the constraint_set flags michael@0: // are ignored, it's not clear from the WMF documentation if they make a michael@0: // difference. michael@0: nsresult rv = NS_OK; michael@0: const int32_t profile = PromiseFlatString(Substring(aCodec, 5, 2)).ToInteger(&rv, 16); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: const int32_t level = PromiseFlatString(Substring(aCodec, 9, 2)).ToInteger(&rv, 16); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: return level >= eAVEncH264VLevel1 && michael@0: level <= eAVEncH264VLevel5_1 && michael@0: (profile == eAVEncH264VProfile_Base || michael@0: profile == eAVEncH264VProfile_Main || michael@0: profile == eAVEncH264VProfile_Extended || michael@0: profile == eAVEncH264VProfile_High); michael@0: } michael@0: michael@0: bool michael@0: WMFDecoder::CanPlayType(const nsACString& aType, michael@0: const nsAString& aCodecs) michael@0: { michael@0: if (!MediaDecoder::IsWMFEnabled() || michael@0: NS_FAILED(LoadDLLs())) { michael@0: return false; michael@0: } michael@0: michael@0: // Assume that if LoadDLLs() didn't fail, we can playback the types that michael@0: // we know should be supported by Windows Media Foundation. michael@0: if ((aType.EqualsASCII("audio/mpeg") || aType.EqualsASCII("audio/mp3")) && michael@0: IsMP3Supported()) { michael@0: // Note: We block MP3 playback on Window 7 SP0 since it seems to crash michael@0: // in some circumstances. michael@0: return !aCodecs.Length() || aCodecs.EqualsASCII("mp3"); michael@0: } michael@0: michael@0: // AAC-LC or MP3 in M4A. michael@0: if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) { michael@0: return !aCodecs.Length() || michael@0: aCodecs.EqualsASCII("mp4a.40.2") || michael@0: aCodecs.EqualsASCII("mp3"); michael@0: } michael@0: michael@0: if (!aType.EqualsASCII("video/mp4")) { michael@0: return false; michael@0: } michael@0: michael@0: // H.264 + AAC in MP4. Verify that all the codecs specifed are ones that michael@0: // we expect that we can play. michael@0: nsCharSeparatedTokenizer tokenizer(aCodecs, ','); michael@0: bool expectMoreTokens = false; michael@0: while (tokenizer.hasMoreTokens()) { michael@0: const nsSubstring& token = tokenizer.nextToken(); michael@0: expectMoreTokens = tokenizer.separatorAfterCurrentToken(); michael@0: if (token.EqualsASCII("mp4a.40.2") || // AAC-LC michael@0: token.EqualsASCII("mp3") || michael@0: IsSupportedH264Codec(token)) { michael@0: continue; michael@0: } michael@0: return false; michael@0: } michael@0: if (expectMoreTokens) { michael@0: // Last codec name was empty michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: WMFDecoder::LoadDLLs() michael@0: { michael@0: return SUCCEEDED(wmf::LoadDLLs()) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: void michael@0: WMFDecoder::UnloadDLLs() michael@0: { michael@0: wmf::UnloadDLLs(); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: WMFDecoder::IsEnabled() michael@0: { michael@0: // We only use WMF on Windows Vista and up michael@0: return IsVistaOrLater() && michael@0: Preferences::GetBool("media.windows-media-foundation.enabled"); michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: