2015-05-08 04:36:37 +03:00
|
|
|
/* -*- 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 "nsDirectoryServiceDefs.h"
|
|
|
|
#include "nsIFile.h"
|
|
|
|
#include "nsCOMPtr.h"
|
2015-05-29 05:07:22 +03:00
|
|
|
#include "nsLiteralString.h"
|
2015-08-11 01:27:41 +03:00
|
|
|
#include "nsCRTGlue.h"
|
2015-08-14 10:18:19 +03:00
|
|
|
#include "mozilla/Base64.h"
|
2017-02-21 03:44:58 +03:00
|
|
|
#include "prio.h"
|
|
|
|
#include "nsIConsoleService.h"
|
|
|
|
#include "mozIGeckoMediaPluginService.h"
|
2017-02-23 04:04:25 +03:00
|
|
|
#include "GMPService.h"
|
2015-05-08 04:36:37 +03:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
2015-08-11 01:27:41 +03:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-20 23:54:20 +03:00
|
|
|
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];
|
2016-12-21 23:20:17 +03:00
|
|
|
buf[0] = hex[(aBytes[i] & 0xf0) >> 4];
|
2016-12-20 23:54:20 +03:00
|
|
|
buf[1] = hex[aBytes[i] & 0x0f];
|
|
|
|
buf[2] = 0;
|
|
|
|
str.AppendASCII(buf);
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2016-12-21 00:37:09 +03:00
|
|
|
nsCString ToHexString(const nsTArray<uint8_t>& aBytes) {
|
|
|
|
return ToHexString(aBytes.Elements(), aBytes.Length());
|
|
|
|
}
|
|
|
|
|
2015-10-14 02:18:06 +03:00
|
|
|
bool FileExists(nsIFile* aFile) {
|
|
|
|
bool exists = false;
|
|
|
|
return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
|
|
|
|
}
|
|
|
|
|
2015-11-27 00:53:17 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-02-09 04:37:22 +03:00
|
|
|
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);
|
2017-09-19 11:14:49 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2016-02-09 04:37:22 +03:00
|
|
|
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) {
|
2016-02-12 00:59:51 +03:00
|
|
|
// 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.
|
2016-02-09 04:37:22 +03:00
|
|
|
continue;
|
|
|
|
}
|
2016-02-12 00:59:51 +03:00
|
|
|
nsAutoCString key(Substring(line, 0, colon));
|
2016-02-09 04:37:22 +03:00
|
|
|
ToLowerCase(key);
|
|
|
|
key.Trim(" ");
|
2016-02-12 00:59:51 +03:00
|
|
|
|
|
|
|
nsCString* value = new nsCString(Substring(line, colon + 1));
|
2016-02-09 04:37:22 +03:00
|
|
|
value->Trim(" ");
|
|
|
|
mValues.Put(key, 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 EmptyCString();
|
|
|
|
}
|
|
|
|
|
2016-11-03 04:33:31 +03:00
|
|
|
bool HaveGMPFor(const nsCString& aAPI, 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;
|
|
|
|
}
|
|
|
|
|
2017-02-21 03:44:58 +03:00
|
|
|
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());
|
|
|
|
}
|
2016-11-03 04:33:31 +03:00
|
|
|
|
2017-02-23 04:04:25 +03:00
|
|
|
RefPtr<AbstractThread> GetGMPAbstractThread() {
|
|
|
|
RefPtr<gmp::GeckoMediaPluginService> service =
|
|
|
|
gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
|
|
|
|
return service ? service->GetAbstractGMPThread() : nullptr;
|
|
|
|
}
|
|
|
|
|
2017-04-27 23:55:28 +03:00
|
|
|
static size_t Align16(size_t aNumber) {
|
|
|
|
const size_t mask = 15; // Alignment - 1.
|
Bug 1351953 - Pre-allocate shmems for the CDM process to use for storing decrypted and audio samples. r=gerald
Makes transfer of samples between the content and CDM processes use shmems.
The Chromium CDM API requires us to implement a synchronous interface to supply
buffers to the CDM for it to write decrypted samples into. We want our buffers
to be backed by shmems, in order to reduce the overhead of transferring decoded
frames. However due to sandboxing restrictions, the CDM process cannot allocate
shmems itself. We don't want to be doing synchronous IPC to request shmems
from the content process, nor do we want to have to do intr IPC or make async
IPC conform to the sync allocation interface. So instead we have the content
process pre-allocate a set of shmems and give them to the CDM process in
advance of them being needed.
When the CDM needs to allocate a buffer for storing a decrypted sample, the CDM
host gives it one of these shmems' buffers. When this is sent back to the
content process, we copy the result out (uploading to a GPU surface for video
frames), and send the shmem back to the CDM process so it can reuse it.
We predict the size of buffer the CDM will allocate, and prepopulate the CDM's
list of shmems with shmems of at least that size, plus a bit of padding for
safety. We pad frames out to be the next multiple of 16, as we've seen some
decoders do that.
Normally the CDM won't allocate more than one buffer at once, but we've seen
cases where it allocates two buffers, returns one and holds onto the other. So
the minimum number of shmems we give to the CDM must be at least two, and the
default is three for safety.
MozReview-Commit-ID: 5FaWAst3aeh
--HG--
extra : rebase_source : a0cb126e72bfb2905bcdf02e864dc654e8340410
2017-03-28 08:59:11 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-04-27 23:55:28 +03:00
|
|
|
size_t ySize = Align16(aWidth) * Align16(aHeight);
|
Bug 1351953 - Pre-allocate shmems for the CDM process to use for storing decrypted and audio samples. r=gerald
Makes transfer of samples between the content and CDM processes use shmems.
The Chromium CDM API requires us to implement a synchronous interface to supply
buffers to the CDM for it to write decrypted samples into. We want our buffers
to be backed by shmems, in order to reduce the overhead of transferring decoded
frames. However due to sandboxing restrictions, the CDM process cannot allocate
shmems itself. We don't want to be doing synchronous IPC to request shmems
from the content process, nor do we want to have to do intr IPC or make async
IPC conform to the sync allocation interface. So instead we have the content
process pre-allocate a set of shmems and give them to the CDM process in
advance of them being needed.
When the CDM needs to allocate a buffer for storing a decrypted sample, the CDM
host gives it one of these shmems' buffers. When this is sent back to the
content process, we copy the result out (uploading to a GPU surface for video
frames), and send the shmem back to the CDM process so it can reuse it.
We predict the size of buffer the CDM will allocate, and prepopulate the CDM's
list of shmems with shmems of at least that size, plus a bit of padding for
safety. We pad frames out to be the next multiple of 16, as we've seen some
decoders do that.
Normally the CDM won't allocate more than one buffer at once, but we've seen
cases where it allocates two buffers, returns one and holds onto the other. So
the minimum number of shmems we give to the CDM must be at least two, and the
default is three for safety.
MozReview-Commit-ID: 5FaWAst3aeh
--HG--
extra : rebase_source : a0cb126e72bfb2905bcdf02e864dc654e8340410
2017-03-28 08:59:11 +03:00
|
|
|
return ySize + (ySize / 4) * 2;
|
|
|
|
}
|
|
|
|
|
2015-05-08 04:36:37 +03:00
|
|
|
} // namespace mozilla
|