Line data Source code
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 1 : SplitAt(const char* aDelims,
24 : const nsACString& aInput,
25 : nsTArray<nsCString>& aOutTokens)
26 : {
27 2 : nsAutoCString str(aInput);
28 1 : char* end = str.BeginWriting();
29 1 : const char* start = nullptr;
30 1 : while (!!(start = NS_strtok(aDelims, &end))) {
31 0 : aOutTokens.AppendElement(nsCString(start));
32 : }
33 1 : }
34 :
35 : nsCString
36 0 : ToHexString(const uint8_t * aBytes, uint32_t aLength)
37 : {
38 : static const char hex[] = {
39 : '0', '1', '2', '3',
40 : '4', '5', '6', '7',
41 : '8', '9', 'a', 'b',
42 : 'c', 'd', 'e', 'f'
43 : };
44 0 : nsCString str;
45 0 : for (uint32_t i = 0; i < aLength; i++) {
46 : 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 : }
52 0 : return str;
53 : }
54 :
55 : nsCString
56 0 : ToHexString(const nsTArray<uint8_t>& aBytes)
57 : {
58 0 : return ToHexString(aBytes.Elements(), aBytes.Length());
59 : }
60 :
61 : bool
62 2 : FileExists(nsIFile* aFile)
63 : {
64 2 : bool exists = false;
65 2 : return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
66 : }
67 :
68 0 : DirectoryEnumerator::DirectoryEnumerator(nsIFile* aPath, Mode aMode)
69 0 : : mMode(aMode)
70 : {
71 0 : aPath->GetDirectoryEntries(getter_AddRefs(mIter));
72 0 : }
73 :
74 : already_AddRefed<nsIFile>
75 0 : DirectoryEnumerator::Next()
76 : {
77 0 : if (!mIter) {
78 0 : return nullptr;
79 : }
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 : }
87 :
88 0 : nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
89 0 : if (NS_FAILED(rv)) {
90 0 : continue;
91 : }
92 :
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 : }
99 : }
100 0 : return path.forget();
101 : }
102 0 : return nullptr;
103 : }
104 :
105 : bool
106 1 : ReadIntoArray(nsIFile* aFile,
107 : nsTArray<uint8_t>& aOutDst,
108 : size_t aMaxLength)
109 : {
110 1 : if (!FileExists(aFile)) {
111 0 : return false;
112 : }
113 :
114 1 : PRFileDesc* fd = nullptr;
115 1 : nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
116 1 : if (NS_FAILED(rv)) {
117 0 : return false;
118 : }
119 :
120 1 : int32_t length = PR_Seek(fd, 0, PR_SEEK_END);
121 1 : PR_Seek(fd, 0, PR_SEEK_SET);
122 :
123 1 : 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 : }
128 1 : aOutDst.SetLength(length);
129 1 : int32_t bytesRead = PR_Read(fd, aOutDst.Elements(), length);
130 1 : PR_Close(fd);
131 1 : return (bytesRead == length);
132 : }
133 :
134 : bool
135 1 : ReadIntoString(nsIFile* aFile,
136 : nsCString& aOutDst,
137 : size_t aMaxLength)
138 : {
139 2 : nsTArray<uint8_t> buf;
140 1 : bool rv = ReadIntoArray(aFile, buf, aMaxLength);
141 1 : if (rv) {
142 1 : buf.AppendElement(0); // Append null terminator, required by nsC*String.
143 1 : aOutDst = nsDependentCString((const char*)buf.Elements(), buf.Length() - 1);
144 : }
145 2 : return rv;
146 : }
147 :
148 : bool
149 0 : GMPInfoFileParser::Init(nsIFile* aInfoFile)
150 : {
151 0 : nsTArray<nsCString> lines;
152 : static const size_t MAX_GMP_INFO_FILE_LENGTH = 5 * 1024;
153 :
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 : }
159 :
160 : // Note: we pass "\r\n" to SplitAt so that we'll split lines delimited
161 : // by \n (Unix), \r\n (Windows) and \r (old MacOSX).
162 0 : SplitAt("\r\n", info, lines);
163 :
164 0 : for (nsCString line : lines) {
165 : // Field name is the string up to but not including the first ':'
166 : // character on the line.
167 0 : int32_t colon = line.FindChar(':');
168 0 : if (colon <= 0) {
169 : // Not allowed to be the first character.
170 : // Info field name must be at least one character.
171 0 : continue;
172 : }
173 0 : nsAutoCString key(Substring(line, 0, colon));
174 0 : ToLowerCase(key);
175 0 : key.Trim(" ");
176 :
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 : }
181 :
182 0 : return true;
183 : }
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 : }
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 : }
201 0 : return EmptyCString();
202 : }
203 :
204 : bool
205 0 : HaveGMPFor(const nsCString& aAPI,
206 : nsTArray<nsCString>&& aTags)
207 : {
208 : 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 : }
213 :
214 0 : bool hasPlugin = false;
215 0 : if (NS_FAILED(mps->HasPluginForAPI(aAPI, &aTags, &hasPlugin))) {
216 0 : return false;
217 : }
218 0 : return hasPlugin;
219 : }
220 :
221 : void
222 0 : LogToConsole(const nsAString& aMsg)
223 : {
224 : 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 : }
230 0 : nsAutoString msg(aMsg);
231 0 : console->LogStringMessage(msg.get());
232 : }
233 :
234 : RefPtr<AbstractThread>
235 0 : GetGMPAbstractThread()
236 : {
237 : RefPtr<gmp::GeckoMediaPluginService> service =
238 0 : gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
239 0 : return service ? service->GetAbstractGMPThread() : nullptr;
240 : }
241 :
242 : static size_t
243 0 : Align16(size_t aNumber)
244 : {
245 0 : const size_t mask = 15; // Alignment - 1.
246 0 : return (aNumber + mask) & ~mask;
247 : }
248 :
249 : size_t
250 0 : I420FrameBufferSizePadded(int32_t aWidth, int32_t aHeight)
251 : {
252 0 : if (aWidth <= 0 || aHeight <= 0 || aWidth > MAX_VIDEO_WIDTH ||
253 : aHeight > MAX_VIDEO_HEIGHT) {
254 0 : return 0;
255 : }
256 :
257 0 : size_t ySize = Align16(aWidth) * Align16(aHeight);
258 0 : return ySize + (ySize / 4) * 2;
259 : }
260 :
261 : } // namespace mozilla
|