summaryrefslogtreecommitdiffstats
path: root/dom/media/gmp/GMPUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/gmp/GMPUtils.cpp')
-rw-r--r--dom/media/gmp/GMPUtils.cpp228
1 files changed, 228 insertions, 0 deletions
diff --git a/dom/media/gmp/GMPUtils.cpp b/dom/media/gmp/GMPUtils.cpp
new file mode 100644
index 0000000000..1bc02d7027
--- /dev/null
+++ b/dom/media/gmp/GMPUtils.cpp
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GMPUtils.h"
+
+#include "GMPService.h"
+#include "VideoLimits.h"
+#include "mozIGeckoMediaPluginService.h"
+#include "mozilla/Base64.h"
+#include "nsCOMPtr.h"
+#include "nsCRTGlue.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIConsoleService.h"
+#include "nsIFile.h"
+#include "nsLiteralString.h"
+#include "prio.h"
+
+namespace mozilla {
+
+void SplitAt(const char* aDelims, const nsACString& aInput,
+ nsTArray<nsCString>& aOutTokens) {
+ nsAutoCString str(aInput);
+ char* end = str.BeginWriting();
+ const char* start = nullptr;
+ while (!!(start = NS_strtok(aDelims, &end))) {
+ aOutTokens.AppendElement(nsCString(start));
+ }
+}
+
+nsCString ToHexString(const uint8_t* aBytes, uint32_t aLength) {
+ static const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ nsCString str;
+ for (uint32_t i = 0; i < aLength; i++) {
+ char buf[3];
+ buf[0] = hex[(aBytes[i] & 0xf0) >> 4];
+ buf[1] = hex[aBytes[i] & 0x0f];
+ buf[2] = 0;
+ str.AppendASCII(buf);
+ }
+ return str;
+}
+
+nsCString ToHexString(const nsTArray<uint8_t>& aBytes) {
+ return ToHexString(aBytes.Elements(), aBytes.Length());
+}
+
+bool FileExists(nsIFile* aFile) {
+ bool exists = false;
+ return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
+}
+
+DirectoryEnumerator::DirectoryEnumerator(nsIFile* aPath, Mode aMode)
+ : mMode(aMode) {
+ aPath->GetDirectoryEntries(getter_AddRefs(mIter));
+}
+
+already_AddRefed<nsIFile> DirectoryEnumerator::Next() {
+ if (!mIter) {
+ return nullptr;
+ }
+ bool hasMore = false;
+ while (NS_SUCCEEDED(mIter->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> supports;
+ nsresult rv = mIter->GetNext(getter_AddRefs(supports));
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+
+ nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+
+ if (mMode == DirsOnly) {
+ bool isDirectory = false;
+ rv = path->IsDirectory(&isDirectory);
+ if (NS_FAILED(rv) || !isDirectory) {
+ continue;
+ }
+ }
+ return path.forget();
+ }
+ return nullptr;
+}
+
+bool ReadIntoArray(nsIFile* aFile, nsTArray<uint8_t>& aOutDst,
+ size_t aMaxLength) {
+ if (!FileExists(aFile)) {
+ return false;
+ }
+
+ PRFileDesc* fd = nullptr;
+ nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ int32_t length = PR_Seek(fd, 0, PR_SEEK_END);
+ PR_Seek(fd, 0, PR_SEEK_SET);
+
+ if (length < 0 || (size_t)length > aMaxLength) {
+ NS_WARNING("EME file is longer than maximum allowed length");
+ PR_Close(fd);
+ return false;
+ }
+ aOutDst.SetLength(length);
+ int32_t bytesRead = PR_Read(fd, aOutDst.Elements(), length);
+ PR_Close(fd);
+ return (bytesRead == length);
+}
+
+bool ReadIntoString(nsIFile* aFile, nsCString& aOutDst, size_t aMaxLength) {
+ nsTArray<uint8_t> buf;
+ bool rv = ReadIntoArray(aFile, buf, aMaxLength);
+ if (rv) {
+ buf.AppendElement(0); // Append null terminator, required by nsC*String.
+ aOutDst = nsDependentCString((const char*)buf.Elements(), buf.Length() - 1);
+ }
+ return rv;
+}
+
+bool GMPInfoFileParser::Init(nsIFile* aInfoFile) {
+ nsTArray<nsCString> lines;
+ static const size_t MAX_GMP_INFO_FILE_LENGTH = 5 * 1024;
+
+ nsAutoCString info;
+ if (!ReadIntoString(aInfoFile, info, MAX_GMP_INFO_FILE_LENGTH)) {
+ NS_WARNING("Failed to read info file in GMP process.");
+ return false;
+ }
+
+ // Note: we pass "\r\n" to SplitAt so that we'll split lines delimited
+ // by \n (Unix), \r\n (Windows) and \r (old MacOSX).
+ SplitAt("\r\n", info, lines);
+
+ for (nsCString line : lines) {
+ // Field name is the string up to but not including the first ':'
+ // character on the line.
+ int32_t colon = line.FindChar(':');
+ if (colon <= 0) {
+ // Not allowed to be the first character.
+ // Info field name must be at least one character.
+ continue;
+ }
+ nsAutoCString key(Substring(line, 0, colon));
+ ToLowerCase(key);
+ key.Trim(" ");
+
+ auto value = MakeUnique<nsCString>(Substring(line, colon + 1));
+ value->Trim(" ");
+ mValues.InsertOrUpdate(
+ key,
+ std::move(value)); // Hashtable assumes ownership of value.
+ }
+
+ return true;
+}
+
+bool GMPInfoFileParser::Contains(const nsCString& aKey) const {
+ nsCString key(aKey);
+ ToLowerCase(key);
+ return mValues.Contains(key);
+}
+
+nsCString GMPInfoFileParser::Get(const nsCString& aKey) const {
+ MOZ_ASSERT(Contains(aKey));
+ nsCString key(aKey);
+ ToLowerCase(key);
+ nsCString* p = nullptr;
+ if (mValues.Get(key, &p)) {
+ return nsCString(*p);
+ }
+ return ""_ns;
+}
+
+bool HaveGMPFor(const nsACString& aAPI, const nsTArray<nsCString>& aTags) {
+ nsCOMPtr<mozIGeckoMediaPluginService> mps =
+ do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+ if (NS_WARN_IF(!mps)) {
+ return false;
+ }
+
+ bool hasPlugin = false;
+ if (NS_FAILED(mps->HasPluginForAPI(aAPI, aTags, &hasPlugin))) {
+ return false;
+ }
+ return hasPlugin;
+}
+
+void LogToConsole(const nsAString& aMsg) {
+ nsCOMPtr<nsIConsoleService> console(
+ do_GetService("@mozilla.org/consoleservice;1"));
+ if (!console) {
+ NS_WARNING("Failed to log message to console.");
+ return;
+ }
+ nsAutoString msg(aMsg);
+ console->LogStringMessage(msg.get());
+}
+
+already_AddRefed<nsISerialEventTarget> GetGMPThread() {
+ RefPtr<gmp::GeckoMediaPluginService> service =
+ gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+ nsCOMPtr<nsISerialEventTarget> thread =
+ service ? service->GetGMPThread() : nullptr;
+ return thread.forget();
+}
+
+static size_t Align16(size_t aNumber) {
+ const size_t mask = 15; // Alignment - 1.
+ return (aNumber + mask) & ~mask;
+}
+
+size_t I420FrameBufferSizePadded(int32_t aWidth, int32_t aHeight) {
+ if (aWidth <= 0 || aHeight <= 0 || aWidth > MAX_VIDEO_WIDTH ||
+ aHeight > MAX_VIDEO_HEIGHT) {
+ return 0;
+ }
+
+ size_t ySize = Align16(aWidth) * Align16(aHeight);
+ return ySize + (ySize / 4) * 2;
+}
+
+} // namespace mozilla