/src/mozilla-central/dom/media/gmp/GMPUtils.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
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 file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "GMPUtils.h" |
8 | | #include "nsDirectoryServiceDefs.h" |
9 | | #include "nsIFile.h" |
10 | | #include "nsCOMPtr.h" |
11 | | #include "nsLiteralString.h" |
12 | | #include "nsCRTGlue.h" |
13 | | #include "mozilla/Base64.h" |
14 | | #include "nsISimpleEnumerator.h" |
15 | | #include "prio.h" |
16 | | #include "nsIConsoleService.h" |
17 | | #include "mozIGeckoMediaPluginService.h" |
18 | | #include "GMPService.h" |
19 | | |
20 | | namespace mozilla { |
21 | | |
22 | | void |
23 | | SplitAt(const char* aDelims, |
24 | | const nsACString& aInput, |
25 | | nsTArray<nsCString>& aOutTokens) |
26 | 0 | { |
27 | 0 | nsAutoCString str(aInput); |
28 | 0 | char* end = str.BeginWriting(); |
29 | 0 | const char* start = nullptr; |
30 | 0 | while (!!(start = NS_strtok(aDelims, &end))) { |
31 | 0 | aOutTokens.AppendElement(nsCString(start)); |
32 | 0 | } |
33 | 0 | } |
34 | | |
35 | | nsCString |
36 | | ToHexString(const uint8_t * aBytes, uint32_t aLength) |
37 | 0 | { |
38 | 0 | static const char hex[] = { |
39 | 0 | '0', '1', '2', '3', |
40 | 0 | '4', '5', '6', '7', |
41 | 0 | '8', '9', 'a', 'b', |
42 | 0 | 'c', 'd', 'e', 'f' |
43 | 0 | }; |
44 | 0 | nsCString str; |
45 | 0 | for (uint32_t i = 0; i < aLength; i++) { |
46 | 0 | char buf[3]; |
47 | 0 | buf[0] = hex[(aBytes[i] & 0xf0) >> 4]; |
48 | 0 | buf[1] = hex[aBytes[i] & 0x0f]; |
49 | 0 | buf[2] = 0; |
50 | 0 | str.AppendASCII(buf); |
51 | 0 | } |
52 | 0 | return str; |
53 | 0 | } |
54 | | |
55 | | nsCString |
56 | | ToHexString(const nsTArray<uint8_t>& aBytes) |
57 | 0 | { |
58 | 0 | return ToHexString(aBytes.Elements(), aBytes.Length()); |
59 | 0 | } |
60 | | |
61 | | bool |
62 | | FileExists(nsIFile* aFile) |
63 | 0 | { |
64 | 0 | bool exists = false; |
65 | 0 | return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists; |
66 | 0 | } |
67 | | |
68 | | DirectoryEnumerator::DirectoryEnumerator(nsIFile* aPath, Mode aMode) |
69 | | : mMode(aMode) |
70 | 0 | { |
71 | 0 | aPath->GetDirectoryEntries(getter_AddRefs(mIter)); |
72 | 0 | } |
73 | | |
74 | | already_AddRefed<nsIFile> |
75 | | DirectoryEnumerator::Next() |
76 | 0 | { |
77 | 0 | if (!mIter) { |
78 | 0 | return nullptr; |
79 | 0 | } |
80 | 0 | bool hasMore = false; |
81 | 0 | while (NS_SUCCEEDED(mIter->HasMoreElements(&hasMore)) && hasMore) { |
82 | 0 | nsCOMPtr<nsISupports> supports; |
83 | 0 | nsresult rv = mIter->GetNext(getter_AddRefs(supports)); |
84 | 0 | if (NS_FAILED(rv)) { |
85 | 0 | continue; |
86 | 0 | } |
87 | 0 | |
88 | 0 | nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv)); |
89 | 0 | if (NS_FAILED(rv)) { |
90 | 0 | continue; |
91 | 0 | } |
92 | 0 | |
93 | 0 | if (mMode == DirsOnly) { |
94 | 0 | bool isDirectory = false; |
95 | 0 | rv = path->IsDirectory(&isDirectory); |
96 | 0 | if (NS_FAILED(rv) || !isDirectory) { |
97 | 0 | continue; |
98 | 0 | } |
99 | 0 | } |
100 | 0 | return path.forget(); |
101 | 0 | } |
102 | 0 | return nullptr; |
103 | 0 | } |
104 | | |
105 | | bool |
106 | | ReadIntoArray(nsIFile* aFile, |
107 | | nsTArray<uint8_t>& aOutDst, |
108 | | size_t aMaxLength) |
109 | 0 | { |
110 | 0 | if (!FileExists(aFile)) { |
111 | 0 | return false; |
112 | 0 | } |
113 | 0 | |
114 | 0 | PRFileDesc* fd = nullptr; |
115 | 0 | nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); |
116 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
117 | 0 | return false; |
118 | 0 | } |
119 | 0 | |
120 | 0 | int32_t length = PR_Seek(fd, 0, PR_SEEK_END); |
121 | 0 | PR_Seek(fd, 0, PR_SEEK_SET); |
122 | 0 |
|
123 | 0 | if (length < 0 || (size_t)length > aMaxLength) { |
124 | 0 | NS_WARNING("EME file is longer than maximum allowed length"); |
125 | 0 | PR_Close(fd); |
126 | 0 | return false; |
127 | 0 | } |
128 | 0 | aOutDst.SetLength(length); |
129 | 0 | int32_t bytesRead = PR_Read(fd, aOutDst.Elements(), length); |
130 | 0 | PR_Close(fd); |
131 | 0 | return (bytesRead == length); |
132 | 0 | } |
133 | | |
134 | | bool |
135 | | ReadIntoString(nsIFile* aFile, |
136 | | nsCString& aOutDst, |
137 | | size_t aMaxLength) |
138 | 0 | { |
139 | 0 | nsTArray<uint8_t> buf; |
140 | 0 | bool rv = ReadIntoArray(aFile, buf, aMaxLength); |
141 | 0 | if (rv) { |
142 | 0 | buf.AppendElement(0); // Append null terminator, required by nsC*String. |
143 | 0 | aOutDst = nsDependentCString((const char*)buf.Elements(), buf.Length() - 1); |
144 | 0 | } |
145 | 0 | return rv; |
146 | 0 | } |
147 | | |
148 | | bool |
149 | | GMPInfoFileParser::Init(nsIFile* aInfoFile) |
150 | 0 | { |
151 | 0 | nsTArray<nsCString> lines; |
152 | 0 | static const size_t MAX_GMP_INFO_FILE_LENGTH = 5 * 1024; |
153 | 0 |
|
154 | 0 | nsAutoCString info; |
155 | 0 | if (!ReadIntoString(aInfoFile, info, MAX_GMP_INFO_FILE_LENGTH)) { |
156 | 0 | NS_WARNING("Failed to read info file in GMP process."); |
157 | 0 | return false; |
158 | 0 | } |
159 | 0 |
|
160 | 0 | // Note: we pass "\r\n" to SplitAt so that we'll split lines delimited |
161 | 0 | // by \n (Unix), \r\n (Windows) and \r (old MacOSX). |
162 | 0 | SplitAt("\r\n", info, lines); |
163 | 0 |
|
164 | 0 | for (nsCString line : lines) { |
165 | 0 | // Field name is the string up to but not including the first ':' |
166 | 0 | // character on the line. |
167 | 0 | int32_t colon = line.FindChar(':'); |
168 | 0 | if (colon <= 0) { |
169 | 0 | // Not allowed to be the first character. |
170 | 0 | // Info field name must be at least one character. |
171 | 0 | continue; |
172 | 0 | } |
173 | 0 | nsAutoCString key(Substring(line, 0, colon)); |
174 | 0 | ToLowerCase(key); |
175 | 0 | key.Trim(" "); |
176 | 0 |
|
177 | 0 | nsCString* value = new nsCString(Substring(line, colon + 1)); |
178 | 0 | value->Trim(" "); |
179 | 0 | mValues.Put(key, value); // Hashtable assumes ownership of value. |
180 | 0 | } |
181 | 0 |
|
182 | 0 | return true; |
183 | 0 | } |
184 | | |
185 | | bool |
186 | 0 | GMPInfoFileParser::Contains(const nsCString& aKey) const { |
187 | 0 | nsCString key(aKey); |
188 | 0 | ToLowerCase(key); |
189 | 0 | return mValues.Contains(key); |
190 | 0 | } |
191 | | |
192 | | nsCString |
193 | 0 | GMPInfoFileParser::Get(const nsCString& aKey) const { |
194 | 0 | MOZ_ASSERT(Contains(aKey)); |
195 | 0 | nsCString key(aKey); |
196 | 0 | ToLowerCase(key); |
197 | 0 | nsCString* p = nullptr; |
198 | 0 | if (mValues.Get(key, &p)) { |
199 | 0 | return nsCString(*p); |
200 | 0 | } |
201 | 0 | return EmptyCString(); |
202 | 0 | } |
203 | | |
204 | | bool |
205 | | HaveGMPFor(const nsCString& aAPI, |
206 | | nsTArray<nsCString>&& aTags) |
207 | 0 | { |
208 | 0 | nsCOMPtr<mozIGeckoMediaPluginService> mps = |
209 | 0 | do_GetService("@mozilla.org/gecko-media-plugin-service;1"); |
210 | 0 | if (NS_WARN_IF(!mps)) { |
211 | 0 | return false; |
212 | 0 | } |
213 | 0 | |
214 | 0 | bool hasPlugin = false; |
215 | 0 | if (NS_FAILED(mps->HasPluginForAPI(aAPI, &aTags, &hasPlugin))) { |
216 | 0 | return false; |
217 | 0 | } |
218 | 0 | return hasPlugin; |
219 | 0 | } |
220 | | |
221 | | void |
222 | | LogToConsole(const nsAString& aMsg) |
223 | 0 | { |
224 | 0 | nsCOMPtr<nsIConsoleService> console( |
225 | 0 | do_GetService("@mozilla.org/consoleservice;1")); |
226 | 0 | if (!console) { |
227 | 0 | NS_WARNING("Failed to log message to console."); |
228 | 0 | return; |
229 | 0 | } |
230 | 0 | nsAutoString msg(aMsg); |
231 | 0 | console->LogStringMessage(msg.get()); |
232 | 0 | } |
233 | | |
234 | | RefPtr<AbstractThread> |
235 | | GetGMPAbstractThread() |
236 | 0 | { |
237 | 0 | RefPtr<gmp::GeckoMediaPluginService> service = |
238 | 0 | gmp::GeckoMediaPluginService::GetGeckoMediaPluginService(); |
239 | 0 | return service ? service->GetAbstractGMPThread() : nullptr; |
240 | 0 | } |
241 | | |
242 | | static size_t |
243 | | Align16(size_t aNumber) |
244 | 0 | { |
245 | 0 | const size_t mask = 15; // Alignment - 1. |
246 | 0 | return (aNumber + mask) & ~mask; |
247 | 0 | } |
248 | | |
249 | | size_t |
250 | | I420FrameBufferSizePadded(int32_t aWidth, int32_t aHeight) |
251 | 0 | { |
252 | 0 | if (aWidth <= 0 || aHeight <= 0 || aWidth > MAX_VIDEO_WIDTH || |
253 | 0 | aHeight > MAX_VIDEO_HEIGHT) { |
254 | 0 | return 0; |
255 | 0 | } |
256 | 0 | |
257 | 0 | size_t ySize = Align16(aWidth) * Align16(aHeight); |
258 | 0 | return ySize + (ySize / 4) * 2; |
259 | 0 | } |
260 | | |
261 | | } // namespace mozilla |