|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "WMF.h" |
|
8 #include "WMFDecoder.h" |
|
9 #include "WMFReader.h" |
|
10 #include "WMFUtils.h" |
|
11 #include "MediaDecoderStateMachine.h" |
|
12 #include "mozilla/Preferences.h" |
|
13 #include "mozilla/WindowsVersion.h" |
|
14 #include "nsCharSeparatedTokenizer.h" |
|
15 |
|
16 #ifdef MOZ_DIRECTSHOW |
|
17 #include "DirectShowDecoder.h" |
|
18 #endif |
|
19 |
|
20 namespace mozilla { |
|
21 |
|
22 MediaDecoderStateMachine* WMFDecoder::CreateStateMachine() |
|
23 { |
|
24 return new MediaDecoderStateMachine(this, new WMFReader(this)); |
|
25 } |
|
26 |
|
27 /* static */ |
|
28 bool |
|
29 WMFDecoder::IsMP3Supported() |
|
30 { |
|
31 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); |
|
32 if (!MediaDecoder::IsWMFEnabled()) { |
|
33 return false; |
|
34 } |
|
35 // MP3 works fine in WMF on Windows Vista and Windows 8. |
|
36 if (!IsWin7OrLater()) { |
|
37 return true; |
|
38 } |
|
39 // MP3 support is disabled if we're on Windows 7 and no service packs are |
|
40 // installed, since it's crashy. Win7 with service packs is not so crashy, |
|
41 // so we enable it there. Note we prefer DirectShow for MP3 playback, but |
|
42 // we still support MP3 in MP4 via WMF, or MP3 in WMF if DirectShow is |
|
43 // disabled. |
|
44 return IsWin7SP1OrLater(); |
|
45 } |
|
46 |
|
47 static bool |
|
48 IsSupportedH264Codec(const nsAString& aCodec) |
|
49 { |
|
50 // According to the WMF documentation: |
|
51 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx |
|
52 // "The Media Foundation H.264 video decoder is a Media Foundation Transform |
|
53 // that supports decoding of Baseline, Main, and High profiles, up to level |
|
54 // 5.1.". We also report that we can play Extended profile, as there are |
|
55 // bitstreams that are Extended compliant that are also Baseline compliant. |
|
56 |
|
57 // H.264 codecs parameters have a type defined as avc1.PPCCLL, where |
|
58 // PP = profile_idc, CC = constraint_set flags, LL = level_idc. |
|
59 // We ignore the constraint_set flags, as it's not clear from the WMF |
|
60 // documentation what constraints the WMF H.264 decoder supports. |
|
61 // See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html |
|
62 // for more details. |
|
63 if (aCodec.Length() != strlen("avc1.PPCCLL")) { |
|
64 return false; |
|
65 } |
|
66 |
|
67 // Verify the codec starts with "avc1.". |
|
68 const nsAString& sample = Substring(aCodec, 0, 5); |
|
69 if (!sample.EqualsASCII("avc1.")) { |
|
70 return false; |
|
71 } |
|
72 |
|
73 // Extract the profile_idc and level_idc. Note: the constraint_set flags |
|
74 // are ignored, it's not clear from the WMF documentation if they make a |
|
75 // difference. |
|
76 nsresult rv = NS_OK; |
|
77 const int32_t profile = PromiseFlatString(Substring(aCodec, 5, 2)).ToInteger(&rv, 16); |
|
78 NS_ENSURE_SUCCESS(rv, false); |
|
79 |
|
80 const int32_t level = PromiseFlatString(Substring(aCodec, 9, 2)).ToInteger(&rv, 16); |
|
81 NS_ENSURE_SUCCESS(rv, false); |
|
82 |
|
83 return level >= eAVEncH264VLevel1 && |
|
84 level <= eAVEncH264VLevel5_1 && |
|
85 (profile == eAVEncH264VProfile_Base || |
|
86 profile == eAVEncH264VProfile_Main || |
|
87 profile == eAVEncH264VProfile_Extended || |
|
88 profile == eAVEncH264VProfile_High); |
|
89 } |
|
90 |
|
91 bool |
|
92 WMFDecoder::CanPlayType(const nsACString& aType, |
|
93 const nsAString& aCodecs) |
|
94 { |
|
95 if (!MediaDecoder::IsWMFEnabled() || |
|
96 NS_FAILED(LoadDLLs())) { |
|
97 return false; |
|
98 } |
|
99 |
|
100 // Assume that if LoadDLLs() didn't fail, we can playback the types that |
|
101 // we know should be supported by Windows Media Foundation. |
|
102 if ((aType.EqualsASCII("audio/mpeg") || aType.EqualsASCII("audio/mp3")) && |
|
103 IsMP3Supported()) { |
|
104 // Note: We block MP3 playback on Window 7 SP0 since it seems to crash |
|
105 // in some circumstances. |
|
106 return !aCodecs.Length() || aCodecs.EqualsASCII("mp3"); |
|
107 } |
|
108 |
|
109 // AAC-LC or MP3 in M4A. |
|
110 if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) { |
|
111 return !aCodecs.Length() || |
|
112 aCodecs.EqualsASCII("mp4a.40.2") || |
|
113 aCodecs.EqualsASCII("mp3"); |
|
114 } |
|
115 |
|
116 if (!aType.EqualsASCII("video/mp4")) { |
|
117 return false; |
|
118 } |
|
119 |
|
120 // H.264 + AAC in MP4. Verify that all the codecs specifed are ones that |
|
121 // we expect that we can play. |
|
122 nsCharSeparatedTokenizer tokenizer(aCodecs, ','); |
|
123 bool expectMoreTokens = false; |
|
124 while (tokenizer.hasMoreTokens()) { |
|
125 const nsSubstring& token = tokenizer.nextToken(); |
|
126 expectMoreTokens = tokenizer.separatorAfterCurrentToken(); |
|
127 if (token.EqualsASCII("mp4a.40.2") || // AAC-LC |
|
128 token.EqualsASCII("mp3") || |
|
129 IsSupportedH264Codec(token)) { |
|
130 continue; |
|
131 } |
|
132 return false; |
|
133 } |
|
134 if (expectMoreTokens) { |
|
135 // Last codec name was empty |
|
136 return false; |
|
137 } |
|
138 return true; |
|
139 } |
|
140 |
|
141 nsresult |
|
142 WMFDecoder::LoadDLLs() |
|
143 { |
|
144 return SUCCEEDED(wmf::LoadDLLs()) ? NS_OK : NS_ERROR_FAILURE; |
|
145 } |
|
146 |
|
147 void |
|
148 WMFDecoder::UnloadDLLs() |
|
149 { |
|
150 wmf::UnloadDLLs(); |
|
151 } |
|
152 |
|
153 /* static */ |
|
154 bool |
|
155 WMFDecoder::IsEnabled() |
|
156 { |
|
157 // We only use WMF on Windows Vista and up |
|
158 return IsVistaOrLater() && |
|
159 Preferences::GetBool("media.windows-media-foundation.enabled"); |
|
160 } |
|
161 |
|
162 } // namespace mozilla |
|
163 |