Import Cobalt 23.master.0.308671
diff --git a/.codespellignorelines b/.codespellignorelines
index 0079aa8..0c7fa5f 100644
--- a/.codespellignorelines
+++ b/.codespellignorelines
@@ -7,3 +7,4 @@
texture_size.height(),
GrMipMapped::kNo, texture_info));
Onces represent initializations that should only ever happen once per process,
+ <Resource Language="TE" />
diff --git a/cobalt/browser/BUILD.gn b/cobalt/browser/BUILD.gn
index c319c72..529dd2f 100644
--- a/cobalt/browser/BUILD.gn
+++ b/cobalt/browser/BUILD.gn
@@ -60,8 +60,6 @@
data_deps = [
"//cobalt/dom:licenses",
"//cobalt/network:copy_ssl_certificates",
- "//cobalt/speech:speech_testdata",
- "//cobalt/webdriver:copy_webdriver_data",
"//third_party/icu:icudata",
]
if (cobalt_font_package == "empty") {
@@ -77,6 +75,8 @@
"//cobalt/debug/backend/content:copy_backend_web_files",
"//cobalt/debug/console/content:copy_console_web_files",
"//cobalt/debug/remote/content:copy_remote_web_files",
+ "//cobalt/speech:speech_testdata",
+ "//cobalt/webdriver:copy_webdriver_data",
"//third_party/devtools:build_release_devtools",
"//third_party/devtools:copy_devtools_modules",
"//third_party/devtools:copy_inspector_images",
diff --git a/cobalt/demos/content/watchdog-demo/index.html b/cobalt/demos/content/watchdog-demo/index.html
index 3354881..4114648 100644
--- a/cobalt/demos/content/watchdog-demo/index.html
+++ b/cobalt/demos/content/watchdog-demo/index.html
@@ -62,7 +62,7 @@
} else if (watchdogFunction == 'ping') {
ret = h5vcc.crashLog.ping('test-name', `test-ping`);
} else if (watchdogFunction == 'getWatchdogViolations') {
- ret = h5vcc.crashLog.getWatchdogViolations(true);
+ ret = h5vcc.crashLog.getWatchdogViolations();
} else if (watchdogFunction == 'getPersistentSettingWatchdogCrash') {
ret = h5vcc.crashLog.getPersistentSettingWatchdogCrash();
} else if (watchdogFunction == 'setPersistentSettingWatchdogCrashTrue') {
diff --git a/cobalt/evergreen_tests/evergreen_tests.py b/cobalt/evergreen_tests/evergreen_tests.py
index b0e2385..74eafb0 100644
--- a/cobalt/evergreen_tests/evergreen_tests.py
+++ b/cobalt/evergreen_tests/evergreen_tests.py
@@ -28,7 +28,6 @@
from starboard.tools.paths import REPOSITORY_ROOT
_DEFAULT_PLATFORM_UNDER_TEST = 'linux'
-_DEFAULT_TEST_TYPE = 'functional'
def _Exec(cmd, env=None):
@@ -58,10 +57,6 @@
'--platform_under_test',
default=_DEFAULT_PLATFORM_UNDER_TEST,
help='The platform to run the tests on (e.g., linux or raspi).')
- arg_parser.add_argument(
- '--test_type',
- default=_DEFAULT_TEST_TYPE,
- help='The type of tests to run: functional or performance.')
authentication_method = arg_parser.add_mutually_exclusive_group()
authentication_method.add_argument(
'--public-key-auth',
@@ -123,9 +118,6 @@
command.append('-a')
command.append('password')
- command.append('-t')
- command.append(args.test_type)
-
command.append(args.platform_under_test)
return _Exec(command, env)
diff --git a/cobalt/h5vcc/h5vcc_crash_log.cc b/cobalt/h5vcc/h5vcc_crash_log.cc
index f9304e9..d61dca0 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.cc
+++ b/cobalt/h5vcc/h5vcc_crash_log.cc
@@ -180,9 +180,9 @@
return false;
}
-std::string H5vccCrashLog::GetWatchdogViolations(bool current) {
+std::string H5vccCrashLog::GetWatchdogViolations() {
watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
- if (watchdog) return watchdog->GetWatchdogViolations(current);
+ if (watchdog) return watchdog->GetWatchdogViolations();
return "";
}
diff --git a/cobalt/h5vcc/h5vcc_crash_log.h b/cobalt/h5vcc/h5vcc_crash_log.h
index c39f4bd..5b1557b 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.h
+++ b/cobalt/h5vcc/h5vcc_crash_log.h
@@ -42,7 +42,7 @@
bool Ping(const std::string& name, const std::string& ping_info);
- std::string GetWatchdogViolations(bool current);
+ std::string GetWatchdogViolations();
bool GetPersistentSettingWatchdogCrash();
diff --git a/cobalt/h5vcc/h5vcc_crash_log.idl b/cobalt/h5vcc/h5vcc_crash_log.idl
index d1e9e1a..5c91aa0 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.idl
+++ b/cobalt/h5vcc/h5vcc_crash_log.idl
@@ -33,10 +33,12 @@
// name, Watchdog client to register.
// description, information on the Watchdog client.
// monitor_state, application state up to which the client is monitored.
+ // Inclusive.
// time_interval, maximum number of microseconds allowed between pings
// before triggering a Watchdog violation.
// time_wait, number of microseconds to initially wait before Watchdog
- // violations can be triggered.
+ // violations can be triggered. Reapplies after client resumes from idle
+ // state due to application state changes.
// replace, behavior with previously registered Watchdog clients of the
// same name.
boolean register(DOMString name, DOMString description,
@@ -52,11 +54,9 @@
// metadata.
boolean ping(DOMString name, DOMString ping_info);
- // Returns a json string containing the Watchdog violations. Current boolean
- // determines whether the current file representing ongoing violations or the
- // previous file containing violations from previous app starts and since the
- // last call (up to a limit) is returned.
- DOMString getWatchdogViolations(boolean current);
+ // Returns a json string containing the Watchdog violations since the last
+ // call. Clears internal cache of Watchdog violations to prevent duplicates.
+ DOMString getWatchdogViolations();
// Gets a persistent Watchdog setting that determines whether or not a
// Watchdog violation will trigger a crash.
diff --git a/cobalt/media/base/playback_statistics.cc b/cobalt/media/base/playback_statistics.cc
index ffb51f8..babd725 100644
--- a/cobalt/media/base/playback_statistics.cc
+++ b/cobalt/media/base/playback_statistics.cc
@@ -33,6 +33,7 @@
volatile SbAtomic32 s_av1_played = 0;
volatile SbAtomic32 s_h264_played = 0;
volatile SbAtomic32 s_hevc_played = 0;
+volatile SbAtomic32 s_vp8_played = 0;
volatile SbAtomic32 s_vp9_played = 0;
volatile SbAtomic32 s_min_video_width = 999999;
volatile SbAtomic32 s_min_video_height = 999999;
@@ -153,6 +154,8 @@
SbAtomicBarrier_Increment(&s_h264_played, 1);
} else if (video_config.codec() == VideoCodec::kHEVC) {
SbAtomicBarrier_Increment(&s_hevc_played, 1);
+ } else if (video_config.codec() == VideoCodec::kVP8) {
+ SbAtomicBarrier_Increment(&s_vp8_played, 1);
} else if (video_config.codec() == VideoCodec::kVP9) {
SbAtomicBarrier_Increment(&s_vp9_played, 1);
} else {
@@ -222,7 +225,7 @@
return starboard::FormatString(
"current_codec: %s, drm: %s, width: %d, height: %d"
", active_players (max): %d (%d), av1: ~%" PRId64 ", h264: ~%" PRId64
- ", hevc: ~%" PRId64 ", vp9: ~%" PRId64
+ ", hevc: ~%" PRId64 ", vp8: ~%" PRId64 ", vp9: ~%" PRId64
", min_width: %d, min_height: %d, max_width: %d, max_height: %d"
", last_working_codec: %s, seek_time: %s"
", first_audio_time: ~%" PRId64 ", first_video_time: ~%" PRId64
@@ -234,6 +237,7 @@
RoundValue(SbAtomicNoBarrier_Load(&s_av1_played)),
RoundValue(SbAtomicNoBarrier_Load(&s_h264_played)),
RoundValue(SbAtomicNoBarrier_Load(&s_hevc_played)),
+ RoundValue(SbAtomicNoBarrier_Load(&s_vp8_played)),
RoundValue(SbAtomicNoBarrier_Load(&s_vp9_played)),
SbAtomicNoBarrier_Load(&s_min_video_width),
SbAtomicNoBarrier_Load(&s_min_video_height),
diff --git a/cobalt/site/docs/development/setup-linux.md b/cobalt/site/docs/development/setup-linux.md
index b17e8bd..b296507 100644
--- a/cobalt/site/docs/development/setup-linux.md
+++ b/cobalt/site/docs/development/setup-linux.md
@@ -52,6 +52,10 @@
$ ccache --max-size=20G
```
+1. Install GN, which we use for our build system code. There are a few ways to
+ get the binary, follow the instructions for whichever way you prefer
+ [here](https://cobalt.googlesource.com/third_party/gn/+/refs/heads/main/#getting-a-binary).
+
1. Clone the Cobalt code repository. The following `git` command creates a
`cobalt` directory that contains the repository:
diff --git a/cobalt/site/docs/reference/starboard/modules/14/media.md b/cobalt/site/docs/reference/starboard/modules/14/media.md
index 45f6400..f7ccacd 100644
--- a/cobalt/site/docs/reference/starboard/modules/14/media.md
+++ b/cobalt/site/docs/reference/starboard/modules/14/media.md
@@ -30,6 +30,9 @@
* `kSbMediaAudioCodecEac3`
* `kSbMediaAudioCodecOpus`
* `kSbMediaAudioCodecVorbis`
+* `kSbMediaAudioCodecMp3`
+* `kSbMediaAudioCodecFlac`
+* `kSbMediaAudioCodecPcm`
### SbMediaAudioCodingType ###
diff --git a/cobalt/site/docs/reference/starboard/modules/14/system.md b/cobalt/site/docs/reference/starboard/modules/14/system.md
index 793670f..b4800a6 100644
--- a/cobalt/site/docs/reference/starboard/modules/14/system.md
+++ b/cobalt/site/docs/reference/starboard/modules/14/system.md
@@ -26,22 +26,6 @@
only if) a system has this capability will SbSystemGetTotalGPUMemory() and
SbSystemGetUsedGPUMemory() be valid to call.
-### SbSystemConnectionType ###
-
-Enumeration of network connection types.
-
-#### Values ####
-
-* `kSbSystemConnectionTypeWired`
-
- The system is on a wired connection.
-* `kSbSystemConnectionTypeWireless`
-
- The system is on a wireless connection.
-* `kSbSystemConnectionTypeUnknown`
-
- The system connection type is unknown.
-
### SbSystemDeviceType ###
Enumeration of device types.
@@ -272,16 +256,6 @@
void SbSystemClearLastError()
```
-### SbSystemGetConnectionType ###
-
-Returns the device's current network connection type.
-
-#### Declaration ####
-
-```
-SbSystemConnectionType SbSystemGetConnectionType()
-```
-
### SbSystemGetDeviceType ###
Returns the type of the device.
diff --git a/cobalt/site/docs/reference/starboard/modules/system.md b/cobalt/site/docs/reference/starboard/modules/system.md
index 793670f..b4800a6 100644
--- a/cobalt/site/docs/reference/starboard/modules/system.md
+++ b/cobalt/site/docs/reference/starboard/modules/system.md
@@ -26,22 +26,6 @@
only if) a system has this capability will SbSystemGetTotalGPUMemory() and
SbSystemGetUsedGPUMemory() be valid to call.
-### SbSystemConnectionType ###
-
-Enumeration of network connection types.
-
-#### Values ####
-
-* `kSbSystemConnectionTypeWired`
-
- The system is on a wired connection.
-* `kSbSystemConnectionTypeWireless`
-
- The system is on a wireless connection.
-* `kSbSystemConnectionTypeUnknown`
-
- The system connection type is unknown.
-
### SbSystemDeviceType ###
Enumeration of device types.
@@ -272,16 +256,6 @@
void SbSystemClearLastError()
```
-### SbSystemGetConnectionType ###
-
-Returns the device's current network connection type.
-
-#### Declaration ####
-
-```
-SbSystemConnectionType SbSystemGetConnectionType()
-```
-
### SbSystemGetDeviceType ###
Returns the type of the device.
diff --git a/cobalt/watchdog/watchdog.cc b/cobalt/watchdog/watchdog.cc
index 49349d1..3b6a888 100644
--- a/cobalt/watchdog/watchdog.cc
+++ b/cobalt/watchdog/watchdog.cc
@@ -34,7 +34,7 @@
namespace {
// The Watchdog violations json file names.
-const char kWatchdogCurrentViolationsJson[] = "watchdog.json";
+const char kWatchdogViolationsJson[] = "watchdog.json";
const char kWatchdogPreviousViolationsJson[] = "watchdog_old.json";
// The default number of microseconds between each monitor loop.
const int64_t kWatchdogSmallestTimeInterval = 1000000;
@@ -104,21 +104,19 @@
SbThreadJoin(watchdog_thread_, nullptr);
}
-std::string Watchdog::GetWatchdogFilePaths(bool current) {
- // Gets the current Watchdog violations file path or the previous Watchdog
- // violations file path.
+std::string Watchdog::GetWatchdogFilePath(bool current) {
+ // Gets the Watchdog violations file path or the previous Watchdog violations
+ // file path with lazy initialization.
if (watchdog_file_ == "") {
// Sets Watchdog violations file paths.
std::vector<char> cache_dir(kSbFileMaxPath + 1, 0);
SbSystemGetPath(kSbSystemPathCacheDirectory, cache_dir.data(),
kSbFileMaxPath);
watchdog_file_ = std::string(cache_dir.data()) + kSbFileSepString +
- std::string(kWatchdogCurrentViolationsJson);
- SB_LOG(INFO) << "Current Watchdog violations file path: " << watchdog_file_;
+ std::string(kWatchdogViolationsJson);
+ SB_LOG(INFO) << "Watchdog violations file path: " << watchdog_file_;
watchdog_old_file_ = std::string(cache_dir.data()) + kSbFileSepString +
std::string(kWatchdogPreviousViolationsJson);
- SB_LOG(INFO) << "Previous Watchdog violations file path: "
- << watchdog_old_file_;
PreservePreviousWatchdogViolations();
}
if (current) return watchdog_file_;
@@ -126,8 +124,8 @@
}
void Watchdog::PreservePreviousWatchdogViolations() {
- // Copies the previous Watchdog violations file containing violations since
- // last app start, if it exists, to preserve it.
+ // Copies the previous Watchdog violations file containing violations before
+ // app start, if it exists, to preserve it.
starboard::ScopedFile read_file(watchdog_file_.c_str(),
kSbFileOpenOnly | kSbFileRead);
if (read_file.IsValid()) {
@@ -137,7 +135,6 @@
starboard::ScopedFile write_file(watchdog_old_file_.c_str(),
kSbFileCreateAlways | kSbFileWrite);
write_file.WriteAll(&watchdog_content[0], kFileSize);
- starboard::SbFileDeleteRecursive(watchdog_file_.c_str(), true);
}
}
@@ -159,66 +156,72 @@
int64_t current_time = SbTimeToPosix(SbTimeGetNow());
SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
- std::string serialized_watchdog_index = "";
+ std::string serialized_client_map = "";
- // Iterates through Watchdog index to monitor all registered clients.
+ // Iterates through client map to monitor all registered clients.
bool new_watchdog_violation = false;
- for (auto& it : static_cast<Watchdog*>(context)->watchdog_index_) {
- // Ignores and resets clients in idle states.
- if (static_cast<Watchdog*>(context)->state_ > it.second->monitor_state) {
- it.second->time_registered_monotonic_microseconds =
- current_monotonic_time;
- it.second->time_last_pinged_microseconds = current_monotonic_time;
+ for (auto& it : static_cast<Watchdog*>(context)->client_map_) {
+ Client* client = it.second.get();
+ // Ignores and resets clients in idle states, clients whose monitor_state
+ // is below the current application state. Resets time_wait_microseconds
+ // and time_interval_microseconds deltas.
+ if (static_cast<Watchdog*>(context)->state_ > client->monitor_state) {
+ client->time_registered_monotonic_microseconds = current_monotonic_time;
+ client->time_last_pinged_microseconds = current_monotonic_time;
continue;
}
SbTimeMonotonic time_delta =
- current_monotonic_time - it.second->time_last_pinged_microseconds;
+ current_monotonic_time - client->time_last_pinged_microseconds;
SbTimeMonotonic time_wait =
current_monotonic_time -
- it.second->time_registered_monotonic_microseconds;
+ client->time_registered_monotonic_microseconds;
// Watchdog violation
- if (time_delta > it.second->time_interval_microseconds &&
- time_wait > it.second->time_wait_microseconds) {
+ if (time_delta > client->time_interval_microseconds &&
+ time_wait > client->time_wait_microseconds) {
// Reset time last pinged.
- it.second->time_last_pinged_microseconds = current_monotonic_time;
- // Get serialized Watchdog index.
- if (serialized_watchdog_index == "") {
- serialized_watchdog_index =
- static_cast<Watchdog*>(context)->GetSerializedWatchdogIndex();
+ client->time_last_pinged_microseconds = current_monotonic_time;
+ // Get serialized client map.
+ if (serialized_client_map == "") {
+ serialized_client_map =
+ static_cast<Watchdog*>(context)->GetSerializedClientMap();
}
// Updates Watchdog violations.
auto iter = (static_cast<Watchdog*>(context)->watchdog_violations_)
- .find(it.second->name);
+ .find(client->name);
bool already_violated =
iter !=
(static_cast<Watchdog*>(context)->watchdog_violations_).end();
if (already_violated) {
// Prevents excessive Watchdog violation updates.
- if (iter->second->violation_count <= kWatchdogMaxViolations)
+ Violation* violation = iter->second.get();
+ if (violation->violation_count <= kWatchdogMaxViolations)
new_watchdog_violation = true;
- iter->second->ping_infos = it.second->ping_infos;
- iter->second->violation_time_microseconds = current_time;
- iter->second->violation_delta_microseconds = time_delta;
- iter->second->violation_count++;
- iter->second->serialized_watchdog_index = serialized_watchdog_index;
+ violation->ping_infos = client->ping_infos;
+ violation->violation_time_microseconds = current_time;
+ violation->violation_delta_microseconds = time_delta;
+ violation->violation_count++;
+ violation->serialized_client_map = serialized_client_map;
} else {
new_watchdog_violation = true;
std::unique_ptr<Violation> violation(new Violation);
- *violation = *(it.second);
+ *violation = *client;
violation->violation_time_microseconds = current_time;
violation->violation_delta_microseconds = time_delta;
violation->violation_count = 1;
- violation->serialized_watchdog_index = serialized_watchdog_index;
+ violation->serialized_client_map = serialized_client_map;
(static_cast<Watchdog*>(context)->watchdog_violations_)
.emplace(violation->name, std::move(violation));
}
}
}
- if (new_watchdog_violation) SerializeWatchdogViolations(context);
+ if (new_watchdog_violation) {
+ SerializeWatchdogViolations(context);
+ MaybeTriggerCrash(context);
+ }
SB_CHECK(SbMutexRelease(&(static_cast<Watchdog*>(context))->mutex_));
SbThreadSleep(static_cast<Watchdog*>(context)->smallest_time_interval_);
@@ -226,71 +229,79 @@
return nullptr;
}
-std::string Watchdog::GetSerializedWatchdogIndex() {
- // Gets the current list of registered clients from the Watchdog index and
+std::string Watchdog::GetSerializedClientMap() {
+ // Gets the current list of registered clients from the client map and
// returns it as a serialized json string.
- std::string serialized_watchdog_index = "[";
+ std::string serialized_client_map = "[";
std::string comma = "";
- for (auto& it : watchdog_index_) {
- serialized_watchdog_index += (comma + "\"" + it.first + "\"");
+ for (auto& it : client_map_) {
+ serialized_client_map += (comma + "\"" + it.first + "\"");
comma = ", ";
}
- serialized_watchdog_index += "]";
- return serialized_watchdog_index;
+ serialized_client_map += "]";
+ return serialized_client_map;
}
void Watchdog::SerializeWatchdogViolations(void* context) {
- // Writes current Watchdog violations to persistent storage as a json file.
- std::string watchdog_json = "{\n \"watchdog_violations\": [\n";
+ // Writes Watchdog violations to persistent storage as a partial json file.
+ std::string watchdog_json = "";
std::string comma = "";
for (auto& it : static_cast<Watchdog*>(context)->watchdog_violations_) {
+ Violation* violation = it.second.get();
std::string ping_infos = "[";
std::string inner_comma = "";
- while (it.second->ping_infos.size() > 0) {
- ping_infos += (inner_comma + "\"" + it.second->ping_infos.front() + "\"");
- it.second->ping_infos.pop();
+ while (violation->ping_infos.size() > 0) {
+ ping_infos += (inner_comma + "\"" + violation->ping_infos.front() + "\"");
+ violation->ping_infos.pop();
inner_comma = ", ";
}
ping_infos += "]";
std::ostringstream ss;
ss << comma << " {\n"
- << " \"name\": \"" << it.second->name << "\",\n"
- << " \"description\": \"" << it.second->description << "\",\n"
+ << " \"name\": \"" << violation->name << "\",\n"
+ << " \"description\": \"" << violation->description << "\",\n"
<< " \"ping_infos\": " << ping_infos << ",\n"
<< " \"monitor_state\": \""
- << std::string(GetApplicationStateString(it.second->monitor_state))
+ << std::string(GetApplicationStateString(violation->monitor_state))
<< "\",\n"
<< " \"time_interval_microseconds\": "
- << it.second->time_interval_microseconds << ",\n"
+ << violation->time_interval_microseconds << ",\n"
<< " \"time_wait_microseconds\": "
- << it.second->time_wait_microseconds << ",\n"
+ << violation->time_wait_microseconds << ",\n"
<< " \"time_registered_microseconds\": "
- << it.second->time_registered_microseconds << ",\n"
+ << violation->time_registered_microseconds << ",\n"
<< " \"violation_time_microseconds\": "
- << it.second->violation_time_microseconds << ",\n"
+ << violation->violation_time_microseconds << ",\n"
<< " \"violation_delta_microseconds\": "
- << it.second->violation_delta_microseconds << ",\n"
- << " \"violation_count\": " << it.second->violation_count << ",\n"
- << " \"watchdog_index\": " << it.second->serialized_watchdog_index
- << "\n"
+ << violation->violation_delta_microseconds << ",\n"
+ << " \"violation_count\": " << violation->violation_count << ",\n"
+ << " \"client_map\": " << violation->serialized_client_map << "\n"
<< " }";
watchdog_json += ss.str();
comma = ",\n";
}
- watchdog_json += "\n ]\n}";
- SB_LOG(INFO) << "Writing Watchdog violations to: "
- << static_cast<Watchdog*>(context)->GetWatchdogFilePaths(true);
- SB_LOG(INFO) << watchdog_json;
+ // Appends previous Watchdog violations.
+ starboard::ScopedFile read_file(
+ (static_cast<Watchdog*>(context)->GetWatchdogFilePath(false)).c_str(),
+ kSbFileOpenOnly | kSbFileRead);
+ if (read_file.IsValid()) {
+ int64_t kFileSize = read_file.GetSize();
+ std::string prev_watchdog_json(kFileSize + 1, '\0');
+ read_file.ReadAll(&prev_watchdog_json[0], kFileSize);
+ prev_watchdog_json.erase(prev_watchdog_json.find('\0'));
+ watchdog_json += ",\n";
+ watchdog_json += prev_watchdog_json;
+ }
+
+ SB_LOG(INFO) << "Writing Watchdog violations:\n" << watchdog_json;
starboard::ScopedFile watchdog_file(
- (static_cast<Watchdog*>(context)->GetWatchdogFilePaths(true)).c_str(),
+ (static_cast<Watchdog*>(context)->GetWatchdogFilePath()).c_str(),
kSbFileCreateAlways | kSbFileWrite);
watchdog_file.WriteAll(watchdog_json.c_str(),
static_cast<int>(watchdog_json.size()));
-
- MaybeTriggerCrash(context);
}
void Watchdog::MaybeTriggerCrash(void* context) {
@@ -317,8 +328,8 @@
// If replace is PING or ALL, handles already registered cases.
if (replace != NONE) {
- auto it = watchdog_index_.find(name);
- bool already_registered = it != watchdog_index_.end();
+ auto it = client_map_.find(name);
+ bool already_registered = it != client_map_.end();
if (already_registered) {
if (replace == PING) {
@@ -344,7 +355,7 @@
client->time_registered_monotonic_microseconds;
// Registers.
- auto result = watchdog_index_.emplace(name, std::move(client));
+ auto result = client_map_.emplace(name, std::move(client));
// Checks for new smallest_time_interval_.
smallest_time_interval_ = std::min(smallest_time_interval_, time_interval);
@@ -364,10 +375,10 @@
if (lock) SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
// Unregisters.
- auto result = watchdog_index_.erase(name);
+ auto result = client_map_.erase(name);
// Sets new smallest_time_interval_.
smallest_time_interval_ = kWatchdogSmallestTimeInterval;
- for (auto& it : watchdog_index_) {
+ for (auto& it : client_map_) {
smallest_time_interval_ = std::min(smallest_time_interval_,
it.second->time_interval_microseconds);
}
@@ -388,8 +399,8 @@
if (is_stub_) return true;
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
- auto it = watchdog_index_.find(name);
- bool client_exists = it != watchdog_index_.end();
+ auto it = client_map_.find(name);
+ bool client_exists = it != client_map_.end();
if (client_exists) {
// Updates last ping.
@@ -408,29 +419,36 @@
return client_exists;
}
-std::string Watchdog::GetWatchdogViolations(bool current) {
- // Gets the current Watchdog violations file representing ongoing violations
- // or gets the previous Watchdog violations file containing violations from
- // previous app starts and since the last call (up to a limit).
+std::string Watchdog::GetWatchdogViolations() {
+ // Gets a json string containing the Watchdog violations since the last
+ // call (up to a limit).
// Watchdog stub
if (is_stub_) return "";
+ std::string watchdog_json = "";
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
- starboard::ScopedFile read_file(GetWatchdogFilePaths(current).c_str(),
+ starboard::ScopedFile read_file(GetWatchdogFilePath().c_str(),
kSbFileOpenOnly | kSbFileRead);
if (read_file.IsValid()) {
int64_t kFileSize = read_file.GetSize();
std::string watchdog_content(kFileSize + 1, '\0');
read_file.ReadAll(&watchdog_content[0], kFileSize);
- SB_CHECK(SbMutexRelease(&mutex_));
- SB_LOG(INFO) << "Reading Watchdog violations:\n" << watchdog_content;
- return watchdog_content;
+ watchdog_content.erase(watchdog_content.find('\0'));
+ watchdog_json = "{\n \"watchdog_violations\": [\n";
+ watchdog_json += watchdog_content;
+ watchdog_json += "\n ]\n}";
+
+ // Removes all Watchdog violations.
+ watchdog_violations_.clear();
+ starboard::SbFileDeleteRecursive(GetWatchdogFilePath().c_str(), true);
+ starboard::SbFileDeleteRecursive(GetWatchdogFilePath(false).c_str(), true);
+ SB_LOG(INFO) << "Reading Watchdog violations:\n" << watchdog_json;
} else {
- SB_CHECK(SbMutexRelease(&mutex_));
SB_LOG(INFO) << "No Watchdog Violations.";
- return "";
}
+ SB_CHECK(SbMutexRelease(&mutex_));
+ return watchdog_json;
}
bool Watchdog::GetPersistentSettingWatchdogCrash() {
diff --git a/cobalt/watchdog/watchdog.h b/cobalt/watchdog/watchdog.h
index d95f1ca..ed14db0 100644
--- a/cobalt/watchdog/watchdog.h
+++ b/cobalt/watchdog/watchdog.h
@@ -39,7 +39,12 @@
std::queue<std::string> ping_infos;
// Application state to continue monitoring client up to.
base::ApplicationState monitor_state;
+ // Maximum number of microseconds allowed between pings before triggering a
+ // Watchdog violation.
int64_t time_interval_microseconds;
+ // Number of microseconds to initially wait before Watchdog violations can be
+ // triggered. Reapplies after client resumes from idle state due to
+ // application state changes.
int64_t time_wait_microseconds;
int64_t time_registered_microseconds; // since epoch
SbTimeMonotonic time_registered_monotonic_microseconds; // since (relative)
@@ -52,16 +57,21 @@
std::string description;
// List of strings optionally provided with each Ping.
std::queue<std::string> ping_infos;
- // Application state to continue monitoring client up to.
+ // Application state to continue monitoring client up to. Inclusive.
base::ApplicationState monitor_state;
+ // Maximum number of microseconds allowed between pings before triggering a
+ // Watchdog violation.
int64_t time_interval_microseconds;
+ // Number of microseconds to initially wait before Watchdog violations can be
+ // triggered. Reapplies after client resumes from idle state due to
+ // application state changes.
int64_t time_wait_microseconds;
int64_t time_registered_microseconds; // since epoch
int64_t violation_time_microseconds; // since epoch
int64_t violation_delta_microseconds; // over time_interval
int64_t violation_count;
- // Watchdog index as a serialized json string
- std::string serialized_watchdog_index;
+ // Client map as a serialized json string
+ std::string serialized_client_map;
void operator=(const Client& c) {
name = c.name;
@@ -96,7 +106,7 @@
bool Unregister(const std::string& name, bool lock = true);
bool Ping(const std::string& name);
bool Ping(const std::string& name, const std::string& info);
- std::string GetWatchdogViolations(bool current = false);
+ std::string GetWatchdogViolations();
bool GetPersistentSettingWatchdogCrash();
void SetPersistentSettingWatchdogCrash(bool can_trigger_crash);
@@ -106,22 +116,21 @@
#endif // defined(_DEBUG)
private:
- std::string GetWatchdogFilePaths(bool current);
+ std::string GetWatchdogFilePath(bool current = true);
void PreservePreviousWatchdogViolations();
static void* Monitor(void* context);
- std::string GetSerializedWatchdogIndex();
+ std::string GetSerializedClientMap();
static void SerializeWatchdogViolations(void* context);
static void MaybeTriggerCrash(void* context);
- // Current Watchdog violations file path.
+ // Watchdog violations file paths.
std::string watchdog_file_;
- // Previous Watchdog violations file path.
std::string watchdog_old_file_;
// Creates a lock which ensures that each loop of monitor is atomic in that
// modifications to is_monitoring_, state_, smallest_time_interval_, and most
- // importantly to the dictionaries containing Watchdog clients,
- // watchdog_index_ and watchdog_violations_, only occur in between loops of
- // monitor. API functions like Register(), Unregister(), Ping(), and
+ // importantly to the dictionaries containing Watchdog clients, client_map_
+ // and watchdog_violations_, only occur in between loops of monitor. API
+ // functions like Register(), Unregister(), Ping(), and
// GetWatchdogViolations() will be called by various threads and interact
// with these class variables.
SbMutex mutex_;
@@ -134,7 +143,7 @@
// Tracks application state.
base::ApplicationState state_ = base::kApplicationStateStarted;
// Dictionary of registered Watchdog clients.
- std::unordered_map<std::string, std::unique_ptr<Client>> watchdog_index_;
+ std::unordered_map<std::string, std::unique_ptr<Client>> client_map_;
// Dictionary of Watchdog violations.
std::unordered_map<std::string, std::unique_ptr<Violation>>
watchdog_violations_;
diff --git a/docker/windows/base/build/Dockerfile b/docker/windows/base/build/Dockerfile
index 658ec30..ec4ebaa 100644
--- a/docker/windows/base/build/Dockerfile
+++ b/docker/windows/base/build/Dockerfile
@@ -29,15 +29,10 @@
# of the execution, i.e. the full invocation string.
COPY ./list_python_processes.py /list_python_processes.py
-# Helper file to ensure python2 is invoked correctly from command line.
-COPY ./python2.bat /python2/python2.bat
-
# Install deps via chocolatey.
RUN iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'));`
mkdir C:\choco-cache;`
- # Install Python 3 before Python 2, so the default on the path is python2.
choco install -y -c C:\choco-cache python3 -ia '/quiet InstallAllUsers=1 PrependPath=1 TargetDir="C:\Python3"';`
- choco install -y -c C:\choco-cache python2 --params '/InstallDir:C:\python2';`
choco install -y -c C:\choco-cache winflexbison3 --params '/InstallDir:C:\bison';`
choco install -y -c C:\choco-cache ninja;`
choco install -y -c C:\choco-cache nodejs-lts;`
@@ -47,16 +42,10 @@
Remove-Item -Force -Recurse $env:TEMP\*;`
C:\fast-win-rmdir.cmd C:\choco-cache;`
# Create version specific copy of each python executable.
- Copy-Item C:\Python3\python.exe C:\Python3\python3.exe;`
- Copy-Item C:\python2\python.exe C:\python2\python2.exe
+ Copy-Item C:\Python3\python.exe C:\Python3\python3.exe
-# Install python2 packages via PIP.
-# Additionally do the same for python3, and set various configurations.
+# Install python3 packages via PIP and set various configurations.
RUN mkdir C:\pip-cache;`
- python2 -m pip install pypiwin32 six --cache-dir C:\pip-cache;`
- C:\fast-win-rmdir.cmd C:\pip-cache;`
- # Install python3 packages via PIP.
- mkdir C:\pip-cache;`
python3 -m pip install six --cache-dir C:\pip-cache;`
C:\fast-win-rmdir.cmd C:\pip-cache;`
# Configure git global settings.
diff --git a/docker/windows/base/build/python2.bat b/docker/windows/base/build/python2.bat
deleted file mode 100644
index 9e2101e..0000000
--- a/docker/windows/base/build/python2.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-@echo off
-setlocal
-c:\python2\python.exe %*
diff --git a/docker/windows/win32/unittest/Dockerfile b/docker/windows/win32/unittest/Dockerfile
deleted file mode 100644
index 780c58c..0000000
--- a/docker/windows/win32/unittest/Dockerfile
+++ /dev/null
@@ -1,40 +0,0 @@
-# escape=`
-
-# Copyright 2021 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-FROM mcr.microsoft.com/windows:1809
-
-# Restore the default Windows shell for correct batch processing.
-SHELL ["cmd", "/S", "/C"]
-
-RUN powershell -ExecutionPolicy unrestricted -Command `
- iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
-
-# Needed to extract the app launcher scripts for running tests.
-RUN choco install -f -y unzip
-
-# Install python2
-RUN choco install -f -y python2 --params '/InstallDir:C:\python2'
-RUN pip install pypiwin32
-RUN setx path "%path%;C:\Python2"
-RUN setx python2 "C:\Python2\python.exe"
-ADD ./python2.bat /python2/python2.bat
-
-# Install visual C runtime redistributables
-ADD https://aka.ms/vs/16/release/vc_redist.x64.exe C:/vcredist_x64.exe
-RUN C:/vcredist_x64.exe /install /passive /norestart /log out.txt
-
-ADD ./unittest.cmd /unittest.cmd
-
-CMD [ "/unittest.cmd", "${PLATFORM}", "${CONFIG}", "${TEST}"]
diff --git a/docker/windows/win32/unittest/python2.bat b/docker/windows/win32/unittest/python2.bat
deleted file mode 100644
index 9e2101e..0000000
--- a/docker/windows/win32/unittest/python2.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-@echo off
-setlocal
-c:\python2\python.exe %*
diff --git a/docker/windows/win32/unittest/unittest.cmd b/docker/windows/win32/unittest/unittest.cmd
deleted file mode 100644
index 36b1426..0000000
--- a/docker/windows/win32/unittest/unittest.cmd
+++ /dev/null
@@ -1,7 +0,0 @@
-if "%1" == "" set "PLATFORM=win-win32"
-if "%2" == "" set "CONFIG=devel"
-if "%3" == "" set "TEST=eztime_test"
-
-cd c:\code\out\%PLATFORM%_%CONFIG%\
-unzip app_launcher.zip -d c:\app_launcher_out\
-%python2% c:\app_launcher_out\starboard\tools\testing\test_runner.py --run --platform %PLATFORM% --config %CONFIG% -o c:\code\out\%PLATFORM%_%CONFIG%
diff --git a/glimp/egl/display.cc b/glimp/egl/display.cc
index 3747d9c..8cfac3c 100644
--- a/glimp/egl/display.cc
+++ b/glimp/egl/display.cc
@@ -27,12 +27,6 @@
namespace glimp {
namespace egl {
-// sce::Gnm::submitDone() is expected to be called once every 5 secs:
-// https://ps4.siedev.net/resources/documents/SDK/9.500/Programming-Startup_Guide/0006.html#__document_toc_00000034
-// sce::Gnm::submitDone() gets called regularly in the rasterizer thread and
-// should be called from the main thread when the app is in suspended state.
-// kSubmitDoneDelay is set to 1/60sec (same scheduling frequency as
-// the app in foreground)
const SbTime kSubmitDoneDelay = kSbTimeSecond / 60;
// Don't repeat the submitDone callback during suspension
diff --git a/glimp/egl/display_impl.h b/glimp/egl/display_impl.h
index caf3c3a..71ac0d1 100644
--- a/glimp/egl/display_impl.h
+++ b/glimp/egl/display_impl.h
@@ -53,8 +53,7 @@
// Creates and returns a new DisplayImpl object.
// To be implemented by each implementing platform.
static nb::scoped_ptr<DisplayImpl> Create(EGLNativeDisplayType display_id);
- // This method is declared here and defined in ps4/egl/display_impl_ps4.cc
- // and ps5/egl/display_impl_ps5.cc respectively.
+ // Submit done call.
static void CallSubmitDone();
// Returns the EGL major and minor versions, if they are not NULL.
diff --git a/starboard/android/apk/app/CMakeLists.txt b/starboard/android/apk/app/CMakeLists.txt
index b3f36b7..d5d598f 100644
--- a/starboard/android/apk/app/CMakeLists.txt
+++ b/starboard/android/apk/app/CMakeLists.txt
@@ -78,7 +78,7 @@
DEPENDS coat_lib
COMMAND ${CMAKE_COMMAND} -E create_symlink
${COBALT_CONTENT_DIR}
- ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../../cobalt_content
+ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../../../../cobalt_content
)
# We need a target (not a file) for the phony native dependency below.
diff --git a/starboard/android/apk/app/build.gradle b/starboard/android/apk/app/build.gradle
index 568d689..2712f25 100644
--- a/starboard/android/apk/app/build.gradle
+++ b/starboard/android/apk/app/build.gradle
@@ -42,8 +42,8 @@
println "TARGET: ${cobaltTarget}"
android {
- compileSdkVersion 'android-30'
- buildToolsVersion '30.0.0'
+ compileSdkVersion 'android-31'
+ buildToolsVersion '31.0.0'
ndkVersion NDK_VERSION
compileOptions {
@@ -72,8 +72,8 @@
}
defaultConfig {
applicationId "dev.cobalt.coat"
- minSdkVersion 21
- targetSdkVersion 30
+ minSdkVersion 24
+ targetSdkVersion 31
versionCode 1
versionName "${buildId}"
manifestPlaceholders = [applicationName: "CoAT: ${cobaltTarget}"]
@@ -140,16 +140,16 @@
}
// Add the directories symlinked by the CMake "cobalt_content" custom command.
debug {
- assets.srcDir "${buildDir}/intermediates/cmake/debug/cobalt_content"
+ assets.srcDir "${buildDir}/intermediates/cxx/cobalt_content"
}
devel {
- assets.srcDir "${buildDir}/intermediates/cmake/devel/cobalt_content"
+ assets.srcDir "${buildDir}/intermediates/cxx/cobalt_content"
}
qa {
- assets.srcDir "${buildDir}/intermediates/cmake/qa/cobalt_content"
+ assets.srcDir "${buildDir}/intermediates/cxx/cobalt_content"
}
release {
- assets.srcDir "${buildDir}/intermediates/cmake/release/cobalt_content"
+ assets.srcDir "${buildDir}/intermediates/cxx/cobalt_content"
}
}
externalNativeBuild {
diff --git a/starboard/android/apk/app/src/app/AndroidManifest.xml b/starboard/android/apk/app/src/app/AndroidManifest.xml
index 78b3400..42d8bc0 100644
--- a/starboard/android/apk/app/src/app/AndroidManifest.xml
+++ b/starboard/android/apk/app/src/app/AndroidManifest.xml
@@ -42,6 +42,7 @@
android:label="${applicationName}">
<activity android:name="dev.cobalt.app.MainActivity"
+ android:exported="true"
android:launchMode="singleTask"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|uiMode"
android:screenOrientation="sensorLandscape"
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index 902ca00..f9ceeee 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -58,9 +58,7 @@
import java.util.Calendar;
import java.util.Enumeration;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Locale;
-import java.util.Set;
import java.util.TimeZone;
/** Implementation of the required JNI methods called by the Starboard C++ code. */
@@ -111,10 +109,6 @@
private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("America/Los_Angeles");
private final long timeNanosecondsPerMicrosecond = 1000;
- private Set<Integer> supportedHdrTypesSet = new HashSet<Integer>();
- private long supportedHdrTypesSetUpdatedAt = 0;
- private final long supportedHdrTypesCacheTtlMs = 1000;
-
public StarboardBridge(
Context appContext,
Holder<Activity> activityHolder,
@@ -747,53 +741,22 @@
}
}
- long supportedHdrTypesSetUpdatedAtNs;
-
- private void refreshHdrTypesCacheIfNecessary() {
- if (System.currentTimeMillis() - supportedHdrTypesSetUpdatedAt < supportedHdrTypesCacheTtlMs) {
- // Cache is up to date.
- return;
- }
- supportedHdrTypesSet.clear();
- supportedHdrTypesSetUpdatedAt = System.currentTimeMillis();
-
+ /** Return supported hdr types. */
+ @RequiresApi(24)
+ @SuppressWarnings("unused")
+ @UsedByNative
+ public int[] getSupportedHdrTypes() {
Display defaultDisplay = DisplayUtil.getDefaultDisplay();
if (defaultDisplay == null) {
- return;
+ return null;
}
Display.HdrCapabilities hdrCapabilities = defaultDisplay.getHdrCapabilities();
if (hdrCapabilities == null) {
- return;
+ return null;
}
- int[] supportedHdrTypes = hdrCapabilities.getSupportedHdrTypes();
- if (supportedHdrTypes == null) {
- return;
- }
-
- for (int supportedType : supportedHdrTypes) {
- supportedHdrTypesSet.add(supportedType);
- }
- }
-
- /**
- * Check if hdrType is supported by the current default display. See
- * https://developer.android.com/reference/android/view/Display.HdrCapabilities.html for valid
- * values.
- */
- @RequiresApi(24)
- @SuppressWarnings("unused")
- @UsedByNative
- public boolean isHdrTypeSupported(int hdrType) {
- if (android.os.Build.VERSION.SDK_INT < 24) {
- return false;
- }
-
- synchronized (this) {
- refreshHdrTypesCacheIfNecessary();
- return supportedHdrTypesSet.contains(hdrType);
- }
+ return hdrCapabilities.getSupportedHdrTypes();
}
/** Return the CobaltMediaSession. */
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
index 69c2e78..4c11399 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
@@ -386,4 +386,22 @@
}
return audioTrack.getUnderrunCount();
}
+
+ @SuppressWarnings("unused")
+ @UsedByNative
+ private int getStartThresholdInFrames() {
+ if (Build.VERSION.SDK_INT >= 31) {
+ return getStartThresholdInFramesV31();
+ }
+ return 0;
+ }
+
+ @RequiresApi(31)
+ private int getStartThresholdInFramesV31() {
+ if (audioTrack == null) {
+ Log.e(TAG, "Unable to call getStartThresholdInFrames() with NULL audio track.");
+ return 0;
+ }
+ return audioTrack.getStartThresholdInFrames();
+ }
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index 9012df2..c5c66cd 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -24,6 +24,7 @@
import android.media.MediaCodec;
import android.media.MediaCodec.CryptoInfo;
import android.media.MediaCodec.CryptoInfo.Pattern;
+import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.VideoCapabilities;
import android.media.MediaCrypto;
@@ -525,22 +526,24 @@
public static MediaCodecBridge createAudioMediaCodecBridge(
long nativeMediaCodecBridge,
String mime,
+ String decoderName,
int sampleRate,
int channelCount,
MediaCrypto crypto,
@Nullable byte[] configurationData) {
+ if (decoderName.equals("")) {
+ Log.e(TAG, "Invalid decoder name.");
+ return null;
+ }
MediaCodec mediaCodec = null;
try {
- String decoderName =
- MediaCodecUtil.findAudioDecoder(mime, 0, false /* mustSupportTunnelMode */);
- if (decoderName.equals("")) {
- Log.e(TAG, String.format("Failed to find decoder: %s", mime));
- return null;
- }
Log.i(TAG, String.format("Creating \"%s\" decoder.", decoderName));
mediaCodec = MediaCodec.createByCodecName(decoderName);
} catch (Exception e) {
- Log.e(TAG, String.format("Failed to create MediaCodec: %s, ", mime), e);
+ Log.e(
+ TAG,
+ String.format("Failed to create MediaCodec: %s, DecoderName: %s", mime, decoderName),
+ e);
return null;
}
if (mediaCodec == null) {
@@ -586,9 +589,7 @@
public static void createVideoMediaCodecBridge(
long nativeMediaCodecBridge,
String mime,
- boolean mustSupportSecure,
- boolean mustSupportSoftwareCodec,
- boolean forceImprovedSupportCheck,
+ String decoderName,
int width,
int height,
int fps,
@@ -600,54 +601,21 @@
MediaCodec mediaCodec = null;
outCreateMediaCodecBridgeResult.mMediaCodecBridge = null;
- boolean mustSupportHdr = android.os.Build.VERSION.SDK_INT >= 24 && colorInfo != null;
- boolean mustSupportTunneled = tunnelModeAudioSessionId != -1;
- // On first pass, try to find a decoder with HDR if the color info is non-null.
- MediaCodecUtil.FindVideoDecoderResult findVideoDecoderResult =
- MediaCodecUtil.findVideoDecoder(
- mime,
- mustSupportSecure,
- mustSupportHdr,
- mustSupportSoftwareCodec,
- mustSupportTunneled,
- forceImprovedSupportCheck,
- -1 /* decoderCacheTtlMs */,
- 0 /* frameWidth */,
- 0 /* frameHeight */,
- 0 /* bitrate */,
- 0 /* fps */);
- if (findVideoDecoderResult.name.equals("") && mustSupportHdr) {
- // On second pass, forget HDR.
- findVideoDecoderResult =
- MediaCodecUtil.findVideoDecoder(
- mime,
- mustSupportSecure,
- false /* mustSupportHdr */,
- mustSupportSoftwareCodec,
- mustSupportTunneled,
- forceImprovedSupportCheck,
- -1 /* decoderCacheTtlMs */,
- 0 /* frameWidth */,
- 0 /* frameHeight */,
- 0 /* bitrate */,
- 0 /* fps */);
+ if (decoderName.equals("")) {
+ String message = "Invalid decoder name.";
+ Log.e(TAG, message);
+ outCreateMediaCodecBridgeResult.mErrorMessage = message;
+ return;
}
+
try {
- String decoderName = findVideoDecoderResult.name;
- if (decoderName.equals("") || findVideoDecoderResult.videoCapabilities == null) {
- String message =
- String.format(
- "Failed to find decoder: %s, mustSupportSecure: %s", mime, mustSupportSecure);
- Log.e(TAG, message);
- outCreateMediaCodecBridgeResult.mErrorMessage = message;
- return;
- }
Log.i(TAG, String.format("Creating \"%s\" decoder.", decoderName));
mediaCodec = MediaCodec.createByCodecName(decoderName);
} catch (Exception e) {
String message =
String.format(
- "Failed to create MediaCodec: %s, mustSupportSecure: %s", mime, mustSupportSecure);
+ "Failed to create MediaCodec: %s, mustSupportSecure: %s," + " DecoderName: %s",
+ mime, crypto != null, decoderName);
Log.e(TAG, message, e);
outCreateMediaCodecBridgeResult.mErrorMessage = message;
return;
@@ -656,6 +624,23 @@
outCreateMediaCodecBridgeResult.mErrorMessage = "mediaCodec is null";
return;
}
+
+ MediaCodecInfo codecInfo = mediaCodec.getCodecInfo();
+ if (codecInfo == null) {
+ outCreateMediaCodecBridgeResult.mErrorMessage = "codecInfo is null";
+ return;
+ }
+ CodecCapabilities codecCapabilities = codecInfo.getCapabilitiesForType(mime);
+ if (codecCapabilities == null) {
+ outCreateMediaCodecBridgeResult.mErrorMessage = "codecCapabilities is null";
+ return;
+ }
+ VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
+ if (videoCapabilities == null) {
+ outCreateMediaCodecBridgeResult.mErrorMessage = "videoCapabilities is null";
+ return;
+ }
+
MediaCodecBridge bridge =
new MediaCodecBridge(
nativeMediaCodecBridge,
@@ -664,13 +649,10 @@
true,
BitrateAdjustmentTypes.NO_ADJUSTMENT,
tunnelModeAudioSessionId);
- MediaFormat mediaFormat =
- createVideoDecoderFormat(mime, width, height, findVideoDecoderResult.videoCapabilities);
+ MediaFormat mediaFormat = createVideoDecoderFormat(mime, width, height, videoCapabilities);
boolean shouldConfigureHdr =
- android.os.Build.VERSION.SDK_INT >= 24
- && colorInfo != null
- && MediaCodecUtil.isHdrCapableVideoDecoder(mime, findVideoDecoderResult);
+ colorInfo != null && MediaCodecUtil.isHdrCapableVideoDecoder(mime, codecCapabilities);
if (shouldConfigureHdr) {
Log.d(TAG, "Setting HDR info.");
mediaFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer);
@@ -688,7 +670,6 @@
Log.d(TAG, "Enabled tunnel mode playback on audio session " + tunnelModeAudioSessionId);
}
- VideoCapabilities videoCapabilities = findVideoDecoderResult.videoCapabilities;
int maxWidth = videoCapabilities.getSupportedWidths().getUpper();
int maxHeight = videoCapabilities.getSupportedHeights().getUpper();
if (fps > 0) {
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
index bd1461e..2d023be 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
@@ -31,6 +31,8 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -41,8 +43,6 @@
// A high priority allow list of brands/model that should always attempt to
// play vp9.
private static final Map<String, Set<String>> vp9AllowList = new HashMap<>();
- // An allow list of software codec names that can be used.
- private static final Set<String> softwareCodecAllowList = new HashSet<>();
// Whether we should report vp9 codecs as supported or not. Will be set
// based on whether vp9AllowList contains our brand/model. If this is set
// to true, then videoCodecDenyList will be ignored.
@@ -51,23 +51,6 @@
private static final String VP9_MIME_TYPE = "video/x-vnd.on2.vp9";
private static final String AV1_MIME_TYPE = "video/av01";
- /**
- * A simple "struct" to bundle up the results from findVideoDecoder, as its clients may require
- * the max supported width and height in addition to just the decoder name.
- */
- public static final class FindVideoDecoderResult {
- public String name;
- public VideoCapabilities videoCapabilities;
- public CodecCapabilities codecCapabilities;
-
- public FindVideoDecoderResult(
- String name, VideoCapabilities videoCapabilities, CodecCapabilities codecCapabilities) {
- this.name = name;
- this.videoCapabilities = videoCapabilities;
- this.codecCapabilities = codecCapabilities;
- }
- }
-
static {
if (Build.VERSION.SDK_INT >= 24 && Build.BRAND.equals("google")) {
videoCodecDenyList.add("OMX.Nvidia.vp9.decode");
@@ -377,61 +360,108 @@
isVp9AllowListed =
vp9AllowList.containsKey(Build.BRAND)
&& vp9AllowList.get(Build.BRAND).contains(Build.MODEL);
-
- softwareCodecAllowList.add("OMX.google.h264.decoder");
}
private MediaCodecUtil() {}
- /**
- * Returns whether a given combination of (frame width x frame height) frames at bitrate and fps
- * has a decoder with mime type.
- *
- * <p>Setting any of the int parameters to 0 indicates that they shouldn't be considered.
- */
- @SuppressWarnings("unused")
- @UsedByNative
- public static boolean hasVideoDecoderFor(
- String mimeType,
- boolean mustSupportSecure,
- boolean mustSupportHdr,
- boolean mustSupportTunnelMode,
- boolean forceImprovedSupportCheck,
- int decoderCacheTtlMs,
- int frameWidth,
- int frameHeight,
- int bitrate,
- int fps) {
- FindVideoDecoderResult findVideoDecoderResult =
- findVideoDecoder(
- mimeType,
- mustSupportSecure,
- mustSupportHdr,
- false /* mustSupportSoftwareCodec */,
- mustSupportTunnelMode,
- forceImprovedSupportCheck,
- decoderCacheTtlMs,
- frameWidth,
- frameHeight,
- bitrate,
- fps);
- return !findVideoDecoderResult.name.isEmpty();
+ /** A wrapper class of codec capability infos. */
+ public static class CodecCapabilityInfo {
+ CodecCapabilityInfo(MediaCodecInfo codecInfo, String mimeType) {
+ this.codecInfo = codecInfo;
+ this.mimeType = mimeType;
+ this.decoderName = codecInfo.getName();
+ this.codecCapabilities = codecInfo.getCapabilitiesForType(mimeType);
+ this.audioCapabilities = this.codecCapabilities.getAudioCapabilities();
+ this.videoCapabilities = this.codecCapabilities.getVideoCapabilities();
+ }
+
+ public MediaCodecInfo codecInfo;
+ public String mimeType;
+ public String decoderName;
+ public CodecCapabilities codecCapabilities;
+ public AudioCapabilities audioCapabilities;
+ public VideoCapabilities videoCapabilities;
+
+ public boolean isSecureRequired() {
+ // MediaCodecList is supposed to feed us names of decoders that do NOT end in ".secure". We
+ // are then supposed to check if FEATURE_SecurePlayback is supported, and if it is and we
+ // want a secure codec, we append ".secure" ourselves, and then pass that to
+ // MediaCodec.createDecoderByName. Some devices, do not follow this spec, and show us
+ // decoders that end in ".secure". Empirically, FEATURE_SecurePlayback has still been
+ // correct when this happens.
+ if (this.decoderName.endsWith(SECURE_DECODER_SUFFIX)) {
+ // If a decoder name ends with ".secure", then we don't want to use it for clear content.
+ return true;
+ }
+ return this.codecCapabilities.isFeatureRequired(
+ MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+ }
+
+ public boolean isSecureSupported() {
+ return this.codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+ }
+
+ public boolean isTunnelModeRequired() {
+ return this.codecCapabilities.isFeatureRequired(
+ MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
+ }
+
+ public boolean isTunnelModeSupported() {
+ return this.codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
+ }
+
+ public boolean isSoftware() {
+ return isSoftwareDecoder(this.codecInfo);
+ }
+
+ public boolean isHdrCapable() {
+ return isHdrCapableVideoDecoder(this.mimeType, this.codecCapabilities);
+ }
}
- /**
- * Returns whether an audio decoder that supports mimeType at bitrate. Setting bitrate to 0
- * indicates that it should not be considered.
- */
+ /** Returns an array of CodecCapabilityInfo for all available decoder. */
@SuppressWarnings("unused")
@UsedByNative
- public static boolean hasAudioDecoderFor(
- String mimeType, int bitrate, boolean mustSupportTunnelMode) {
- return !findAudioDecoder(mimeType, bitrate, mustSupportTunnelMode).equals("");
+ public static CodecCapabilityInfo[] getAllCodecCapabilityInfos() {
+ List<CodecCapabilityInfo> codecCapabilityInfos = new ArrayList<>();
+
+ for (MediaCodecInfo codecInfo : new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos()) {
+ // We don't use encoder.
+ if (codecInfo.isEncoder()) {
+ continue;
+ }
+
+ // Filter blacklisted video decoders.
+ String name = codecInfo.getName();
+ if (!isVp9AllowListed && videoCodecDenyList.contains(name)) {
+ Log.v(TAG, String.format("Rejecting %s, reason: codec is on deny list", name));
+ continue;
+ }
+ if (name.endsWith(SECURE_DECODER_SUFFIX)) {
+ // If we want a secure decoder, then make sure the version without ".secure" isn't
+ // denylisted.
+ String nameWithoutSecureSuffix =
+ name.substring(0, name.length() - SECURE_DECODER_SUFFIX.length());
+ if (!isVp9AllowListed && videoCodecDenyList.contains(nameWithoutSecureSuffix)) {
+ String format = "Rejecting %s, reason: offpsec denylisted secure decoder";
+ Log.v(TAG, String.format(format, name));
+ continue;
+ }
+ }
+
+ for (String mimeType : codecInfo.getSupportedTypes()) {
+ codecCapabilityInfos.add(new CodecCapabilityInfo(codecInfo, mimeType));
+ }
+ }
+ CodecCapabilityInfo[] array = new CodecCapabilityInfo[codecCapabilityInfos.size()];
+ return codecCapabilityInfos.toArray(array);
}
- /** Determine whether findVideoDecoderResult is capable of playing HDR. */
+ /** Determine whether codecCapabilities is capable of playing HDR. */
public static boolean isHdrCapableVideoDecoder(
- String mimeType, FindVideoDecoderResult findVideoDecoderResult) {
+ String mimeType, CodecCapabilities codecCapabilities) {
// VP9Profile* values were not added until API level 24. See
// https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel.html.
if (Build.VERSION.SDK_INT < 24) {
@@ -443,7 +473,6 @@
return false;
}
- CodecCapabilities codecCapabilities = findVideoDecoderResult.codecCapabilities;
if (codecCapabilities == null) {
return false;
}
@@ -467,14 +496,34 @@
return false;
}
+ /** Return true if and only if info belongs to a software decoder. */
+ public static boolean isSoftwareDecoder(MediaCodecInfo codecInfo) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ return !codecInfo.isHardwareAccelerated();
+ }
+ String name = codecInfo.getName().toLowerCase(Locale.ROOT);
+ // This is taken from libstagefright/OMXCodec.cpp for pre codec2.
+ if (name.startsWith("omx.google.")) {
+ return true;
+ }
+
+ // Codec2 names sw decoders this way.
+ // See hardware/google/av/codec2/vndk/C2Store.cpp.
+ if (name.startsWith("c2.google.") || name.startsWith("c2.android.")) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
- * The same as hasVideoDecoderFor, returns a FindVideoDecoderResult constructed from the video
- * decoder if it is found, or an empty instance otherwise.
+ * The same as hasVideoDecoderFor, returns the name of the video decoder if it is found, or ""
+ * otherwise.
*
* <p>NOTE: This code path is called repeatedly by the player to determine the decoding
* capabilities of the device. To ensure speedy playback the code below should be kept performant.
*/
- public static FindVideoDecoderResult findVideoDecoder(
+ public static String findVideoDecoder(
String mimeType,
boolean mustSupportSecure,
boolean mustSupportHdr,
@@ -516,15 +565,16 @@
for (VideoDecoderCache.CachedDecoder decoder :
VideoDecoderCache.getCachedDecoders(mimeType, decoderCacheTtlMs)) {
String name = decoder.info.getName();
- if (mustSupportSoftwareCodec && !softwareCodecAllowList.contains(name)) {
- Log.v(TAG, "Rejecting " + name + ", reason: require software codec");
- continue;
- }
if (!isVp9AllowListed && videoCodecDenyList.contains(name)) {
Log.v(TAG, "Rejecting " + name + ", reason: codec is on deny list");
continue;
}
+ if (mustSupportSoftwareCodec && !isSoftwareDecoder(decoder.info)) {
+ Log.v(TAG, "Rejecting " + name + ", reason: require software codec");
+ continue;
+ }
+
// MediaCodecList is supposed to feed us names of decoders that do NOT end in ".secure". We
// are then supposed to check if FEATURE_SecurePlayback is supported, and if it is and we
// want a secure codec, we append ".secure" ourselves, and then pass that to
@@ -664,20 +714,19 @@
}
}
+ if (mustSupportHdr && !isHdrCapableVideoDecoder(mimeType, decoder.codecCapabilities)) {
+ Log.v(TAG, "Rejecting " + name + ", reason: codec does not support HDR");
+ continue;
+ }
+
String resultName =
(mustSupportSecure && !name.endsWith(SECURE_DECODER_SUFFIX))
? (name + SECURE_DECODER_SUFFIX)
: name;
- FindVideoDecoderResult findVideoDecoderResult =
- new FindVideoDecoderResult(resultName, videoCapabilities, decoder.codecCapabilities);
- if (mustSupportHdr && !isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult)) {
- Log.v(TAG, "Rejecting " + name + ", reason: codec does not support HDR");
- continue;
- }
Log.v(TAG, "Found suitable decoder, " + name);
- return findVideoDecoderResult;
+ return resultName;
}
- return new FindVideoDecoderResult("", null, null);
+ return "";
}
/**
@@ -841,10 +890,8 @@
&& !name.endsWith(SECURE_DECODER_SUFFIX))
? (name + SECURE_DECODER_SUFFIX)
: name;
- FindVideoDecoderResult findVideoDecoderResult =
- new FindVideoDecoderResult(resultName, videoCapabilities, codecCapabilities);
boolean isHdrCapable =
- isHdrCapableVideoDecoder(codecCapabilities.getMimeType(), findVideoDecoderResult);
+ isHdrCapableVideoDecoder(codecCapabilities.getMimeType(), codecCapabilities);
if (videoCapabilities != null) {
String frameRateAndResolutionString =
getSupportedResolutionsAndFrameRates(videoCapabilities, isHdrCapable);
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
index b712376..1d3428e 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
@@ -170,14 +170,22 @@
* @return true if the container and the crypto scheme is supported, or false otherwise.
*/
@UsedByNative
- static boolean isWidevineCryptoSchemeSupported(boolean usesCbcs) {
- if (Build.VERSION.SDK_INT < 24 && usesCbcs) {
- Log.e(TAG, "Encryption scheme 'cbcs' is not supported on this platform.");
- return false;
- }
+ static boolean isWidevineCryptoSchemeSupported() {
return MediaDrm.isCryptoSchemeSupported(WIDEVINE_UUID);
}
+ /**
+ * Check whether `cbcs` scheme is supported.
+ *
+ * @return true if the `cbcs` encryption is supported, or false otherwise.
+ */
+ @UsedByNative
+ static boolean isCbcsSchemeSupported() {
+ // While 'cbcs' scheme was originally implemented in N, there was a bug (in the
+ // DRM code) which means that it didn't really work properly until N-MR1).
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
+ }
+
/** Destroy the MediaDrmBridge object. */
@UsedByNative
void destroy() {
diff --git a/starboard/android/apk/build.gradle b/starboard/android/apk/build.gradle
index 302652a..3db6ad6 100644
--- a/starboard/android/apk/build.gradle
+++ b/starboard/android/apk/build.gradle
@@ -20,7 +20,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.1.0'
+ classpath 'com.android.tools.build:gradle:7.0.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties b/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties
index 186b715..29e4134 100644
--- a/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties
+++ b/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/starboard/android/shared/BUILD.gn b/starboard/android/shared/BUILD.gn
index 599bd7d..5efe78e 100644
--- a/starboard/android/shared/BUILD.gn
+++ b/starboard/android/shared/BUILD.gn
@@ -334,6 +334,8 @@
"log_is_tty.cc",
"log_raw.cc",
"main.cc",
+ "media_capabilities_cache.cc",
+ "media_capabilities_cache.h",
"media_codec_bridge.cc",
"media_codec_bridge.h",
"media_common.h",
@@ -446,6 +448,18 @@
"player_components_factory.cc",
]
}
+
+ if (sb_is_evergreen_compatible) {
+ public_deps += [ "//starboard/elf_loader:evergreen_config" ]
+
+ if (!sb_evergreen_compatible_enable_lite) {
+ public_deps += [ "//starboard/loader_app:pending_restart" ]
+ }
+ }
+
+ if (sb_evergreen_compatible_use_libunwind) {
+ deps += [ "//third_party/llvm-project/libunwind:unwind_starboard" ]
+ }
}
static_library("starboard_base_symbolize") {
diff --git a/starboard/android/shared/audio_sink_min_required_frames_tester.cc b/starboard/android/shared/audio_sink_min_required_frames_tester.cc
index 80656e2..92ca9e4 100644
--- a/starboard/android/shared/audio_sink_min_required_frames_tester.cc
+++ b/starboard/android/shared/audio_sink_min_required_frames_tester.cc
@@ -131,16 +131,30 @@
wait_timeout = !condition_variable_.WaitTimed(kSbTimeSecond * 5);
}
- if (wait_timeout) {
- SB_LOG(ERROR) << "Audio sink min required frames tester timeout.";
- }
+ // Get start threshold before release the audio sink.
+ int start_threshold = audio_sink_->GetStartThresholdInFrames();
+ // |min_required_frames_| is shared between two threads. Release audio sink
+ // to end audio sink thread before access |min_required_frames_| on this
+ // thread.
delete audio_sink_;
audio_sink_ = nullptr;
- // Call |received_cb_| after audio sink thread is ended.
- // |min_required_frames_| is shared between two threads.
- if (!destroying_.load() && !wait_timeout) {
+ if (wait_timeout) {
+ SB_LOG(ERROR) << "Audio sink min required frames tester timeout.";
+ // Overwrite |min_required_frames_| if failed to get a stable result.
+ min_required_frames_ = max_required_frames_;
+ }
+
+ if (start_threshold > min_required_frames_) {
+ SB_LOG(INFO) << "Audio sink min required frames is overwritten from "
+ << min_required_frames_ << " to audio track start threshold "
+ << start_threshold << ".";
+ // Overwrite |min_required_frames_| to match |start_threshold|.
+ min_required_frames_ = start_threshold;
+ }
+
+ if (!destroying_.load()) {
task.received_cb(task.number_of_channels, task.sample_type,
task.sample_rate, min_required_frames_);
}
diff --git a/starboard/android/shared/audio_track_audio_sink_type.cc b/starboard/android/shared/audio_track_audio_sink_type.cc
index 45d90b0..d055a29 100644
--- a/starboard/android/shared/audio_track_audio_sink_type.cc
+++ b/starboard/android/shared/audio_track_audio_sink_type.cc
@@ -389,6 +389,10 @@
return bridge_.GetUnderrunCount();
}
+int AudioTrackAudioSink::GetStartThresholdInFrames() {
+ return bridge_.GetStartThresholdInFrames();
+}
+
// static
int AudioTrackAudioSinkType::GetMinBufferSizeInFrames(
int channels,
diff --git a/starboard/android/shared/audio_track_audio_sink_type.h b/starboard/android/shared/audio_track_audio_sink_type.h
index 035b093..38c7014 100644
--- a/starboard/android/shared/audio_track_audio_sink_type.h
+++ b/starboard/android/shared/audio_track_audio_sink_type.h
@@ -125,6 +125,7 @@
void SetVolume(double volume) override;
int GetUnderrunCount();
+ int GetStartThresholdInFrames();
private:
static void* ThreadEntryPoint(void* context);
diff --git a/starboard/android/shared/audio_track_bridge.cc b/starboard/android/shared/audio_track_bridge.cc
index aad3562..629a96e 100644
--- a/starboard/android/shared/audio_track_bridge.cc
+++ b/starboard/android/shared/audio_track_bridge.cc
@@ -296,6 +296,15 @@
"()I");
}
+int AudioTrackBridge::GetStartThresholdInFrames(
+ JniEnvExt* env /*= JniEnvExt::Get()*/) {
+ SB_DCHECK(env);
+ SB_DCHECK(is_valid());
+
+ return env->CallIntMethodOrAbort(j_audio_track_bridge_,
+ "getStartThresholdInFrames", "()I");
+}
+
} // namespace shared
} // namespace android
} // namespace starboard
diff --git a/starboard/android/shared/audio_track_bridge.h b/starboard/android/shared/audio_track_bridge.h
index 9ddb4f1..1844151 100644
--- a/starboard/android/shared/audio_track_bridge.h
+++ b/starboard/android/shared/audio_track_bridge.h
@@ -83,6 +83,7 @@
JniEnvExt* env = JniEnvExt::Get());
bool GetAndResetHasAudioDeviceChanged(JniEnvExt* env = JniEnvExt::Get());
int GetUnderrunCount(JniEnvExt* env = JniEnvExt::Get());
+ int GetStartThresholdInFrames(JniEnvExt* env = JniEnvExt::Get());
private:
int max_samples_per_write_;
diff --git a/starboard/android/shared/download_sdk.sh b/starboard/android/shared/download_sdk.sh
index d968f8b..e05cef4 100755
--- a/starboard/android/shared/download_sdk.sh
+++ b/starboard/android/shared/download_sdk.sh
@@ -44,14 +44,14 @@
# Update the installation
${SDK_MANAGER_TOOL} --sdk_root=${ANDROID_SDK_ROOT} \
- "build-tools;30.0.0" \
+ "build-tools;31.0.0" \
"cmake;3.10.2.4988404" \
"cmdline-tools;1.0" \
"extras;android;m2repository" \
"extras;google;m2repository" \
"ndk;21.1.6352462" \
"patcher;v4" \
- "platforms;android-30" \
+ "platforms;android-31" \
"platform-tools"
echo "Android SDK updated."
diff --git a/starboard/android/shared/media_capabilities_cache.cc b/starboard/android/shared/media_capabilities_cache.cc
new file mode 100644
index 0000000..53360ea
--- /dev/null
+++ b/starboard/android/shared/media_capabilities_cache.cc
@@ -0,0 +1,531 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/android/shared/media_capabilities_cache.h"
+
+#include <utility>
+
+#include "starboard/android/shared/jni_utils.h"
+#include "starboard/android/shared/media_common.h"
+#include "starboard/common/log.h"
+#include "starboard/once.h"
+
+namespace starboard {
+namespace android {
+namespace shared {
+namespace {
+
+// https://developer.android.com/reference/android/view/Display.HdrCapabilities.html#HDR_TYPE_HDR10
+const jint HDR_TYPE_DOLBY_VISION = 1;
+const jint HDR_TYPE_HDR10 = 2;
+const jint HDR_TYPE_HLG = 3;
+const jint HDR_TYPE_HDR10_PLUS = 4;
+
+const char SECURE_DECODER_SUFFIX[] = ".secure";
+
+bool EndsWith(const std::string& str, const std::string& suffix) {
+ if (str.size() < suffix.size()) {
+ return false;
+ }
+ return strcmp(str.c_str() + (str.size() - suffix.size()), suffix.c_str()) ==
+ 0;
+}
+
+Range ConvertJavaRangeToRange(JniEnvExt* env, jobject j_range) {
+ jobject j_upper_comparable = env->CallObjectMethodOrAbort(
+ j_range, "getUpper", "()Ljava/lang/Comparable;");
+ jint j_upper_int =
+ env->CallIntMethodOrAbort(j_upper_comparable, "intValue", "()I");
+
+ jobject j_lower_comparable = env->CallObjectMethodOrAbort(
+ j_range, "getLower", "()Ljava/lang/Comparable;");
+ jint j_lower_int =
+ env->CallIntMethodOrAbort(j_lower_comparable, "intValue", "()I");
+ return Range(j_lower_int, j_upper_int);
+}
+
+void ConvertStringToLowerCase(std::string* str) {
+ for (int i = 0; i < str->length(); i++) {
+ (*str)[i] = std::tolower((*str)[i]);
+ }
+}
+
+bool GetIsWidevineSupported() {
+ return JniEnvExt::Get()->CallStaticBooleanMethodOrAbort(
+ "dev/cobalt/media/MediaDrmBridge",
+ "isWidevineCryptoSchemeSupported", "()Z") == JNI_TRUE;
+}
+
+bool GetIsCbcsSupported() {
+ return JniEnvExt::Get()->CallStaticBooleanMethodOrAbort(
+ "dev/cobalt/media/MediaDrmBridge", "isCbcsSchemeSupported",
+ "()Z") == JNI_TRUE;
+}
+
+std::set<SbMediaTransferId> GetSupportedHdrTypes() {
+ std::set<SbMediaTransferId> supported_transfer_ids;
+
+ JniEnvExt* env = JniEnvExt::Get();
+ jintArray j_supported_hdr_types = static_cast<jintArray>(
+ env->CallStarboardObjectMethodOrAbort("getSupportedHdrTypes", "()[I"));
+ jsize length = env->GetArrayLength(j_supported_hdr_types);
+ jint* numbers = env->GetIntArrayElements(j_supported_hdr_types, 0);
+ for (int i = 0; i < length; i++) {
+ switch (numbers[i]) {
+ case HDR_TYPE_DOLBY_VISION:
+ continue;
+ case HDR_TYPE_HDR10:
+ supported_transfer_ids.insert(kSbMediaTransferIdSmpteSt2084);
+ continue;
+ case HDR_TYPE_HLG:
+ supported_transfer_ids.insert(kSbMediaTransferIdAribStdB67);
+ continue;
+ case HDR_TYPE_HDR10_PLUS:
+ continue;
+ }
+ }
+ env->ReleaseIntArrayElements(j_supported_hdr_types, numbers, 0);
+
+ return supported_transfer_ids;
+}
+
+bool GetIsPassthroughSupported(SbMediaAudioCodec codec) {
+ SbMediaAudioCodingType coding_type;
+ switch (codec) {
+ case kSbMediaAudioCodecAc3:
+ coding_type = kSbMediaAudioCodingTypeAc3;
+ break;
+ case kSbMediaAudioCodecEac3:
+ coding_type = kSbMediaAudioCodingTypeDolbyDigitalPlus;
+ break;
+ default:
+ return false;
+ }
+ int encoding = GetAudioFormatSampleType(coding_type);
+ JniEnvExt* env = JniEnvExt::Get();
+ ScopedLocalJavaRef<jobject> j_audio_output_manager(
+ env->CallStarboardObjectMethodOrAbort(
+ "getAudioOutputManager", "()Ldev/cobalt/media/AudioOutputManager;"));
+ return env->CallBooleanMethodOrAbort(j_audio_output_manager.Get(),
+ "hasPassthroughSupportFor", "(I)Z",
+ encoding) == JNI_TRUE;
+}
+
+int GetMaxAudioOutputChannels() {
+ JniEnvExt* env = JniEnvExt::Get();
+ ScopedLocalJavaRef<jobject> j_audio_output_manager(
+ env->CallStarboardObjectMethodOrAbort(
+ "getAudioOutputManager", "()Ldev/cobalt/media/AudioOutputManager;"));
+ return static_cast<int>(env->CallIntMethodOrAbort(
+ j_audio_output_manager.Get(), "getMaxChannels", "()I"));
+}
+
+} // namespace
+
+CodecCapability::CodecCapability(JniEnvExt* env, jobject j_codec_info) {
+ SB_DCHECK(env);
+ SB_DCHECK(j_codec_info);
+
+ name_ = env->GetStringStandardUTFOrAbort(
+ env->GetStringFieldOrAbort(j_codec_info, "decoderName"));
+
+ is_secure_required_ =
+ env->CallBooleanMethodOrAbort(j_codec_info, "isSecureRequired", "()Z") ==
+ JNI_TRUE;
+ is_secure_supported_ =
+ env->CallBooleanMethodOrAbort(j_codec_info, "isSecureSupported", "()Z") ==
+ JNI_TRUE;
+ is_tunnel_mode_required_ =
+ env->CallBooleanMethodOrAbort(j_codec_info, "isTunnelModeRequired",
+ "()Z") == JNI_TRUE;
+ is_tunnel_mode_supported_ =
+ env->CallBooleanMethodOrAbort(j_codec_info, "isTunnelModeSupported",
+ "()Z") == JNI_TRUE;
+}
+
+AudioCodecCapability::AudioCodecCapability(JniEnvExt* env,
+ jobject j_codec_info,
+ jobject j_audio_capabilities)
+ : CodecCapability(env, j_codec_info) {
+ SB_DCHECK(env);
+ SB_DCHECK(j_codec_info);
+ SB_DCHECK(j_audio_capabilities);
+
+ jobject j_bitrate_range = env->CallObjectMethodOrAbort(
+ j_audio_capabilities, "getBitrateRange", "()Landroid/util/Range;");
+ supported_bitrates_ = ConvertJavaRangeToRange(env, j_bitrate_range);
+
+ // Overwrite the lower bound to 0.
+ supported_bitrates_.minimum = 0;
+}
+
+bool AudioCodecCapability::IsBitrateSupported(int bitrate) const {
+ return supported_bitrates_.Contains(bitrate);
+}
+
+VideoCodecCapability::VideoCodecCapability(JniEnvExt* env,
+ jobject j_codec_info,
+ jobject j_video_capabilities)
+ : CodecCapability(env, j_codec_info) {
+ SB_DCHECK(env);
+ SB_DCHECK(j_codec_info);
+ SB_DCHECK(j_video_capabilities);
+
+ is_software_decoder_ = env->CallBooleanMethodOrAbort(
+ j_codec_info, "isSoftware", "()Z") == JNI_TRUE;
+
+ is_hdr_capable_ = env->CallBooleanMethodOrAbort(j_codec_info, "isHdrCapable",
+ "()Z") == JNI_TRUE;
+
+ j_video_capabilities_ = env->ConvertLocalRefToGlobalRef(j_video_capabilities);
+
+ jobject j_width_range = env->CallObjectMethodOrAbort(
+ j_video_capabilities_, "getSupportedWidths", "()Landroid/util/Range;");
+ supported_widths_ = ConvertJavaRangeToRange(env, j_width_range);
+
+ jobject j_height_range = env->CallObjectMethodOrAbort(
+ j_video_capabilities_, "getSupportedHeights", "()Landroid/util/Range;");
+ supported_heights_ = ConvertJavaRangeToRange(env, j_height_range);
+
+ jobject j_bitrate_range = env->CallObjectMethodOrAbort(
+ j_video_capabilities_, "getBitrateRange", "()Landroid/util/Range;");
+ supported_bitrates_ = ConvertJavaRangeToRange(env, j_bitrate_range);
+
+ jobject j_frame_rate_range = env->CallObjectMethodOrAbort(
+ j_video_capabilities_, "getSupportedFrameRates",
+ "()Landroid/util/Range;");
+ supported_frame_rates_ = ConvertJavaRangeToRange(env, j_frame_rate_range);
+}
+
+VideoCodecCapability::~VideoCodecCapability() {
+ JniEnvExt* env = JniEnvExt::Get();
+ env->DeleteGlobalRef(j_video_capabilities_);
+}
+
+bool VideoCodecCapability::IsBitrateSupported(int bitrate) const {
+ return supported_bitrates_.Contains(bitrate);
+}
+
+bool VideoCodecCapability::AreResolutionAndRateSupported(
+ bool force_improved_support_check,
+ int frame_width,
+ int frame_height,
+ int fps) {
+ if (force_improved_support_check) {
+ if (frame_width != 0 && frame_height != 0 && fps != 0) {
+ return JniEnvExt::Get()->CallBooleanMethodOrAbort(
+ j_video_capabilities_, "areSizeAndRateSupported", "(IID)Z",
+ frame_width, frame_height,
+ static_cast<jdouble>(fps)) == JNI_TRUE;
+ } else if (frame_width != 0 && frame_height != 0) {
+ return JniEnvExt::Get()->CallBooleanMethodOrAbort(
+ j_video_capabilities_, "isSizeSupported", "(II)Z", frame_width,
+ frame_height) == JNI_TRUE;
+ }
+ }
+ if (frame_width != 0 && !supported_widths_.Contains(frame_width)) {
+ return false;
+ }
+ if (frame_height != 0 && !supported_heights_.Contains(frame_height)) {
+ return false;
+ }
+ if (fps != 0 && !supported_frame_rates_.Contains(fps)) {
+ return false;
+ }
+ return true;
+}
+
+// static
+SB_ONCE_INITIALIZE_FUNCTION(MediaCapabilitiesCache,
+ MediaCapabilitiesCache::GetInstance);
+
+bool MediaCapabilitiesCache::IsWidevineSupported() {
+ if (!is_enabled_) {
+ return GetIsWidevineSupported();
+ }
+ ScopedLock scoped_lock(mutex_);
+ LazyInitialize_Locked();
+ return is_widevine_supported_;
+}
+
+bool MediaCapabilitiesCache::IsCbcsSchemeSupported() {
+ if (!is_enabled_) {
+ return GetIsCbcsSupported();
+ }
+ ScopedLock scoped_lock(mutex_);
+ LazyInitialize_Locked();
+ return is_cbcs_supported_;
+}
+
+bool MediaCapabilitiesCache::IsHDRTransferCharacteristicsSupported(
+ SbMediaTransferId transfer_id) {
+ if (!is_enabled_) {
+ std::set<SbMediaTransferId> supported_transfer_ids = GetSupportedHdrTypes();
+ return supported_transfer_ids.find(transfer_id) !=
+ supported_transfer_ids.end();
+ }
+ ScopedLock scoped_lock(mutex_);
+ LazyInitialize_Locked();
+ return supported_transfer_ids_.find(transfer_id) !=
+ supported_transfer_ids_.end();
+}
+
+bool MediaCapabilitiesCache::IsPassthroughSupported(SbMediaAudioCodec codec) {
+ if (!is_enabled_) {
+ return GetIsPassthroughSupported(codec);
+ }
+ // IsPassthroughSupported() caches the results of previous quiries, and does
+ // not rely on LazyInitialize(), which is different from other functions.
+ ScopedLock scoped_lock(mutex_);
+ auto iter = passthrough_supportabilities_.find(codec);
+ if (iter != passthrough_supportabilities_.end()) {
+ return iter->second;
+ }
+ bool supported = GetIsPassthroughSupported(codec);
+ passthrough_supportabilities_[codec] = supported;
+ return supported;
+}
+
+int MediaCapabilitiesCache::GetMaxAudioOutputChannels() {
+ if (!is_enabled_) {
+ return ::starboard::android::shared::GetMaxAudioOutputChannels();
+ }
+
+ ScopedLock scoped_lock(mutex_);
+ LazyInitialize_Locked();
+ return max_audio_output_channels_;
+}
+
+bool MediaCapabilitiesCache::HasAudioDecoderFor(const std::string& mime_type,
+ int bitrate,
+ bool must_support_tunnel_mode) {
+ return !FindAudioDecoder(mime_type, bitrate, must_support_tunnel_mode)
+ .empty();
+}
+
+bool MediaCapabilitiesCache::HasVideoDecoderFor(
+ const std::string& mime_type,
+ bool must_support_secure,
+ bool must_support_hdr,
+ bool must_support_tunnel_mode,
+ bool force_improved_support_check,
+ int frame_width,
+ int frame_height,
+ int bitrate,
+ int fps) {
+ return !FindVideoDecoder(mime_type, must_support_secure, must_support_hdr,
+ false, must_support_tunnel_mode,
+ force_improved_support_check, frame_width,
+ frame_height, bitrate, fps)
+ .empty();
+}
+
+std::string MediaCapabilitiesCache::FindAudioDecoder(
+ const std::string& mime_type,
+ int bitrate,
+ bool must_support_tunnel_mode) {
+ if (!is_enabled_) {
+ JniEnvExt* env = JniEnvExt::Get();
+ ScopedLocalJavaRef<jstring> j_mime(
+ env->NewStringStandardUTFOrAbort(mime_type.c_str()));
+ jobject j_decoder_name = env->CallStaticObjectMethodOrAbort(
+ "dev/cobalt/media/MediaCodecUtil", "findAudioDecoder",
+ "(Ljava/lang/String;IZ)Ljava/lang/String;", j_mime.Get(), bitrate,
+ must_support_tunnel_mode);
+ return env->GetStringStandardUTFOrAbort(
+ static_cast<jstring>(j_decoder_name));
+ }
+
+ ScopedLock scoped_lock(mutex_);
+ LazyInitialize_Locked();
+
+ for (auto& audio_capability : audio_codec_capabilities_map_[mime_type]) {
+ // Reject if tunnel mode is required but codec doesn't support it.
+ if (must_support_tunnel_mode &&
+ !audio_capability->is_tunnel_mode_supported()) {
+ continue;
+ }
+ // Reject if bitrate is not supported.
+ if (!audio_capability->IsBitrateSupported(bitrate)) {
+ continue;
+ }
+ return audio_capability->name();
+ }
+
+ return "";
+}
+
+std::string MediaCapabilitiesCache::FindVideoDecoder(
+ const std::string& mime_type,
+ bool must_support_secure,
+ bool must_support_hdr,
+ bool require_software_codec,
+ bool must_support_tunnel_mode,
+ bool force_improved_support_check,
+ int frame_width,
+ int frame_height,
+ int bitrate,
+ int fps) {
+ if (!is_enabled_) {
+ JniEnvExt* env = JniEnvExt::Get();
+ ScopedLocalJavaRef<jstring> j_mime(
+ env->NewStringStandardUTFOrAbort(mime_type.c_str()));
+ jobject j_decoder_name = env->CallStaticObjectMethodOrAbort(
+ "dev/cobalt/media/MediaCodecUtil", "findVideoDecoder",
+ "(Ljava/lang/String;ZZZZZIIIII)Ljava/lang/String;", j_mime.Get(),
+ must_support_secure, must_support_hdr,
+ false, /* mustSupportSoftwareCodec */
+ must_support_tunnel_mode, force_improved_support_check,
+ -1, /* decoderCacheTtlMs */
+ frame_width, frame_height, bitrate, fps);
+ return env->GetStringStandardUTFOrAbort(
+ static_cast<jstring>(j_decoder_name));
+ }
+
+ ScopedLock scoped_lock(mutex_);
+ LazyInitialize_Locked();
+
+ for (auto& video_capability : video_codec_capabilities_map_[mime_type]) {
+ // Reject if secure decoder is required but codec doesn't support it.
+ if (must_support_secure && !video_capability->is_secure_supported()) {
+ continue;
+ }
+ // Reject if non secure decoder is required but codec doesn't support it.
+ if (!must_support_secure && video_capability->is_secure_required()) {
+ continue;
+ }
+ // Reject if tunnel mode is required but codec doesn't support it.
+ if (must_support_tunnel_mode &&
+ !video_capability->is_tunnel_mode_supported()) {
+ continue;
+ }
+ // Reject if non tunnel mode is required but codec doesn't support it.
+ if (!must_support_tunnel_mode &&
+ video_capability->is_tunnel_mode_required()) {
+ continue;
+ }
+ // Reject if software codec is required but codec is not.
+ if (require_software_codec && !video_capability->is_software_decoder()) {
+ continue;
+ }
+ // Reject if hdr is required but codec doesn't support it.
+ if (must_support_hdr && !video_capability->is_hdr_capable()) {
+ continue;
+ }
+
+ bool enable_improved_support_check =
+ force_improved_support_check ||
+ (frame_width > 3840 || frame_height > 2160);
+ // Reject if resolution or frame rate is not supported.
+ if (!video_capability->AreResolutionAndRateSupported(
+ enable_improved_support_check, frame_width, frame_height, fps)) {
+ continue;
+ }
+
+ // Reject if bitrate is not supported.
+ if (bitrate != 0 && !video_capability->IsBitrateSupported(bitrate)) {
+ continue;
+ }
+
+ // Append ".secure" for secure decoder if not represents.
+ if (must_support_secure &&
+ !EndsWith(video_capability->name(), SECURE_DECODER_SUFFIX)) {
+ return video_capability->name() + SECURE_DECODER_SUFFIX;
+ }
+ return video_capability->name();
+ }
+
+ return "";
+}
+
+void MediaCapabilitiesCache::ClearCache() {
+ ScopedLock scoped_lock(mutex_);
+ is_initialized_ = false;
+ is_widevine_supported_ = false;
+ is_cbcs_supported_ = false;
+ supported_transfer_ids_.clear();
+ passthrough_supportabilities_.clear();
+ audio_codec_capabilities_map_.clear();
+ video_codec_capabilities_map_.clear();
+ max_audio_output_channels_ = -1;
+}
+
+void MediaCapabilitiesCache::LazyInitialize_Locked() {
+ mutex_.DCheckAcquired();
+
+ if (is_initialized_) {
+ return;
+ }
+
+ is_widevine_supported_ = GetIsWidevineSupported();
+ is_cbcs_supported_ = GetIsCbcsSupported();
+ supported_transfer_ids_ = GetSupportedHdrTypes();
+ max_audio_output_channels_ =
+ ::starboard::android::shared::GetMaxAudioOutputChannels();
+
+ LoadCodecInfos_Locked();
+
+ is_initialized_ = true;
+}
+
+void MediaCapabilitiesCache::LoadCodecInfos_Locked() {
+ SB_DCHECK(audio_codec_capabilities_map_.empty());
+ SB_DCHECK(video_codec_capabilities_map_.empty());
+ mutex_.DCheckAcquired();
+
+ JniEnvExt* env = JniEnvExt::Get();
+ jobjectArray j_codec_infos =
+ static_cast<jobjectArray>(env->CallStaticObjectMethodOrAbort(
+ "dev/cobalt/media/MediaCodecUtil", "getAllCodecCapabilityInfos",
+ "()[Ldev/cobalt/media/MediaCodecUtil$CodecCapabilityInfo;"));
+ jsize length = env->GetArrayLength(j_codec_infos);
+ // Note: Codec infos are sorted by the framework such that the best
+ // decoders come first.
+ // This order is maintained in the cache.
+ for (int i = 0; i < length; i++) {
+ jobject j_codec_info = env->GetObjectArrayElementOrAbort(j_codec_infos, i);
+
+ std::string mime_type = env->GetStringStandardUTFOrAbort(
+ env->GetStringFieldOrAbort(j_codec_info, "mimeType"));
+
+ // Convert the mime type to lower case.
+ ConvertStringToLowerCase(&mime_type);
+
+ jobject j_audio_capabilities = env->GetObjectFieldOrAbort(
+ j_codec_info, "audioCapabilities",
+ "Landroid/media/MediaCodecInfo$AudioCapabilities;");
+ if (j_audio_capabilities) {
+ // Found an audio decoder.
+ std::unique_ptr<AudioCodecCapability> audio_codec_capabilities(
+ new AudioCodecCapability(env, j_codec_info, j_audio_capabilities));
+ audio_codec_capabilities_map_[mime_type].push_back(
+ std::move(audio_codec_capabilities));
+ continue;
+ }
+ jobject j_video_capabilities = env->GetObjectFieldOrAbort(
+ j_codec_info, "videoCapabilities",
+ "Landroid/media/MediaCodecInfo$VideoCapabilities;");
+ if (j_video_capabilities) {
+ // Found a video decoder.
+ std::unique_ptr<VideoCodecCapability> video_codec_capabilities(
+ new VideoCodecCapability(env, j_codec_info, j_video_capabilities));
+ video_codec_capabilities_map_[mime_type].push_back(
+ std::move(video_codec_capabilities));
+ }
+ }
+}
+
+} // namespace shared
+} // namespace android
+} // namespace starboard
diff --git a/starboard/android/shared/media_capabilities_cache.h b/starboard/android/shared/media_capabilities_cache.h
new file mode 100644
index 0000000..46da91a
--- /dev/null
+++ b/starboard/android/shared/media_capabilities_cache.h
@@ -0,0 +1,197 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_ANDROID_SHARED_MEDIA_CAPABILITIES_CACHE_H_
+#define STARBOARD_ANDROID_SHARED_MEDIA_CAPABILITIES_CACHE_H_
+
+#include <jni.h>
+#include <atomic>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "starboard/android/shared/jni_env_ext.h"
+#include "starboard/common/mutex.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace android {
+namespace shared {
+
+// TODO: encapsulate a common Range class.
+struct Range {
+ Range() : minimum(0), maximum(0) {}
+ Range(int min, int max) : minimum(min), maximum(max) {}
+ int minimum;
+ int maximum;
+
+ bool Contains(int val) const { return val >= minimum && val <= maximum; }
+};
+
+class CodecCapability {
+ public:
+ CodecCapability(JniEnvExt* env, jobject j_codec_info);
+ virtual ~CodecCapability() {}
+
+ const std::string& name() const { return name_; }
+ bool is_secure_required() const { return is_secure_required_; }
+ bool is_secure_supported() const { return is_secure_supported_; }
+ bool is_tunnel_mode_required() const { return is_tunnel_mode_required_; }
+ bool is_tunnel_mode_supported() const { return is_tunnel_mode_supported_; }
+
+ private:
+ CodecCapability(const CodecCapability&) = delete;
+ CodecCapability& operator=(const CodecCapability&) = delete;
+
+ std::string name_;
+ bool is_secure_required_;
+ bool is_secure_supported_;
+ bool is_tunnel_mode_required_;
+ bool is_tunnel_mode_supported_;
+};
+
+class AudioCodecCapability : public CodecCapability {
+ public:
+ AudioCodecCapability(JniEnvExt* env,
+ jobject j_codec_info,
+ jobject j_audio_capabilities);
+ ~AudioCodecCapability() override {}
+
+ bool IsBitrateSupported(int bitrate) const;
+
+ private:
+ AudioCodecCapability(const AudioCodecCapability&) = delete;
+ AudioCodecCapability& operator=(const AudioCodecCapability&) = delete;
+
+ Range supported_bitrates_;
+};
+
+class VideoCodecCapability : public CodecCapability {
+ public:
+ VideoCodecCapability(JniEnvExt* env,
+ jobject j_codec_info,
+ jobject j_video_capabilities);
+ ~VideoCodecCapability() override;
+
+ bool is_software_decoder() const { return is_software_decoder_; }
+ bool is_hdr_capable() const { return is_hdr_capable_; }
+
+ bool IsBitrateSupported(int bitrate) const;
+ // VideoCodecCapability caches java object MediaCodecInfo.VideoCapabilities.
+ // If improved support check is used,
+ // VideoCapabilities.areSizeAndRateSupported() or
+ // VideoCapabilities.isSizeSupported() will be used to check the
+ // supportability.
+ bool AreResolutionAndRateSupported(bool force_improved_support_check,
+ int frame_width,
+ int frame_height,
+ int fps);
+
+ private:
+ VideoCodecCapability(const VideoCodecCapability&) = delete;
+ VideoCodecCapability& operator=(const VideoCodecCapability&) = delete;
+
+ bool is_software_decoder_;
+ bool is_hdr_capable_;
+ jobject j_video_capabilities_;
+ Range supported_widths_;
+ Range supported_heights_;
+ Range supported_bitrates_;
+ Range supported_frame_rates_;
+};
+
+class MediaCapabilitiesCache {
+ public:
+ static MediaCapabilitiesCache* GetInstance();
+
+ bool IsWidevineSupported();
+ bool IsCbcsSchemeSupported();
+
+ bool IsHDRTransferCharacteristicsSupported(SbMediaTransferId transfer_id);
+
+ bool IsPassthroughSupported(SbMediaAudioCodec codec);
+
+ int GetMaxAudioOutputChannels();
+
+ bool HasAudioDecoderFor(const std::string& mime_type,
+ int bitrate,
+ bool must_support_tunnel_mode);
+
+ bool HasVideoDecoderFor(const std::string& mime_type,
+ bool must_support_secure,
+ bool must_support_hdr,
+ bool must_support_tunnel_mode,
+ bool force_improved_support_check,
+ int frame_width,
+ int frame_height,
+ int bitrate,
+ int fps);
+
+ std::string FindAudioDecoder(const std::string& mime_type,
+ int bitrate,
+ bool must_support_tunnel_mode);
+
+ std::string FindVideoDecoder(const std::string& mime_type,
+ bool must_support_secure,
+ bool must_support_hdr,
+ bool require_software_codec,
+ bool must_support_tunnel_mode,
+ bool force_improved_support_check,
+ int frame_width,
+ int frame_height,
+ int bitrate,
+ int fps);
+
+ bool IsEnabled() const { return is_enabled_; }
+ void SetCacheEnabled(bool enabled) { is_enabled_ = enabled; }
+ void ClearCache();
+
+ private:
+ MediaCapabilitiesCache() {}
+ ~MediaCapabilitiesCache() {}
+
+ MediaCapabilitiesCache(const MediaCapabilitiesCache&) = delete;
+ MediaCapabilitiesCache& operator=(const MediaCapabilitiesCache&) = delete;
+
+ void LazyInitialize_Locked();
+ void LoadCodecInfos_Locked();
+
+ Mutex mutex_;
+
+ std::set<SbMediaTransferId> supported_transfer_ids_;
+ std::map<SbMediaAudioCodec, bool> passthrough_supportabilities_;
+
+ typedef std::vector<std::unique_ptr<AudioCodecCapability>>
+ AudioCodecCapabilities;
+ typedef std::vector<std::unique_ptr<VideoCodecCapability>>
+ VideoCodecCapabilities;
+
+ std::map<std::string, AudioCodecCapabilities> audio_codec_capabilities_map_;
+ std::map<std::string, VideoCodecCapabilities> video_codec_capabilities_map_;
+
+ std::atomic_bool is_enabled_{false};
+ bool is_initialized_ = false;
+ bool is_widevine_supported_ = false;
+ bool is_cbcs_supported_ = false;
+ int max_audio_output_channels_ = -1;
+};
+
+} // namespace shared
+} // namespace android
+} // namespace starboard
+
+#endif // STARBOARD_ANDROID_SHARED_MEDIA_CAPABILITIES_CACHE_H_
diff --git a/starboard/android/shared/media_codec_bridge.cc b/starboard/android/shared/media_codec_bridge.cc
index d663f08..2e2cd1a 100644
--- a/starboard/android/shared/media_codec_bridge.cc
+++ b/starboard/android/shared/media_codec_bridge.cc
@@ -14,6 +14,7 @@
#include "starboard/android/shared/media_codec_bridge.h"
+#include "starboard/android/shared/media_capabilities_cache.h"
#include "starboard/common/string.h"
namespace starboard {
@@ -163,8 +164,20 @@
const char* mime =
SupportedAudioCodecToMimeType(audio_codec, &is_passthrough);
if (!mime) {
+ SB_LOG(ERROR) << "Unsupported codec " << audio_codec << ".";
return scoped_ptr<MediaCodecBridge>(NULL);
}
+
+ std::string decoder_name =
+ MediaCapabilitiesCache::GetInstance()->FindAudioDecoder(
+ mime, 0, /* bitrate */
+ false /* must_support_tunnel_mode */);
+
+ if (decoder_name.empty()) {
+ SB_LOG(ERROR) << "Failed to find decoder for " << audio_codec << ".";
+ return scoped_ptr<MediaCodecBridge>(NULL);
+ }
+
JniEnvExt* env = JniEnvExt::Get();
ScopedLocalJavaRef<jbyteArray> configuration_data;
if (audio_codec == kSbMediaAudioCodecOpus &&
@@ -174,18 +187,21 @@
audio_sample_info.audio_specific_config_size));
}
ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
+ ScopedLocalJavaRef<jstring> j_decoder_name(
+ env->NewStringStandardUTFOrAbort(decoder_name.c_str()));
scoped_ptr<MediaCodecBridge> native_media_codec_bridge(
new MediaCodecBridge(handler));
jobject j_media_codec_bridge = env->CallStaticObjectMethodOrAbort(
"dev/cobalt/media/MediaCodecBridge", "createAudioMediaCodecBridge",
- "(JLjava/lang/String;IILandroid/media/MediaCrypto;[B)Ldev/cobalt/media/"
- "MediaCodecBridge;",
+ "(JLjava/lang/String;Ljava/lang/String;IILandroid/media/MediaCrypto;"
+ "[B)Ldev/cobalt/media/MediaCodecBridge;",
reinterpret_cast<jlong>(native_media_codec_bridge.get()), j_mime.Get(),
- audio_sample_info.samples_per_second,
+ j_decoder_name.Get(), audio_sample_info.samples_per_second,
audio_sample_info.number_of_channels, j_media_crypto,
configuration_data.Get());
if (!j_media_codec_bridge) {
+ SB_LOG(ERROR) << "Failed to create codec bridge for " << audio_codec << ".";
return scoped_ptr<MediaCodecBridge>(NULL);
}
@@ -216,8 +232,48 @@
*error_message = FormatString("Unsupported mime for codec %d", video_codec);
return scoped_ptr<MediaCodecBridge>(NULL);
}
+
+ const bool must_support_secure = !!j_media_crypto;
+ const bool must_support_hdr = color_metadata;
+ const bool must_support_tunnel_mode = tunnel_mode_audio_session_id != -1;
+ // On first pass, try to find a decoder with HDR if the color info is
+ // non-null.
+ std::string decoder_name =
+ MediaCapabilitiesCache::GetInstance()->FindVideoDecoder(
+ mime, must_support_secure, /* must_support_secure */
+ must_support_hdr, /* must_support_hdr */
+ require_software_codec, /* is_software_codec */
+ must_support_tunnel_mode, /* must_support_tunnel_mode */
+ force_improved_support_check, /* force_improved_support_check */
+ 0, /* frame_width */
+ 0, /* frame_height */
+ 0, /* bitrate */
+ 0 /* fps */);
+ if (decoder_name.empty() && color_metadata) {
+ // On second pass, forget HDR.
+ decoder_name = MediaCapabilitiesCache::GetInstance()->FindVideoDecoder(
+ mime, must_support_secure, /* must_support_secure */
+ false, /* must_support_hdr */
+ require_software_codec, /* is_software_codec */
+ must_support_tunnel_mode, /* must_support_tunnel_mode */
+ force_improved_support_check, /* force_improved_support_check */
+ 0, /* frame_width */
+ 0, /* frame_height */
+ 0, /* bitrate */
+ 0 /* fps */);
+ }
+
+ if (decoder_name.empty()) {
+ *error_message =
+ FormatString("Failed to find decoder: %s, mustSupportSecure: %d.", mime,
+ !!j_media_crypto);
+ return scoped_ptr<MediaCodecBridge>(NULL);
+ }
+
JniEnvExt* env = JniEnvExt::Get();
ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
+ ScopedLocalJavaRef<jstring> j_decoder_name(
+ env->NewStringStandardUTFOrAbort(decoder_name.c_str()));
ScopedLocalJavaRef<jobject> j_color_info(nullptr);
if (color_metadata) {
@@ -257,16 +313,16 @@
new MediaCodecBridge(handler));
env->CallStaticVoidMethodOrAbort(
"dev/cobalt/media/MediaCodecBridge", "createVideoMediaCodecBridge",
- "(JLjava/lang/String;ZZZIIILandroid/view/Surface;"
+ "(JLjava/lang/String;Ljava/lang/String;IIILandroid/view/Surface;"
"Landroid/media/MediaCrypto;"
"Ldev/cobalt/media/MediaCodecBridge$ColorInfo;"
"I"
"Ldev/cobalt/media/MediaCodecBridge$CreateMediaCodecBridgeResult;)"
"V",
reinterpret_cast<jlong>(native_media_codec_bridge.get()), j_mime.Get(),
- !!j_media_crypto, require_software_codec, force_improved_support_check,
- width, height, fps, j_surface, j_media_crypto, j_color_info.Get(),
- tunnel_mode_audio_session_id, j_create_media_codec_bridge_result.Get());
+ j_decoder_name.Get(), width, height, fps, j_surface, j_media_crypto,
+ j_color_info.Get(), tunnel_mode_audio_session_id,
+ j_create_media_codec_bridge_result.Get());
jobject j_media_codec_bridge = env->CallObjectMethodOrAbort(
j_create_media_codec_bridge_result.Get(), "mediaCodecBridge",
diff --git a/starboard/android/shared/media_get_audio_configuration.cc b/starboard/android/shared/media_get_audio_configuration.cc
index ef59d08..bd70438 100644
--- a/starboard/android/shared/media_get_audio_configuration.cc
+++ b/starboard/android/shared/media_get_audio_configuration.cc
@@ -14,11 +14,9 @@
#include "starboard/media.h"
-#include "starboard/android/shared/jni_env_ext.h"
-#include "starboard/android/shared/jni_utils.h"
+#include "starboard/android/shared/media_capabilities_cache.h"
-using starboard::android::shared::JniEnvExt;
-using starboard::android::shared::ScopedLocalJavaRef;
+using starboard::android::shared::MediaCapabilitiesCache;
bool SbMediaGetAudioConfiguration(
int output_index,
@@ -32,12 +30,8 @@
out_configuration->latency = 0;
out_configuration->coding_type = kSbMediaAudioCodingTypePcm;
- JniEnvExt* env = JniEnvExt::Get();
- ScopedLocalJavaRef<jobject> j_audio_output_manager(
- env->CallStarboardObjectMethodOrAbort(
- "getAudioOutputManager", "()Ldev/cobalt/media/AudioOutputManager;"));
- int channels = static_cast<int>(env->CallIntMethodOrAbort(
- j_audio_output_manager.Get(), "getMaxChannels", "()I"));
+ int channels =
+ MediaCapabilitiesCache::GetInstance()->GetMaxAudioOutputChannels();
if (channels < 2) {
SB_DLOG(WARNING)
<< "The supported channels from output device is smaller than 2. "
diff --git a/starboard/android/shared/media_is_audio_supported.cc b/starboard/android/shared/media_is_audio_supported.cc
index d9efe42..46b73e5 100644
--- a/starboard/android/shared/media_is_audio_supported.cc
+++ b/starboard/android/shared/media_is_audio_supported.cc
@@ -15,20 +15,19 @@
#include "starboard/shared/starboard/media/media_support_internal.h"
#include "starboard/android/shared/jni_utils.h"
+#include "starboard/android/shared/media_capabilities_cache.h"
#include "starboard/android/shared/media_common.h"
#include "starboard/audio_sink.h"
#include "starboard/configuration.h"
#include "starboard/configuration_constants.h"
#include "starboard/media.h"
-#include "starboard/shared/starboard/media/mime_type.h"
-using starboard::android::shared::JniEnvExt;
-using starboard::android::shared::ScopedLocalJavaRef;
+using starboard::android::shared::MediaCapabilitiesCache;
using starboard::android::shared::SupportedAudioCodecToMimeType;
using starboard::shared::starboard::media::MimeType;
bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- const char* content_type,
+ const MimeType* mime_type,
int64_t bitrate) {
if (bitrate >= kSbMediaMaxAudioBitrateInBitsPerSecond) {
return false;
@@ -41,28 +40,41 @@
return false;
}
- MimeType mime_type(content_type);
- if (strlen(content_type) > 0) {
+ bool enable_tunnel_mode = false;
+ bool enable_audio_passthrough = true;
+ if (mime_type) {
+ if (!mime_type->is_valid()) {
+ return false;
+ }
// Allows for disabling the use of the AudioDeviceCallback API to detect
// when audio peripherals are connected. Enabled by default.
// (https://developer.android.com/reference/android/media/AudioDeviceCallback)
- mime_type.RegisterBoolParameter("enableaudiodevicecallback");
+ if (!mime_type->ValidateBoolParameter("enableaudiodevicecallback")) {
+ return false;
+ }
+
// Allows for enabling tunneled playback. Disabled by default.
// (https://source.android.com/devices/tv/multimedia-tunneling)
- mime_type.RegisterBoolParameter("tunnelmode");
+ if (!mime_type->ValidateBoolParameter("tunnelmode")) {
+ return false;
+ }
+ enable_tunnel_mode = mime_type->GetParamBoolValue("tunnelmode", false);
+
// Enables audio passthrough if the codec supports it.
- mime_type.RegisterBoolParameter("audiopassthrough");
+ if (!mime_type->ValidateBoolParameter("audiopassthrough")) {
+ return false;
+ }
+ enable_audio_passthrough =
+ mime_type->GetParamBoolValue("audiopassthrough", true);
+
// Allows for disabling the CONTENT_TYPE_MOVIE AudioAttribute for
// non-tunneled playbacks with PCM audio. Enabled by default.
// (https://developer.android.com/reference/android/media/AudioAttributes#CONTENT_TYPE_MOVIE)
- mime_type.RegisterBoolParameter("enablepcmcontenttypemovie");
-
- if (!mime_type.is_valid()) {
+ if (!mime_type->ValidateBoolParameter("enablepcmcontenttypemovie")) {
return false;
}
}
- bool enable_tunnel_mode = mime_type.GetParamBoolValue("tunnelmode", false);
if (enable_tunnel_mode && !SbAudioSinkIsAudioSampleTypeSupported(
kSbMediaAudioSampleTypeInt16Deprecated)) {
SB_LOG(WARNING)
@@ -77,13 +89,10 @@
return true;
}
- JniEnvExt* env = JniEnvExt::Get();
- ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
- auto media_codec_supported =
- env->CallStaticBooleanMethodOrAbort(
- "dev/cobalt/media/MediaCodecUtil", "hasAudioDecoderFor",
- "(Ljava/lang/String;IZ)Z", j_mime.Get(), static_cast<jint>(bitrate),
- enable_tunnel_mode) == JNI_TRUE;
+ bool media_codec_supported =
+ MediaCapabilitiesCache::GetInstance()->HasAudioDecoderFor(
+ mime, bitrate, enable_tunnel_mode);
+
if (!media_codec_supported) {
return false;
}
@@ -92,29 +101,12 @@
return true;
}
- if (!mime_type.GetParamBoolValue("audiopassthrough", true)) {
+ if (!enable_audio_passthrough) {
SB_LOG(INFO) << "Passthrough codec is rejected because passthrough is "
"disabled through mime param.";
return false;
}
- SbMediaAudioCodingType coding_type;
- switch (audio_codec) {
- case kSbMediaAudioCodecAc3:
- coding_type = kSbMediaAudioCodingTypeAc3;
- break;
- case kSbMediaAudioCodecEac3:
- coding_type = kSbMediaAudioCodingTypeDolbyDigitalPlus;
- break;
- default:
- return false;
- }
- int encoding =
- ::starboard::android::shared::GetAudioFormatSampleType(coding_type);
- ScopedLocalJavaRef<jobject> j_audio_output_manager(
- env->CallStarboardObjectMethodOrAbort(
- "getAudioOutputManager", "()Ldev/cobalt/media/AudioOutputManager;"));
- return env->CallBooleanMethodOrAbort(j_audio_output_manager.Get(),
- "hasPassthroughSupportFor", "(I)Z",
- encoding) == JNI_TRUE;
+ return MediaCapabilitiesCache::GetInstance()->IsPassthroughSupported(
+ audio_codec);
}
diff --git a/starboard/android/shared/media_is_supported.cc b/starboard/android/shared/media_is_supported.cc
index f6cfe75..fcd6806 100644
--- a/starboard/android/shared/media_is_supported.cc
+++ b/starboard/android/shared/media_is_supported.cc
@@ -16,7 +16,7 @@
#include "starboard/shared/starboard/media/media_support_internal.h"
-#include "starboard/android/shared/jni_env_ext.h"
+#include "starboard/android/shared/media_capabilities_cache.h"
#include "starboard/android/shared/media_common.h"
#include "starboard/media.h"
#include "starboard/shared/starboard/media/mime_type.h"
@@ -26,15 +26,15 @@
SbMediaAudioCodec audio_codec,
const char* key_system) {
using starboard::android::shared::IsWidevineL1;
- using starboard::android::shared::JniEnvExt;
+ using starboard::android::shared::MediaCapabilitiesCache;
using starboard::shared::starboard::media::MimeType;
// It is possible that the |key_system| comes with extra attributes, like
// `com.widevine.alpha; encryptionscheme="cenc"`. We prepend "key_system/"
// to it, so it can be parsed by MimeType.
MimeType mime_type(std::string("key_system/") + key_system);
- mime_type.RegisterStringParameter("encryptionscheme", "cenc|cbcs|cbcs-1-9");
- if (!mime_type.is_valid()) {
+ if (!mime_type.is_valid() || !mime_type.ValidateStringParameter(
+ "encryptionscheme", "cenc|cbcs|cbcs-1-9")) {
return false;
}
@@ -51,11 +51,16 @@
if (!IsWidevineL1(key_system_type)) {
return false;
}
+
+ if (!MediaCapabilitiesCache::GetInstance()->IsWidevineSupported()) {
+ return false;
+ }
+
std::string encryption_scheme =
mime_type.GetParamStringValue("encryptionscheme", "");
- bool uses_cbcs =
- encryption_scheme == "cbcs" || encryption_scheme == "cbcs-1-9";
- return JniEnvExt::Get()->CallStaticBooleanMethodOrAbort(
- "dev/cobalt/media/MediaDrmBridge",
- "isWidevineCryptoSchemeSupported", "(Z)Z", uses_cbcs) == JNI_TRUE;
+ if (encryption_scheme == "cbcs" || encryption_scheme == "cbcs-1-9") {
+ return MediaCapabilitiesCache::GetInstance()->IsCbcsSchemeSupported();
+ }
+
+ return true;
}
diff --git a/starboard/android/shared/media_is_video_supported.cc b/starboard/android/shared/media_is_video_supported.cc
index ff0acdf..1f5c851 100644
--- a/starboard/android/shared/media_is_video_supported.cc
+++ b/starboard/android/shared/media_is_video_supported.cc
@@ -14,47 +14,19 @@
#include "starboard/shared/starboard/media/media_support_internal.h"
-#include "starboard/android/shared/jni_env_ext.h"
-#include "starboard/android/shared/jni_utils.h"
+#include "starboard/android/shared/media_capabilities_cache.h"
#include "starboard/android/shared/media_common.h"
#include "starboard/configuration.h"
#include "starboard/media.h"
#include "starboard/shared/starboard/media/media_util.h"
-#include "starboard/shared/starboard/media/mime_type.h"
-using starboard::android::shared::JniEnvExt;
-using starboard::android::shared::ScopedLocalJavaRef;
+using starboard::android::shared::MediaCapabilitiesCache;
using starboard::android::shared::SupportedVideoCodecToMimeType;
using starboard::shared::starboard::media::IsSDRVideo;
using starboard::shared::starboard::media::MimeType;
-namespace {
-
-// https://developer.android.com/reference/android/view/Display.HdrCapabilities.html#HDR_TYPE_HDR10
-const jint HDR_TYPE_DOLBY_VISION = 1;
-const jint HDR_TYPE_HDR10 = 2;
-const jint HDR_TYPE_HLG = 3;
-
-bool IsHDRTransferCharacteristicsSupported(SbMediaTransferId transfer_id) {
- jint hdr_type;
- if (transfer_id == kSbMediaTransferIdSmpteSt2084) {
- hdr_type = HDR_TYPE_HDR10;
- } else if (transfer_id == kSbMediaTransferIdAribStdB67) {
- hdr_type = HDR_TYPE_HLG;
- } else {
- // No other transfer functions are supported, see
- // https://source.android.com/devices/tech/display/hdr.
- return false;
- }
-
- return JniEnvExt::Get()->CallStarboardBooleanMethodOrAbort(
- "isHdrTypeSupported", "(I)Z", hdr_type) == JNI_TRUE;
-}
-
-} // namespace
-
bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
- const char* content_type,
+ const MimeType* mime_type,
int profile,
int level,
int bit_depth,
@@ -68,36 +40,48 @@
bool decode_to_texture_required) {
const bool must_support_hdr =
!IsSDRVideo(bit_depth, primary_id, transfer_id, matrix_id);
- if (must_support_hdr && !IsHDRTransferCharacteristicsSupported(transfer_id)) {
+ if (must_support_hdr &&
+ !MediaCapabilitiesCache::GetInstance()
+ ->IsHDRTransferCharacteristicsSupported(transfer_id)) {
return false;
}
// While not necessarily true, for now we assume that all Android devices
// can play decode-to-texture video just as well as normal video.
- // Check extended parameters for correctness and return false if any invalid
- // invalid params are found.
- MimeType mime_type(content_type);
- if (strlen(content_type) > 0) {
- // Allows for enabling tunneled playback. Disabled by default.
- // https://source.android.com/devices/tv/multimedia-tunneling
- mime_type.RegisterBoolParameter("tunnelmode");
- // Override endianness on HDR Info header. Defaults to little.
- mime_type.RegisterStringParameter("hdrinfoendianness", "big|little");
- // Forces the use of specific Android APIs (isSizeSupported() and
- // areSizeAndRateSupported()) to determine format support.
- mime_type.RegisterBoolParameter("forceimprovedsupportcheck");
- // Override the default decoder cache TTL to the specified value.
- // The cache will be disabled if the value is non-positive.
- // TODO(b/227356434): RegisterIntParameter requires API review.
- // mime_type.RegisterIntParameter("decoder_cache_ttl_ms");
-
- if (!mime_type.is_valid()) {
+ bool must_support_tunnel_mode = false;
+ bool force_improved_support_check = true;
+ int decoder_cache_ttl_ms = -1;
+ if (mime_type) {
+ if (!mime_type->is_valid()) {
return false;
}
+
+ // Allows for enabling tunneled playback. Disabled by default.
+ // https://source.android.com/devices/tv/multimedia-tunneling
+ if (!mime_type->ValidateBoolParameter("tunnelmode")) {
+ return false;
+ }
+ must_support_tunnel_mode =
+ mime_type->GetParamBoolValue("tunnelmode", false);
+
+ // Override endianness on HDR Info header. Defaults to little.
+ if (!mime_type->ValidateStringParameter("hdrinfoendianness",
+ "big|little")) {
+ return false;
+ }
+
+ // Forces the use of specific Android APIs (isSizeSupported() and
+ // areSizeAndRateSupported()) to determine format support.
+ if (!mime_type->ValidateBoolParameter("forceimprovedsupportcheck")) {
+ return false;
+ }
+ force_improved_support_check =
+ mime_type->GetParamBoolValue("forceimprovedsupportcheck", true);
+
+ decoder_cache_ttl_ms =
+ mime_type->GetParamIntValue("decoder_cache_ttl_ms", -1);
}
- bool must_support_tunnel_mode =
- mime_type.GetParamBoolValue("tunnelmode", false);
if (must_support_tunnel_mode && decode_to_texture_required) {
SB_LOG(WARNING) << "Tunnel mode is rejected because output mode decode to "
"texture is required but not supported.";
@@ -108,24 +92,14 @@
if (!mime) {
return false;
}
- JniEnvExt* env = JniEnvExt::Get();
- ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
// We assume that if a device supports a format for clear playback, it will
// also support it for encrypted playback. However, some devices require
// tunneled playback to be encrypted, so we must align the tunnel mode
// requirement with the secure playback requirement.
const bool require_secure_playback = must_support_tunnel_mode;
- const bool force_improved_support_check =
- mime_type.GetParamBoolValue("forceimprovedsupportcheck", true);
- const int decoder_cache_ttl_ms =
- mime_type.GetParamIntValue("decoder_cache_ttl_ms", -1);
- return env->CallStaticBooleanMethodOrAbort(
- "dev/cobalt/media/MediaCodecUtil", "hasVideoDecoderFor",
- "(Ljava/lang/String;ZZZZIIIII)Z", j_mime.Get(),
- require_secure_playback, must_support_hdr,
- must_support_tunnel_mode, force_improved_support_check,
- decoder_cache_ttl_ms, frame_width, frame_height,
- static_cast<jint>(bitrate), fps) == JNI_TRUE;
+ return MediaCapabilitiesCache::GetInstance()->HasVideoDecoderFor(
+ mime, require_secure_playback, must_support_hdr, must_support_tunnel_mode,
+ force_improved_support_check, frame_width, frame_height, bitrate, fps);
}
diff --git a/starboard/android/shared/player_components_factory.h b/starboard/android/shared/player_components_factory.h
index 3b683fb..6d26909 100644
--- a/starboard/android/shared/player_components_factory.h
+++ b/starboard/android/shared/player_components_factory.h
@@ -24,6 +24,7 @@
#include "starboard/android/shared/drm_system.h"
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/android/shared/jni_utils.h"
+#include "starboard/android/shared/media_capabilities_cache.h"
#include "starboard/android/shared/media_common.h"
#include "starboard/android/shared/video_decoder.h"
#include "starboard/atomic.h"
@@ -206,25 +207,26 @@
error_message);
}
- MimeType audio_mime_type(creation_parameters.audio_mime());
+ bool enable_audio_device_callback = true;
if (strlen(creation_parameters.audio_mime()) > 0) {
- audio_mime_type.RegisterBoolParameter("enableaudiodevicecallback");
- audio_mime_type.RegisterBoolParameter("audiopassthrough");
- if (!audio_mime_type.is_valid()) {
+ MimeType audio_mime_type(creation_parameters.audio_mime());
+ if (!audio_mime_type.is_valid() ||
+ !audio_mime_type.ValidateBoolParameter("enableaudiodevicecallback") ||
+ !audio_mime_type.ValidateBoolParameter("audiopassthrough")) {
return scoped_ptr<PlayerComponents>();
}
- }
- bool enable_audio_device_callback =
- audio_mime_type.GetParamBoolValue("enableaudiodevicecallback", true);
- SB_LOG(INFO) << "AudioDeviceCallback is "
- << (enable_audio_device_callback ? "enabled." : "disabled.");
+ enable_audio_device_callback =
+ audio_mime_type.GetParamBoolValue("enableaudiodevicecallback", true);
+ SB_LOG(INFO) << "AudioDeviceCallback is "
+ << (enable_audio_device_callback ? "enabled." : "disabled.");
- if (!audio_mime_type.GetParamBoolValue("audiopassthrough", true)) {
- SB_LOG(INFO) << "Mime attribute \"audiopassthrough\" is set to: "
- "false. Passthrough is disabled.";
- return scoped_ptr<PlayerComponents>();
+ if (!audio_mime_type.GetParamBoolValue("audiopassthrough", true)) {
+ SB_LOG(INFO) << "Mime attribute \"audiopassthrough\" is set to: "
+ "false. Passthrough is disabled.";
+ return scoped_ptr<PlayerComponents>();
+ }
}
SB_LOG(INFO) << "Creating passthrough components.";
@@ -244,15 +246,20 @@
constexpr int kTunnelModeAudioSessionId = -1;
constexpr bool kForceSecurePipelineUnderTunnelMode = false;
- MimeType video_mime_type(creation_parameters.video_mime());
- video_mime_type.RegisterBoolParameter("forceimprovedsupportcheck");
- if (!video_mime_type.is_valid()) {
- return scoped_ptr<PlayerComponents>();
+ bool force_improved_support_check = true;
+
+ if (strlen(creation_parameters.video_mime()) > 0) {
+ MimeType video_mime_type(creation_parameters.video_mime());
+ if (!video_mime_type.is_valid() ||
+ !video_mime_type.ValidateBoolParameter(
+ "forceimprovedsupportcheck")) {
+ return scoped_ptr<PlayerComponents>();
+ }
+ force_improved_support_check = video_mime_type.GetParamBoolValue(
+ "forceimprovedsupportcheck", true);
+ SB_LOG_IF(INFO, !force_improved_support_check)
+ << "Improved support check is disabled for queries under 4K.";
}
- const bool force_improved_support_check =
- video_mime_type.GetParamBoolValue("forceimprovedsupportcheck", true);
- SB_LOG_IF(INFO, !force_improved_support_check)
- << "Improved support check is disabled for queries under 4K.";
scoped_ptr<VideoDecoder> video_decoder =
CreateVideoDecoder(creation_parameters, kTunnelModeAudioSessionId,
@@ -292,13 +299,11 @@
? creation_parameters.audio_mime()
: "";
MimeType audio_mime_type(audio_mime);
- if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone &&
- strlen(creation_parameters.audio_mime()) > 0) {
- audio_mime_type.RegisterBoolParameter("tunnelmode");
- audio_mime_type.RegisterBoolParameter("enableaudiodevicecallback");
- audio_mime_type.RegisterBoolParameter("enablepcmcontenttypemovie");
-
- if (!audio_mime_type.is_valid()) {
+ if (strlen(audio_mime) > 0) {
+ if (!audio_mime_type.is_valid() ||
+ !audio_mime_type.ValidateBoolParameter("tunnelmode") ||
+ !audio_mime_type.ValidateBoolParameter("enableaudiodevicecallback") ||
+ !audio_mime_type.ValidateBoolParameter("enablepcmcontenttypemovie")) {
*error_message =
"Invalid audio MIME: '" + std::string(audio_mime) + "'";
return false;
@@ -310,12 +315,10 @@
? creation_parameters.video_mime()
: "";
MimeType video_mime_type(video_mime);
- if (creation_parameters.video_codec() != kSbMediaVideoCodecNone &&
- strlen(creation_parameters.video_mime()) > 0) {
- video_mime_type.RegisterBoolParameter("tunnelmode");
- video_mime_type.RegisterBoolParameter("forceimprovedsupportcheck");
-
- if (!video_mime_type.is_valid()) {
+ if (strlen(video_mime) > 0) {
+ if (!video_mime_type.is_valid() ||
+ !video_mime_type.ValidateBoolParameter("tunnelmode") ||
+ !video_mime_type.ValidateBoolParameter("forceimprovedsupportcheck")) {
*error_message =
"Invalid video MIME: '" + std::string(video_mime) + "'";
return false;
@@ -496,18 +499,18 @@
bool force_secure_pipeline_under_tunnel_mode,
bool force_improved_support_check,
std::string* error_message) {
- // Use mime param to determine endianness of HDR metadata. If param is
- // missing or invalid it defaults to Little Endian.
- MimeType video_mime_type(creation_parameters.video_mime());
-
+ bool force_big_endian_hdr_metadata = false;
if (strlen(creation_parameters.video_mime()) > 0) {
- video_mime_type.RegisterStringParameter("hdrinfoendianness",
+ // Use mime param to determine endianness of HDR metadata. If param is
+ // missing or invalid it defaults to Little Endian.
+ MimeType video_mime_type(creation_parameters.video_mime());
+ video_mime_type.ValidateStringParameter("hdrinfoendianness",
"big|little");
+ const std::string& hdr_info_endianness =
+ video_mime_type.GetParamStringValue("hdrinfoendianness",
+ /*default=*/"little");
+ force_big_endian_hdr_metadata = hdr_info_endianness == "big";
}
- const std::string& hdr_info_endianness =
- video_mime_type.GetParamStringValue("hdrinfoendianness",
- /*default=*/"little");
- bool force_big_endian_hdr_metadata = hdr_info_endianness == "big";
scoped_ptr<VideoDecoder> video_decoder(new VideoDecoder(
creation_parameters.video_codec(),
@@ -563,29 +566,24 @@
<< creation_parameters.video_codec() << " is not supported.";
return false;
}
- JniEnvExt* env = JniEnvExt::Get();
- ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
DrmSystem* drm_system_ptr =
static_cast<DrmSystem*>(creation_parameters.drm_system());
jobject j_media_crypto =
drm_system_ptr ? drm_system_ptr->GetMediaCrypto() : NULL;
bool is_encrypted = !!j_media_crypto;
- if (env->CallStaticBooleanMethodOrAbort(
- "dev/cobalt/media/MediaCodecUtil", "hasVideoDecoderFor",
- "(Ljava/lang/String;ZZZZIIII)Z", j_mime.Get(), is_encrypted, false,
- true, force_improved_support_check, 0, 0, 0, 0) == JNI_TRUE) {
+ if (MediaCapabilitiesCache::GetInstance()->HasVideoDecoderFor(
+ mime, is_encrypted, false, true, force_improved_support_check, 0, 0,
+ 0, 0)) {
return true;
}
if (kForceSecurePipelineInTunnelModeWhenRequired && !is_encrypted) {
const bool kIsEncrypted = true;
auto support_tunnel_mode_under_secure_pipeline =
- env->CallStaticBooleanMethodOrAbort(
- "dev/cobalt/media/MediaCodecUtil", "hasVideoDecoderFor",
- "(Ljava/lang/String;ZZZZIIII)Z", j_mime.Get(), kIsEncrypted,
- false, true, force_improved_support_check, 0, 0, 0,
- 0) == JNI_TRUE;
+ MediaCapabilitiesCache::GetInstance()->HasVideoDecoderFor(
+ mime, kIsEncrypted, false, true, force_improved_support_check, 0,
+ 0, 0, 0);
if (support_tunnel_mode_under_secure_pipeline) {
*force_secure_pipeline_under_tunnel_mode = true;
return true;
diff --git a/starboard/elf_loader/BUILD.gn b/starboard/elf_loader/BUILD.gn
index 4a9f9a6..07974b0 100644
--- a/starboard/elf_loader/BUILD.gn
+++ b/starboard/elf_loader/BUILD.gn
@@ -108,9 +108,6 @@
"//starboard",
]
- # TODO: Remove this dependency once MediaSession is migrated to use CobaltExtensions.
- deps += cobalt_platform_dependencies
-
if (!sb_is_evergreen_compatible) {
deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
}
@@ -173,9 +170,6 @@
":elf_loader",
]
- # TODO: Remove this dependency once MediaSession is migrated to use CobaltExtensions.
- deps += cobalt_platform_dependencies
-
data_deps = [ ":copy_elf_loader_testdata" ]
}
}
diff --git a/starboard/evergreen/testing/performance_tests/baseline_update_test.sh b/starboard/evergreen/testing/performance_tests/baseline_update_test.sh
deleted file mode 100644
index de3a2f6..0000000
--- a/starboard/evergreen/testing/performance_tests/baseline_update_test.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Unset the previous test's name and runner function.
-unset TEST_NAME
-unset TEST_FILE
-unset -f run_test
-
-TEST_NAME="BaselineUpdatePerformance"
-
-function run_test() {
- source $(dirname "$0")/performance_tests/run_update_trial.sh
-
- for i in {1..3}; do
- run_update_trial "$i";
- result=$?
- if [[ "${result}" -ne 0 ]]; then
- return 1
- fi
- done
-
- return 0
-}
diff --git a/starboard/evergreen/testing/performance_tests/compression_update_test.sh b/starboard/evergreen/testing/performance_tests/compression_update_test.sh
deleted file mode 100644
index 4b1d4ef..0000000
--- a/starboard/evergreen/testing/performance_tests/compression_update_test.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Unset the previous test's name and runner function.
-unset TEST_NAME
-unset TEST_FILE
-unset -f run_test
-
-TEST_NAME="CompressionUpdatePerformance"
-
-function run_test() {
- source $(dirname "$0")/performance_tests/run_update_trial.sh
-
- for i in {1..3}; do
- run_update_trial "$i" "--compress_update" "--loader_use_compression";
- result=$?
- if [[ "${result}" -ne 0 ]]; then
- return 1
- fi
- done
-
- return 0
-}
diff --git a/starboard/evergreen/testing/performance_tests/run_update_trial.sh b/starboard/evergreen/testing/performance_tests/run_update_trial.sh
deleted file mode 100644
index 7188c4a..0000000
--- a/starboard/evergreen/testing/performance_tests/run_update_trial.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Runs an update cycle to generate performance logs.
-#
-# The size of the update is reported by this function directly, while memory
-# usage and shared library load time are logged by the loader app process.
-#
-# Args:
-# Trial number, extra arguments for the loader app run to install an update,
-# extra arguments for the loader app run to load the update.
-function run_update_trial() {
- clear_storage
-
- log "info" "Running trial ${1}"
- # Run the loader app once until an update has been installed.
- cycle_cobalt "https://www.youtube.com/tv" "${TEST_NAME}.0.log" "PingSender::SendPingComplete" "--loader_track_memory=100 ${2}"
-
- run_command "ls -l \"${STORAGE_DIR}/installation_1/lib\""
-
- # And run the loader app once more until it loads the installed update. The
- # memory tracker is configured with a shorter period to capture more data
- # during loading of the shared library.
- cycle_cobalt "https://www.youtube.com/tv" "${TEST_NAME}.1.log" "Loading took" "--loader_track_memory=10 ${3}"
-
- if grep -Eq "content/app/cobalt/lib/" "${LOG_PATH}/${TEST_NAME}.1.log"; then
- log "error" "The system image was loaded instead of the update, which must have failed"
- return 1
- fi
-
- return 0
-}
diff --git a/starboard/evergreen/testing/run_all_tests.sh b/starboard/evergreen/testing/run_all_tests.sh
index 2349c2e..efb9d0b 100755
--- a/starboard/evergreen/testing/run_all_tests.sh
+++ b/starboard/evergreen/testing/run_all_tests.sh
@@ -21,8 +21,7 @@
DIR="$(dirname "${0}")"
AUTH_METHOD="public-key"
-TEST_TYPE="functional"
-while getopts "d:a:t:" o; do
+while getopts "d:a:" o; do
case "${o}" in
d)
DEVICE_ID=${OPTARG}
@@ -30,9 +29,6 @@
a)
AUTH_METHOD=${OPTARG}
;;
- t)
- TEST_TYPE=${OPTARG}
- ;;
esac
done
shift $((OPTIND-1))
@@ -45,14 +41,8 @@
source $DIR/setup.sh
# Find all of the test files within the 'test' subdirectory.
-if [[ "${TEST_TYPE}" == "functional" ]]; then
- TESTS=($(eval "find ${DIR}/tests -maxdepth 1 -name '*_test.sh'"))
-elif [[ "${TEST_TYPE}" == "performance" ]]; then
- TESTS=($(eval "find ${DIR}/performance_tests -maxdepth 1 -name '*_test.sh'"))
-else
- echo "Only functional and performance tests are supported"
- exit 1
-fi
+TESTS=($(eval "find ${DIR}/tests -maxdepth 1 -name '*_test.sh'"))
+
COUNT=0
RETRIED=()
FAILED=()
diff --git a/starboard/linux/shared/media_is_audio_supported.cc b/starboard/linux/shared/media_is_audio_supported.cc
index 1780684..bedb0c7 100644
--- a/starboard/linux/shared/media_is_audio_supported.cc
+++ b/starboard/linux/shared/media_is_audio_supported.cc
@@ -19,14 +19,11 @@
#include "starboard/configuration_constants.h"
#include "starboard/media.h"
-bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- const char* content_type,
- int64_t bitrate) {
- if (!content_type) {
- SB_LOG(WARNING) << "|content_type| cannot be nullptr.";
- return false;
- }
+using ::starboard::shared::starboard::media::MimeType;
+bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+ const MimeType* mime_type,
+ int64_t bitrate) {
if (audio_codec == kSbMediaAudioCodecAac) {
return bitrate <= kSbMediaMaxAudioBitrateInBitsPerSecond;
}
@@ -42,5 +39,14 @@
}
}
+ if (audio_codec == kSbMediaAudioCodecVorbis) {
+ return bitrate <= kSbMediaMaxAudioBitrateInBitsPerSecond;
+ }
+#if SB_API_VERSION >= 14
+ if (audio_codec == kSbMediaAudioCodecMp3) {
+ return bitrate <= kSbMediaMaxAudioBitrateInBitsPerSecond;
+ }
+#endif // SB_API_VERSION >= 14
+
return false;
}
diff --git a/starboard/linux/shared/media_is_video_supported.cc b/starboard/linux/shared/media_is_video_supported.cc
index 98ccacd..e932cb1 100644
--- a/starboard/linux/shared/media_is_video_supported.cc
+++ b/starboard/linux/shared/media_is_video_supported.cc
@@ -22,11 +22,12 @@
#include "starboard/shared/libde265/de265_library_loader.h"
#include "starboard/shared/starboard/media/media_util.h"
-using starboard::shared::de265::is_de265_supported;
-using starboard::shared::starboard::media::IsSDRVideo;
+using ::starboard::shared::de265::is_de265_supported;
+using ::starboard::shared::starboard::media::IsSDRVideo;
+using ::starboard::shared::starboard::media::MimeType;
bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
- const char* content_type,
+ const MimeType* mime_type,
int profile,
int level,
int bit_depth,
@@ -38,11 +39,6 @@
int64_t bitrate,
int fps,
bool decode_to_texture_required) {
- if (!content_type) {
- SB_LOG(WARNING) << "|content_type| cannot be nullptr.";
- return false;
- }
-
if (!IsSDRVideo(bit_depth, primary_id, transfer_id, matrix_id)) {
if (bit_depth != 10 && bit_depth != 12) {
return false;
@@ -67,6 +63,7 @@
return (video_codec == kSbMediaVideoCodecAv1 ||
video_codec == kSbMediaVideoCodecH264 ||
(video_codec == kSbMediaVideoCodecH265 && is_de265_supported()) ||
+ (video_codec == kSbMediaVideoCodecVp8) ||
(video_codec == kSbMediaVideoCodecVp9)) &&
frame_width <= 1920 && frame_height <= 1080 &&
bitrate <= kSbMediaMaxVideoBitrateInBitsPerSecond && fps <= 60;
diff --git a/starboard/loader_app/BUILD.gn b/starboard/loader_app/BUILD.gn
index 951827f..3ef98fd 100644
--- a/starboard/loader_app/BUILD.gn
+++ b/starboard/loader_app/BUILD.gn
@@ -47,7 +47,6 @@
"//cobalt/content/fonts:copy_font_data",
"//starboard/elf_loader",
]
- deps += cobalt_platform_dependencies
}
}
@@ -68,7 +67,6 @@
"//cobalt/content/fonts:copy_font_data",
"//starboard/elf_loader:elf_loader_sys",
]
- deps += cobalt_platform_dependencies
}
}
}
diff --git a/starboard/nplb/media_can_play_mime_and_key_system_test.cc b/starboard/nplb/media_can_play_mime_and_key_system_test.cc
index beea8ac..af93b99 100644
--- a/starboard/nplb/media_can_play_mime_and_key_system_test.cc
+++ b/starboard/nplb/media_can_play_mime_and_key_system_test.cc
@@ -74,6 +74,10 @@
result = SbMediaCanPlayMimeAndKeySystem(
"audio/webm; codecs=\"opus\"; channels=2", "");
ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+ // Two codecs
+ result = SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", "");
+ ASSERT_EQ(result, kSbMediaSupportTypeProbably);
}
TEST(SbMediaCanPlayMimeAndKeySystem, Invalid) {
diff --git a/starboard/nplb/media_set_audio_write_duration_test.cc b/starboard/nplb/media_set_audio_write_duration_test.cc
index 237fe58..84ff9b2 100644
--- a/starboard/nplb/media_set_audio_write_duration_test.cc
+++ b/starboard/nplb/media_set_audio_write_duration_test.cc
@@ -31,7 +31,7 @@
namespace {
using ::starboard::testing::FakeGraphicsContextProvider;
-using shared::starboard::player::video_dmp::VideoDmpReader;
+using ::shared::starboard::player::video_dmp::VideoDmpReader;
using ::testing::ValuesIn;
const SbTime kDuration = kSbTimeSecond / 2;
@@ -261,8 +261,7 @@
const SbMediaAudioSampleInfo* audio_sample_info =
&dmp_reader.audio_sample_info();
- if (SbMediaIsAudioSupported(dmp_reader.audio_codec(),
- "", // content_type
+ if (SbMediaIsAudioSupported(dmp_reader.audio_codec(), nullptr,
dmp_reader.audio_bitrate())) {
test_params.push_back(filename);
}
diff --git a/starboard/raspi/shared/media_is_video_supported.cc b/starboard/raspi/shared/media_is_video_supported.cc
index 704faea..16a7088 100644
--- a/starboard/raspi/shared/media_is_video_supported.cc
+++ b/starboard/raspi/shared/media_is_video_supported.cc
@@ -19,8 +19,11 @@
#include "starboard/media.h"
#include "starboard/shared/starboard/media/media_util.h"
+using ::starboard::shared::starboard::media::IsSDRVideo;
+using ::starboard::shared::starboard::media::MimeType;
+
bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
- const char* content_type,
+ const MimeType* mime_type,
int profile,
int level,
int bit_depth,
@@ -32,8 +35,6 @@
int64_t bitrate,
int fps,
bool decode_to_texture_required) {
- using starboard::shared::starboard::media::IsSDRVideo;
-
if (!IsSDRVideo(bit_depth, primary_id, transfer_id, matrix_id)) {
return false;
}
diff --git a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
index 34d66fd..50cb9bd 100644
--- a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
@@ -46,6 +46,12 @@
return kSbHasAc3Audio ? AV_CODEC_ID_EAC3 : AV_CODEC_ID_NONE;
case kSbMediaAudioCodecOpus:
return AV_CODEC_ID_OPUS;
+ case kSbMediaAudioCodecVorbis:
+ return AV_CODEC_ID_VORBIS;
+#if SB_API_VERSION >= 14
+ case kSbMediaAudioCodecMp3:
+ return AV_CODEC_ID_MP3;
+#endif // SB_API_VERSION >= 14
default:
return AV_CODEC_ID_NONE;
}
@@ -272,7 +278,8 @@
codec_context_->extradata = NULL;
codec_context_->extradata_size = 0;
- if (codec_context_->codec_id == AV_CODEC_ID_OPUS &&
+ if ((codec_context_->codec_id == AV_CODEC_ID_OPUS ||
+ codec_context_->codec_id == AV_CODEC_ID_VORBIS) &&
audio_sample_info_.audio_specific_config_size > 0) {
// AV_INPUT_BUFFER_PADDING_SIZE is not defined in ancient avcodec.h. Use a
// large enough padding here explicitly.
diff --git a/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc b/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
index 1a6678e..d8605a6 100644
--- a/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
@@ -74,6 +74,22 @@
}
#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
+AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaVideoCodec video_codec) {
+ // Note: although SbMediaVideoCodec values exist for them, MPEG2VIDEO, THEORA,
+ // and VC1 are not included here since we do not officially support them.
+ // Also, note that VP9 and HEVC use different decoders (not FFmpeg).
+ switch (video_codec) {
+ case kSbMediaVideoCodecH264:
+ return AV_CODEC_ID_H264;
+ case kSbMediaVideoCodecVp8:
+ return AV_CODEC_ID_VP8;
+ default:
+ SB_DLOG(WARNING) << "FFmpeg decoder does not support SbMediaVideoCodec "
+ << video_codec;
+ }
+ return AV_CODEC_ID_NONE;
+}
+
const bool g_registered =
FFMPEGDispatch::RegisterSpecialization(FFMPEG,
LIBAVCODEC_VERSION_MAJOR,
@@ -320,7 +336,7 @@
}
codec_context_->codec_type = AVMEDIA_TYPE_VIDEO;
- codec_context_->codec_id = AV_CODEC_ID_H264;
+ codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(video_codec_);
codec_context_->profile = FF_PROFILE_UNKNOWN;
codec_context_->coded_width = 0;
codec_context_->coded_height = 0;
diff --git a/starboard/shared/starboard/media/BUILD.gn b/starboard/shared/starboard/media/BUILD.gn
index 1e7355b..28ba606 100644
--- a/starboard/shared/starboard/media/BUILD.gn
+++ b/starboard/shared/starboard/media/BUILD.gn
@@ -17,10 +17,20 @@
sources = [
"//starboard/shared/starboard/media/avc_util.cc",
"//starboard/shared/starboard/media/avc_util.h",
+ "//starboard/shared/starboard/media/bitrate_supportability_cache.cc",
+ "//starboard/shared/starboard/media/bitrate_supportability_cache.h",
"//starboard/shared/starboard/media/codec_util.cc",
"//starboard/shared/starboard/media/codec_util.h",
+ "//starboard/shared/starboard/media/key_system_supportability_cache.cc",
+ "//starboard/shared/starboard/media/key_system_supportability_cache.h",
"//starboard/shared/starboard/media/media_util.cc",
"//starboard/shared/starboard/media/media_util.h",
+ "//starboard/shared/starboard/media/mime_supportability_cache.cc",
+ "//starboard/shared/starboard/media/mime_supportability_cache.h",
+ "//starboard/shared/starboard/media/mime_util.cc",
+ "//starboard/shared/starboard/media/mime_util.h",
+ "//starboard/shared/starboard/media/parsed_mime_info.cc",
+ "//starboard/shared/starboard/media/parsed_mime_info.h",
"//starboard/shared/starboard/media/video_capabilities.cc",
"//starboard/shared/starboard/media/video_capabilities.h",
"//starboard/shared/starboard/media/vp9_util.cc",
diff --git a/starboard/shared/starboard/media/bitrate_supportability_cache.cc b/starboard/shared/starboard/media/bitrate_supportability_cache.cc
new file mode 100644
index 0000000..01c2917
--- /dev/null
+++ b/starboard/shared/starboard/media/bitrate_supportability_cache.cc
@@ -0,0 +1,150 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/bitrate_supportability_cache.h"
+
+#include <map>
+
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/log.h"
+#include "starboard/media.h"
+#include "starboard/once.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+template <typename T>
+class BitrateSupportabilityContainer {
+ public:
+ Supportability GetSupportability(T codec, int bitrate) {
+ // Reject invalid parameters.
+ if (bitrate < 0) {
+ return kSupportabilityNotSupported;
+ }
+ // Bitrate 0 is always supported.
+ if (bitrate == 0) {
+ return kSupportabilitySupported;
+ }
+
+ ScopedLock scoped_lock(mutex_);
+ auto iter = supported_bitrate_ranges_.find(codec);
+ if (iter == supported_bitrate_ranges_.end()) {
+ return kSupportabilityUnknown;
+ }
+ const Range& range = iter->second;
+ if (bitrate < range.minimum || bitrate > range.maximum) {
+ return kSupportabilityNotSupported;
+ }
+ return kSupportabilitySupported;
+ }
+
+ void SetSupportedBitrate(T codec, int min, int max) {
+ SB_DCHECK(min >= 0 && max >= min);
+
+ ScopedLock scoped_lock(mutex_);
+ supported_bitrate_ranges_[codec] = Range(min, max);
+ }
+ void ClearContainer() {
+ ScopedLock scoped_lock(mutex_);
+ supported_bitrate_ranges_.clear();
+ }
+
+ private:
+ struct Range {
+ Range() : minimum(0), maximum(0) {}
+ Range(int min, int max) : minimum(min), maximum(max) {}
+ int minimum;
+ int maximum;
+ };
+
+ Mutex mutex_;
+ std::map<T, Range> supported_bitrate_ranges_;
+};
+
+template <typename T>
+SB_ONCE_INITIALIZE_FUNCTION(BitrateSupportabilityContainer<T>, GetContainer);
+
+} // namespace
+
+// static
+SB_ONCE_INITIALIZE_FUNCTION(BitrateSupportabilityCache,
+ BitrateSupportabilityCache::GetInstance);
+
+Supportability BitrateSupportabilityCache::GetBitrateSupportability(
+ const ParsedMimeInfo& mime_info) {
+ SB_DCHECK(mime_info.is_valid());
+
+ if (!is_enabled_) {
+ return kSupportabilityUnknown;
+ }
+
+ Supportability audio_supportability = kSupportabilitySupported;
+ if (mime_info.has_audio_info()) {
+ audio_supportability = GetContainer<SbMediaAudioCodec>()->GetSupportability(
+ mime_info.audio_info().codec, mime_info.audio_info().bitrate);
+ }
+
+ Supportability video_supportability = kSupportabilitySupported;
+ if (mime_info.has_video_info()) {
+ video_supportability = GetContainer<SbMediaVideoCodec>()->GetSupportability(
+ mime_info.video_info().codec, mime_info.video_info().bitrate);
+ }
+
+ if (audio_supportability == kSupportabilityNotSupported ||
+ video_supportability == kSupportabilityNotSupported) {
+ return kSupportabilityNotSupported;
+ }
+ if (audio_supportability == kSupportabilityUnknown ||
+ video_supportability == kSupportabilityUnknown) {
+ return kSupportabilityUnknown;
+ }
+ return kSupportabilitySupported;
+}
+
+void BitrateSupportabilityCache::SetSupportedBitrate(SbMediaAudioCodec codec,
+ int min,
+ int max) {
+ SB_DCHECK(min >= 0 && min <= max) << "Invalid bitrate range.";
+
+ if (!is_enabled_) {
+ return;
+ }
+ GetContainer<SbMediaAudioCodec>()->SetSupportedBitrate(codec, min, max);
+}
+
+void BitrateSupportabilityCache::SetSupportedBitrate(SbMediaVideoCodec codec,
+ int min,
+ int max) {
+ SB_DCHECK(min >= 0 && min <= max) << "Invalid bitrate range.";
+
+ if (!is_enabled_) {
+ return;
+ }
+ GetContainer<SbMediaVideoCodec>()->SetSupportedBitrate(codec, min, max);
+}
+
+void BitrateSupportabilityCache::ClearCache() {
+ GetContainer<SbMediaAudioCodec>()->ClearContainer();
+ GetContainer<SbMediaVideoCodec>()->ClearContainer();
+}
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/starboard/shared/starboard/media/bitrate_supportability_cache.h b/starboard/shared/starboard/media/bitrate_supportability_cache.h
new file mode 100644
index 0000000..fc1f553
--- /dev/null
+++ b/starboard/shared/starboard/media/bitrate_supportability_cache.h
@@ -0,0 +1,66 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_BITRATE_SUPPORTABILITY_CACHE_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_BITRATE_SUPPORTABILITY_CACHE_H_
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/mime_supportability_cache.h"
+#include "starboard/shared/starboard/media/parsed_mime_info.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+// TODO: add unit tests for BitrateSupportabilityCache
+class BitrateSupportabilityCache {
+ public:
+ static BitrateSupportabilityCache* GetInstance();
+
+ // When cache is not enabled, GetBitrateSupportability() will always return
+ // kSupportabilityUnknown, and SetSupportedBitrate() will do nothing.
+ bool IsEnabled() const { return is_enabled_; }
+ void SetCacheEnabled(bool enabled) { is_enabled_ = enabled; }
+
+ // Get bitrate supportability.
+ Supportability GetBitrateSupportability(const ParsedMimeInfo& mime_info);
+ // Set supported bitrate range for the |codec|. Note that if supported bitrate
+ // range is not set, MimeSupportabilityCache::GetMimeSupportability() will
+ // always return kSupportabilityUnknown.
+ void SetSupportedBitrate(SbMediaAudioCodec codec, int min, int max);
+ void SetSupportedBitrate(SbMediaVideoCodec codec, int min, int max);
+
+ // Clear all cached supported bitrate ranges.
+ void ClearCache();
+
+ private:
+ // Class can only be instanced via the singleton
+ BitrateSupportabilityCache() {}
+ ~BitrateSupportabilityCache() {}
+
+ BitrateSupportabilityCache(const BitrateSupportabilityCache&) = delete;
+ BitrateSupportabilityCache& operator=(const BitrateSupportabilityCache&) =
+ delete;
+
+ std::atomic_bool is_enabled_{false};
+};
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_BITRATE_SUPPORTABILITY_CACHE_H_
diff --git a/starboard/shared/starboard/media/key_system_supportability_cache.cc b/starboard/shared/starboard/media/key_system_supportability_cache.cc
new file mode 100644
index 0000000..6ee6532
--- /dev/null
+++ b/starboard/shared/starboard/media/key_system_supportability_cache.cc
@@ -0,0 +1,169 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/key_system_supportability_cache.h"
+
+#include <cstring>
+#include <map>
+#include <string>
+
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/media.h"
+#include "starboard/once.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+template <typename T>
+class KeySystemSupportabilityContainer {
+ public:
+ Supportability GetKeySystemSupportability(T codec, const char* key_system) {
+ SB_DCHECK(key_system);
+ SB_DCHECK(strlen(key_system) > 0);
+
+ ScopedLock scoped_lock(mutex_);
+ auto map_iter = key_system_supportabilities_.find(codec);
+ if (map_iter == key_system_supportabilities_.end()) {
+ return kSupportabilityUnknown;
+ }
+ KeySystemToSupportabilityMap& map = map_iter->second;
+ auto supportability_iter = map.find(std::string(key_system));
+ if (supportability_iter == map.end()) {
+ return kSupportabilityUnknown;
+ }
+ return supportability_iter->second;
+ }
+
+ void CacheKeySystemSupportability(T codec,
+ const char* key_system,
+ Supportability supportability) {
+ SB_DCHECK(key_system);
+ SB_DCHECK(strlen(key_system) > 0);
+ SB_DCHECK(supportability != kSupportabilityUnknown);
+
+ ScopedLock scoped_lock(mutex_);
+ key_system_supportabilities_[codec][key_system] = supportability;
+ }
+
+ void ClearContainer() {
+ ScopedLock scoped_lock(mutex_);
+ key_system_supportabilities_.clear();
+ }
+
+ private:
+ typedef std::map<std::string, Supportability> KeySystemToSupportabilityMap;
+
+ Mutex mutex_;
+ std::map<T, KeySystemToSupportabilityMap> key_system_supportabilities_;
+};
+
+template <typename T>
+SB_ONCE_INITIALIZE_FUNCTION(KeySystemSupportabilityContainer<T>, GetContainer);
+
+} // namespace
+
+// static
+SB_ONCE_INITIALIZE_FUNCTION(KeySystemSupportabilityCache,
+ KeySystemSupportabilityCache::GetInstance);
+
+Supportability KeySystemSupportabilityCache::GetKeySystemSupportability(
+ SbMediaAudioCodec codec,
+ const char* key_system) {
+ SB_DCHECK(key_system);
+
+ // Empty key system is always supported.
+ if (strlen(key_system) == 0) {
+ return kSupportabilitySupported;
+ }
+
+ if (!is_enabled_) {
+ return kSupportabilityUnknown;
+ }
+
+ return GetContainer<SbMediaAudioCodec>()->GetKeySystemSupportability(
+ codec, key_system);
+}
+
+Supportability KeySystemSupportabilityCache::GetKeySystemSupportability(
+ SbMediaVideoCodec codec,
+ const char* key_system) {
+ SB_DCHECK(key_system);
+
+ // Empty key system is always supported.
+ if (strlen(key_system) == 0) {
+ return kSupportabilitySupported;
+ }
+
+ if (!is_enabled_) {
+ return kSupportabilityUnknown;
+ }
+
+ return GetContainer<SbMediaVideoCodec>()->GetKeySystemSupportability(
+ codec, key_system);
+}
+
+void KeySystemSupportabilityCache::CacheKeySystemSupportability(
+ SbMediaAudioCodec codec,
+ const char* key_system,
+ Supportability supportability) {
+ SB_DCHECK(key_system);
+ SB_DCHECK(supportability != kSupportabilityUnknown);
+
+ if (!is_enabled_) {
+ return;
+ }
+
+ if (strlen(key_system) == 0) {
+ SB_LOG(WARNING) << "Rejected empty key system as it's always supported.";
+ }
+
+ GetContainer<SbMediaAudioCodec>()->CacheKeySystemSupportability(
+ codec, key_system, supportability);
+}
+
+void KeySystemSupportabilityCache::CacheKeySystemSupportability(
+ SbMediaVideoCodec codec,
+ const char* key_system,
+ Supportability supportability) {
+ SB_DCHECK(key_system);
+ SB_DCHECK(strlen(key_system) > 0);
+ SB_DCHECK(supportability != kSupportabilityUnknown);
+
+ if (!is_enabled_) {
+ return;
+ }
+
+ if (strlen(key_system) == 0) {
+ SB_LOG(WARNING) << "Rejected empty key system as it's always supported.";
+ return;
+ }
+
+ GetContainer<SbMediaVideoCodec>()->CacheKeySystemSupportability(
+ codec, key_system, supportability);
+}
+
+void KeySystemSupportabilityCache::ClearCache() {
+ GetContainer<SbMediaAudioCodec>()->ClearContainer();
+ GetContainer<SbMediaVideoCodec>()->ClearContainer();
+}
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/starboard/shared/starboard/media/key_system_supportability_cache.h b/starboard/shared/starboard/media/key_system_supportability_cache.h
new file mode 100644
index 0000000..a1f2f68
--- /dev/null
+++ b/starboard/shared/starboard/media/key_system_supportability_cache.h
@@ -0,0 +1,70 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_KEY_SYSTEM_SUPPORTABILITY_CACHE_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_KEY_SYSTEM_SUPPORTABILITY_CACHE_H_
+
+#include <atomic>
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/mime_supportability_cache.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+// TODO: add unit tests for KeySystemSupportabilityCache
+class KeySystemSupportabilityCache {
+ public:
+ static KeySystemSupportabilityCache* GetInstance();
+
+ // When cache is not enabled, GetKeySystemSupportability() will always return
+ // kSupportabilityUnknown, and CacheKeySystemSupportability() will do nothing.
+ bool IsEnabled() const { return is_enabled_; }
+ void SetCacheEnabled(bool enabled) { is_enabled_ = enabled; }
+
+ // Get & cache key system supportability.
+ Supportability GetKeySystemSupportability(SbMediaAudioCodec codec,
+ const char* key_system);
+ Supportability GetKeySystemSupportability(SbMediaVideoCodec codec,
+ const char* key_system);
+ void CacheKeySystemSupportability(SbMediaAudioCodec codec,
+ const char* key_system,
+ Supportability supportability);
+ void CacheKeySystemSupportability(SbMediaVideoCodec codec,
+ const char* key_system,
+ Supportability supportability);
+
+ // Clear all cached supportabilities.
+ void ClearCache();
+
+ private:
+ // Class can only be instanced via the singleton
+ KeySystemSupportabilityCache() {}
+ ~KeySystemSupportabilityCache() {}
+
+ KeySystemSupportabilityCache(const KeySystemSupportabilityCache&) = delete;
+ KeySystemSupportabilityCache& operator=(const KeySystemSupportabilityCache&) =
+ delete;
+
+ std::atomic_bool is_enabled_{false};
+};
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_KEY_SYSTEM_SUPPORTABILITY_CACHE_H_
diff --git a/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc b/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
index b3c5723..8e5bae0 100644
--- a/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
+++ b/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
@@ -12,12 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/media.h"
-
#include "starboard/common/log.h"
-#include "starboard/common/string.h"
-#include "starboard/shared/starboard/media/media_util.h"
-#include "starboard/shared/starboard/media/mime_type.h"
+#include "starboard/media.h"
+#include "starboard/shared/starboard/media/mime_util.h"
SbMediaSupportType SbMediaCanPlayMimeAndKeySystem(const char* mime,
const char* key_system) {
@@ -31,11 +28,6 @@
return kSbMediaSupportTypeNotSupported;
}
- starboard::shared::starboard::media::MimeType mime_type(mime);
- if (!mime_type.is_valid()) {
- SB_DLOG(WARNING) << mime << " is not a valid mime type";
- return kSbMediaSupportTypeNotSupported;
- }
-
- return CanPlayMimeAndKeySystem(mime_type, key_system);
+ return starboard::shared::starboard::media::CanPlayMimeAndKeySystem(
+ mime, key_system);
}
diff --git a/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc b/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
index 6d2c701..e4689ca 100644
--- a/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
+++ b/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
@@ -18,8 +18,10 @@
#include "starboard/configuration_constants.h"
#include "starboard/media.h"
+using ::starboard::shared::starboard::media::MimeType;
+
bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- const char* content_type,
+ const MimeType* mime_type,
int64_t bitrate) {
if (audio_codec == kSbMediaAudioCodecAac) {
return bitrate <= kSbMediaMaxAudioBitrateInBitsPerSecond;
diff --git a/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc b/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
index fc72934..83dbd14 100644
--- a/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
+++ b/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
@@ -18,8 +18,10 @@
#include "starboard/configuration_constants.h"
#include "starboard/media.h"
+using ::starboard::shared::starboard::media::MimeType;
+
bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- const char* content_type,
+ const MimeType* mime_type,
int64_t bitrate) {
return audio_codec == kSbMediaAudioCodecAac &&
bitrate <= kSbMediaMaxAudioBitrateInBitsPerSecond;
diff --git a/starboard/shared/starboard/media/media_support_internal.h b/starboard/shared/starboard/media/media_support_internal.h
index bf5fe78..6fcc832 100644
--- a/starboard/shared/starboard/media/media_support_internal.h
+++ b/starboard/shared/starboard/media/media_support_internal.h
@@ -18,6 +18,7 @@
#include "starboard/configuration.h"
#include "starboard/media.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/mime_type.h"
#ifdef __cplusplus
extern "C" {
@@ -31,9 +32,9 @@
// the platform to decode any supported input formats.
//
// |video_codec|: The |SbMediaVideoCodec| being checked for platform
-// compatibility.
+// compatibility.
// |audio_codec|: The |SbMediaAudioCodec| being checked for platform
-// compatibility.
+// compatibility.
// |key_system|: The key system being checked for platform compatibility.
SB_EXPORT bool SbMediaIsSupported(SbMediaVideoCodec video_codec,
SbMediaAudioCodec audio_codec,
@@ -46,9 +47,8 @@
// function returns |false|.
//
// |video_codec|: The video codec used in the media content.
-// |content_type|: The full content type passed to the corresponding dom
-// interface if there is any. Otherwise it will be set to "".
-// It should never to set to NULL.
+// |mime_type|: The parsed mime type passed to the corresponding interface.
+// Note that |mime_type| can be NULL.
// |profile|: The profile in the context of |video_codec|. It should be set to
// -1 when it is unknown or not applicable.
// |level|: The level in the context of |video_codec|. It should be set to -1
@@ -75,32 +75,33 @@
// it indicates that the fps shouldn't be considered.
// |decode_to_texture_required|: Whether or not the resulting video frames can
// be decoded and used as textures by the GPU.
-bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
- const char* content_type,
- int profile,
- int level,
- int bit_depth,
- SbMediaPrimaryId primary_id,
- SbMediaTransferId transfer_id,
- SbMediaMatrixId matrix_id,
- int frame_width,
- int frame_height,
- int64_t bitrate,
- int fps,
- bool decode_to_texture_required);
+bool SbMediaIsVideoSupported(
+ SbMediaVideoCodec video_codec,
+ const starboard::shared::starboard::media::MimeType* mime_type,
+ int profile,
+ int level,
+ int bit_depth,
+ SbMediaPrimaryId primary_id,
+ SbMediaTransferId transfer_id,
+ SbMediaMatrixId matrix_id,
+ int frame_width,
+ int frame_height,
+ int64_t bitrate,
+ int fps,
+ bool decode_to_texture_required);
// Indicates whether this platform supports |audio_codec| at |bitrate|.
// If |audio_codec| is not supported under any condition, this function
// returns |false|.
//
// |audio_codec|: The media's audio codec (|SbMediaAudioCodec|).
-// |content_type|: The full content type passed to the corresponding dom
-// interface if there is any. Otherwise it will be set to "".
-// It should never to set to NULL.
+// |mime_type|: The parsed mime type passed to the corresponding interface.
+// Note that |mime_type| can be NULL.
// |bitrate|: The media's bitrate.
-bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- const char* content_type,
- int64_t bitrate);
+bool SbMediaIsAudioSupported(
+ SbMediaAudioCodec audio_codec,
+ const starboard::shared::starboard::media::MimeType* mime_type,
+ int64_t bitrate);
#ifdef __cplusplus
} // extern "C"
diff --git a/starboard/shared/starboard/media/media_util.cc b/starboard/shared/starboard/media/media_util.cc
index a5a135c..58bb7bf 100644
--- a/starboard/shared/starboard/media/media_util.cc
+++ b/starboard/shared/starboard/media/media_util.cc
@@ -21,9 +21,7 @@
#include "starboard/common/media.h"
#include "starboard/common/string.h"
#include "starboard/log.h"
-#include "starboard/memory.h"
#include "starboard/shared/starboard/media/codec_util.h"
-#include "starboard/shared/starboard/media/media_support_internal.h"
#include "starboard/shared/starboard/media/mime_type.h"
namespace starboard {
@@ -36,165 +34,6 @@
const int64_t kDefaultBitRate = 0;
const int64_t kDefaultAudioChannels = 2;
-bool IsSupportedAudioCodec(const MimeType& mime_type,
- const std::string& codec,
- const char* key_system) {
- SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codec.c_str());
- if (audio_codec == kSbMediaAudioCodecNone) {
- return false;
- }
-
- // TODO: allow platform-specific rejection of a combination of codec &
- // number of channels, by passing channels to SbMediaAudioIsSupported and /
- // or SbMediaIsSupported.
-
- if (strlen(key_system) != 0) {
- if (!SbMediaIsSupported(kSbMediaVideoCodecNone, audio_codec, key_system)) {
- return false;
- }
- }
-
- int channels = mime_type.GetParamIntValue("channels", kDefaultAudioChannels);
- if (!IsAudioOutputSupported(kSbMediaAudioCodingTypePcm, channels)) {
- return false;
- }
-
- int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate);
-
- if (!SbMediaIsAudioSupported(audio_codec,
- mime_type.raw_content_type().c_str(), bitrate)) {
- return false;
- }
-
- switch (audio_codec) {
- case kSbMediaAudioCodecNone:
- SB_NOTREACHED();
- return false;
- case kSbMediaAudioCodecAac:
- return mime_type.subtype() == "mp4";
- case kSbMediaAudioCodecAc3:
- if (!kSbHasAc3Audio) {
- SB_NOTREACHED() << "AC3 audio is not enabled on this platform. To "
- << "enable it, set kSbHasAc3Audio to |true|.";
- return false;
- }
- return mime_type.subtype() == "mp4";
- case kSbMediaAudioCodecEac3:
- if (!kSbHasAc3Audio) {
- SB_NOTREACHED() << "AC3 audio is not enabled on this platform. To "
- << "enable it, set kSbHasAc3Audio to |true|.";
- return false;
- }
- return mime_type.subtype() == "mp4";
- case kSbMediaAudioCodecOpus:
- case kSbMediaAudioCodecVorbis:
- return mime_type.subtype() == "webm";
-#if SB_API_VERSION >= 14
- case kSbMediaAudioCodecMp3:
- return mime_type.subtype() == "mpeg" || mime_type.subtype() == "mp3" ||
- mime_type.subtype() == "mp4";
- case kSbMediaAudioCodecPcm:
- return mime_type.subtype() == "wav" || mime_type.subtype() == "wave" ||
- mime_type.subtype() == "x-wav" ||
- mime_type.subtype() == "x-pn-wav";
- case kSbMediaAudioCodecFlac:
- return mime_type.subtype() == "ogg";
-#endif // SB_API_VERSION >= 14
- }
-
- SB_NOTREACHED();
- return false;
-}
-
-bool IsSupportedVideoCodec(const MimeType& mime_type,
- const std::string& codec,
- const char* key_system,
- bool decode_to_texture_required) {
- SbMediaVideoCodec video_codec;
- int profile = -1;
- int level = -1;
- int bit_depth = 8;
- SbMediaPrimaryId primary_id = kSbMediaPrimaryIdUnspecified;
- SbMediaTransferId transfer_id = kSbMediaTransferIdUnspecified;
- SbMediaMatrixId matrix_id = kSbMediaMatrixIdUnspecified;
-
- if (!ParseVideoCodec(codec.c_str(), &video_codec, &profile, &level,
- &bit_depth, &primary_id, &transfer_id, &matrix_id)) {
- return false;
- }
- SB_DCHECK(video_codec != kSbMediaVideoCodecNone);
-
- if (strlen(key_system) != 0) {
- if (!SbMediaIsSupported(video_codec, kSbMediaAudioCodecNone, key_system)) {
- return false;
- }
- }
-
- std::string eotf = mime_type.GetParamStringValue("eotf", "");
- if (!eotf.empty()) {
- SbMediaTransferId transfer_id_from_eotf = GetTransferIdFromString(eotf);
- // If the eotf is not known, reject immediately - without checking with
- // the platform.
- if (transfer_id_from_eotf == kSbMediaTransferIdUnknown) {
- return false;
- }
- if (transfer_id != kSbMediaTransferIdUnspecified &&
- transfer_id != transfer_id_from_eotf) {
- SB_LOG_IF(WARNING, transfer_id != kSbMediaTransferIdUnspecified)
- << "transfer_id " << transfer_id << " set by the codec string \""
- << codec << "\" will be overwritten by the eotf attribute " << eotf;
- }
- transfer_id = transfer_id_from_eotf;
- }
-
- std::string cryptoblockformat =
- mime_type.GetParamStringValue("cryptoblockformat", "");
- if (!cryptoblockformat.empty()) {
- if (mime_type.subtype() != "webm" || cryptoblockformat != "subsample") {
- return false;
- }
- }
-
- int width = mime_type.GetParamIntValue("width", 0);
- int height = mime_type.GetParamIntValue("height", 0);
- int fps = mime_type.GetParamIntValue("framerate", 0);
-
- int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate);
-
- if (width < 0 || height < 0 || fps < 0 || bitrate < 0) {
- return false;
- }
-
- if (!SbMediaIsVideoSupported(
- video_codec, mime_type.raw_content_type().c_str(), profile, level,
- bit_depth, primary_id, transfer_id, matrix_id, width, height, bitrate,
- fps, decode_to_texture_required)) {
- return false;
- }
-
- switch (video_codec) {
- case kSbMediaVideoCodecNone:
- SB_NOTREACHED();
- return false;
- case kSbMediaVideoCodecH264:
- case kSbMediaVideoCodecH265:
- return mime_type.subtype() == "mp4";
- case kSbMediaVideoCodecMpeg2:
- case kSbMediaVideoCodecTheora:
- return false; // No associated container in YT.
- case kSbMediaVideoCodecVc1:
- case kSbMediaVideoCodecAv1:
- return mime_type.subtype() == "mp4";
- case kSbMediaVideoCodecVp8:
- return mime_type.subtype() == "webm";
- case kSbMediaVideoCodecVp9:
- return mime_type.subtype() == "mp4" || mime_type.subtype() == "webm";
- }
-
- SB_NOTREACHED();
- return false;
-}
-
} // namespace
AudioSampleInfo::AudioSampleInfo() {
@@ -254,24 +93,6 @@
return *this;
}
-bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels) {
- int count = SbMediaGetAudioOutputCount();
-
- for (int output_index = 0; output_index < count; ++output_index) {
- SbMediaAudioConfiguration configuration;
- if (!SbMediaGetAudioConfiguration(output_index, &configuration)) {
- continue;
- }
-
- if (configuration.coding_type == coding_type &&
- configuration.number_of_channels >= channels) {
- return true;
- }
- }
-
- return false;
-}
-
bool IsSDRVideo(int bit_depth,
SbMediaPrimaryId primary_id,
SbMediaTransferId transfer_id,
@@ -346,17 +167,6 @@
return bit_depth == 8;
}
-SbMediaTransferId GetTransferIdFromString(const std::string& transfer_id) {
- if (transfer_id == "bt709") {
- return kSbMediaTransferIdBt709;
- } else if (transfer_id == "smpte2084") {
- return kSbMediaTransferIdSmpteSt2084;
- } else if (transfer_id == "arib-std-b67") {
- return kSbMediaTransferIdAribStdB67;
- }
- return kSbMediaTransferIdUnknown;
-}
-
int GetBytesPerSample(SbMediaAudioSampleType sample_type) {
switch (sample_type) {
case kSbMediaAudioSampleTypeInt16Deprecated:
@@ -369,84 +179,6 @@
return 4;
}
-SbMediaSupportType CanPlayMimeAndKeySystem(const MimeType& mime_type,
- const char* key_system) {
- SB_DCHECK(mime_type.is_valid());
-
- if (mime_type.type() != "audio" && mime_type.type() != "video") {
- return kSbMediaSupportTypeNotSupported;
- }
-
- auto codecs = mime_type.GetCodecs();
-
- // Pre-filter for |key_system|.
- if (strlen(key_system) != 0) {
- if (!SbMediaIsSupported(kSbMediaVideoCodecNone, kSbMediaAudioCodecNone,
- key_system)) {
- return kSbMediaSupportTypeNotSupported;
- }
- }
-
- bool decode_to_texture_required = false;
- std::string decode_to_texture_value =
- mime_type.GetParamStringValue("decode-to-texture", "false");
- if (decode_to_texture_value == "true") {
- decode_to_texture_required = true;
- } else if (decode_to_texture_value != "false") {
- // If an invalid value (e.g. not "true" or "false") is passed in for
- // decode-to-texture, trivially reject.
- return kSbMediaSupportTypeNotSupported;
- }
-
- if (codecs.size() == 0) {
- // This happens when the H5 player is either querying for progressive
- // playback support, or probing for generic mp4 support without specific
- // codecs. We only support "audio/mp4" and "video/mp4" for these cases.
- if ((mime_type.type() == "audio" || mime_type.type() == "video") &&
- mime_type.subtype() == "mp4") {
- return kSbMediaSupportTypeMaybe;
- }
- return kSbMediaSupportTypeNotSupported;
- }
-
- if (codecs.size() > 2) {
- return kSbMediaSupportTypeNotSupported;
- }
-
- bool has_audio_codec = false;
- bool has_video_codec = false;
- for (const auto& codec : codecs) {
- if (IsSupportedAudioCodec(mime_type, codec, key_system)) {
- if (has_audio_codec) {
- // We don't support two audio codecs in one stream.
- return kSbMediaSupportTypeNotSupported;
- }
- has_audio_codec = true;
- continue;
- }
- if (IsSupportedVideoCodec(mime_type, codec, key_system,
- decode_to_texture_required)) {
- if (mime_type.type() != "video") {
- // Video can only be contained in "video/*", while audio can be
- // contained in both "audio/*" and "video/*".
- return kSbMediaSupportTypeNotSupported;
- }
- if (has_video_codec) {
- // We don't support two video codecs in one stream.
- return kSbMediaSupportTypeNotSupported;
- }
- has_video_codec = true;
- continue;
- }
- return kSbMediaSupportTypeNotSupported;
- }
-
- if (has_audio_codec || has_video_codec) {
- return kSbMediaSupportTypeProbably;
- }
- return kSbMediaSupportTypeNotSupported;
-}
-
std::string GetStringRepresentation(const uint8_t* data, const int size) {
std::string result;
diff --git a/starboard/shared/starboard/media/media_util.h b/starboard/shared/starboard/media/media_util.h
index aef3cb8..8a97986 100644
--- a/starboard/shared/starboard/media/media_util.h
+++ b/starboard/shared/starboard/media/media_util.h
@@ -21,7 +21,6 @@
#include "starboard/media.h"
#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/media/mime_type.h"
namespace starboard {
namespace shared {
@@ -48,49 +47,14 @@
std::string max_video_capabilities_storage;
};
-bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels);
-
bool IsSDRVideo(int bit_depth,
SbMediaPrimaryId primary_id,
SbMediaTransferId transfer_id,
SbMediaMatrixId matrix_id);
bool IsSDRVideo(const char* mime);
-// Turns |eotf| into value of SbMediaTransferId. If |eotf| isn't recognized the
-// function returns kSbMediaTransferIdReserved0.
-// This function supports all eotfs required by YouTube TV HTML5 Technical
-// Requirements (2018).
-SbMediaTransferId GetTransferIdFromString(const std::string& eotf);
-
int GetBytesPerSample(SbMediaAudioSampleType sample_type);
-// Calls to canPlayType() and isTypeSupported() are redirected to this function.
-// Following are some example inputs:
-// canPlayType(video/mp4)
-// canPlayType(video/mp4; codecs="avc1.42001E, mp4a.40.2")
-// canPlayType(video/webm)
-// isTypeSupported(video/webm; codecs="vp9")
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; width=640)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; width=99999)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; height=360)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; height=99999)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; framerate=30)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; framerate=9999)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; bitrate=300000)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; bitrate=2000000000)
-// isTypeSupported(audio/mp4; codecs="mp4a.40.2")
-// isTypeSupported(audio/webm; codecs="vorbis")
-// isTypeSupported(video/webm; codecs="vp9")
-// isTypeSupported(video/webm; codecs="vp9")
-// isTypeSupported(audio/webm; codecs="opus")
-// isTypeSupported(audio/mp4; codecs="mp4a.40.2"; channels=2)
-// isTypeSupported(audio/mp4; codecs="mp4a.40.2"; channels=99)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; decode-to-texture=true)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; decode-to-texture=false)
-// isTypeSupported(video/mp4; codecs="avc1.4d401e"; decode-to-texture=invalid)
-SbMediaSupportType CanPlayMimeAndKeySystem(const MimeType& mime_type,
- const char* key_system);
-
std::string GetStringRepresentation(const uint8_t* data, const int size);
std::string GetMixedRepresentation(const uint8_t* data,
const int size,
diff --git a/starboard/shared/starboard/media/mime_supportability_cache.cc b/starboard/shared/starboard/media/mime_supportability_cache.cc
new file mode 100644
index 0000000..34024f3
--- /dev/null
+++ b/starboard/shared/starboard/media/mime_supportability_cache.cc
@@ -0,0 +1,227 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/mime_supportability_cache.h"
+
+#include <queue>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+
+#include "starboard/common/log.h"
+#include "starboard/common/media.h"
+#include "starboard/common/mutex.h"
+#include "starboard/log.h"
+#include "starboard/media.h"
+#include "starboard/once.h"
+#include "starboard/shared/starboard/media/mime_type.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+const size_t kDefaultCacheMaxSize = 2000;
+
+class MimeSupportabilityContainer {
+ public:
+ struct Entry {
+ ParsedMimeInfo mime_info;
+ Supportability supportability = kSupportabilityUnknown;
+
+ explicit Entry(const std::string& mime_string) : mime_info(mime_string) {}
+ };
+
+ // GetParsedMimeAndSupportability() will first try to find a cached Entry for
+ // the mime string. If no cached entry, a new Entry will be created with
+ // parsed mime information and supportability kSupportabilityUnknown.
+ // Ideally, we should decouple mime parsing and cache functionality, but
+ // considering that the cache is only for internal use, to avoid repeated
+ // lookups, we do parsing in this function.
+ const Entry& GetParsedMimeAndSupportability(const std::string& mime_string) {
+ ScopedLock scoped_lock(mutex_);
+ auto entry_iter = entries_.find(mime_string);
+ if (entry_iter != entries_.end()) {
+ return entry_iter->second;
+ }
+
+ // We can't find anything from the cache. Parse mime string and cache
+ // parsed MimeType and ParsedMimeInfo.
+ auto insert_result = entries_.insert({mime_string, Entry(mime_string)});
+
+ fifo_queue_.push(insert_result.first);
+ while (fifo_queue_.size() > max_size_) {
+ entries_.erase(fifo_queue_.front());
+ fifo_queue_.pop();
+ }
+ SB_DCHECK(entries_.size() == fifo_queue_.size());
+
+ return insert_result.first->second;
+ }
+
+ // CacheSupportability() will find the target entry and update the
+ // supportability. If there's no existing entry, it will parse the mime
+ // string and create one.
+ void CacheSupportability(const std::string& mime_string,
+ Supportability supportability) {
+ SB_DCHECK(!mime_string.empty());
+ SB_DCHECK(supportability != kSupportabilityUnknown);
+
+ {
+ ScopedLock scoped_lock(mutex_);
+ auto entry_iter = entries_.find(mime_string);
+ if (entry_iter != entries_.end()) {
+ entry_iter->second.supportability = supportability;
+ return;
+ }
+ }
+
+ // Parse the mime string and create an entry.
+ GetParsedMimeAndSupportability(mime_string);
+ // Update the supportability again.
+ CacheSupportability(mime_string, supportability);
+ }
+
+ // ClearCachedSupportabilities() will reset all cached |supportability|, but
+ // will not remove parsed mime infos.
+ void ClearCachedSupportabilities() {
+ ScopedLock scoped_lock(mutex_);
+ for (auto& iter : entries_) {
+ iter.second.supportability = kSupportabilityUnknown;
+ }
+ }
+
+ void SetCacheMaxSize(int size) { max_size_ = size; }
+
+ void DumpCache() {
+ ScopedLock scoped_lock(mutex_);
+ std::stringstream ss;
+ ss << "\n========Dumping MimeInfoCache========";
+ for (const auto& entry_iter : entries_) {
+ const ParsedMimeInfo& mime_info = entry_iter.second.mime_info;
+ ss << "\nMime: " << entry_iter.first;
+ ss << "\n ParsedMimeInfo:";
+ ss << "\n MimeType : " << mime_info.mime_type().ToString();
+ if (mime_info.is_valid()) {
+ if (mime_info.has_audio_info()) {
+ const ParsedMimeInfo::AudioCodecInfo& audio_info =
+ mime_info.audio_info();
+ ss << "\n Audio Codec : "
+ << GetMediaAudioCodecName(audio_info.codec);
+ ss << "\n Channels : " << audio_info.channels;
+ }
+ if (mime_info.has_video_info()) {
+ const ParsedMimeInfo::VideoCodecInfo& video_info =
+ mime_info.video_info();
+ ss << "\n Video Codec : "
+ << GetMediaVideoCodecName(video_info.codec);
+ ss << "\n Profile : " << video_info.profile;
+ ss << "\n Level : " << video_info.level;
+ ss << "\n BitDepth : " << video_info.bit_depth;
+ ss << "\n PrimaryId : "
+ << GetMediaPrimaryIdName(video_info.primary_id);
+ ss << "\n TransferId : "
+ << GetMediaTransferIdName(video_info.transfer_id);
+ ss << "\n MatrixId : "
+ << GetMediaMatrixIdName(video_info.matrix_id);
+ ss << "\n Width : " << video_info.frame_width;
+ ss << "\n Height : " << video_info.frame_height;
+ ss << "\n Fps : " << video_info.fps;
+ ss << "\n DecodeToTexture : "
+ << (video_info.decode_to_texture_required ? "true" : "false");
+ }
+ } else {
+ ss << "\n Mime info is not valid";
+ }
+
+ ss << "\n Supportability: ";
+ switch (entry_iter.second.supportability) {
+ case kSupportabilityUnknown:
+ ss << "Unknown";
+ break;
+ case kSupportabilitySupported:
+ ss << "Supported";
+ break;
+ case kSupportabilityNotSupported:
+ ss << "NotSupported";
+ break;
+ }
+ }
+ ss << "\n========End of Dumping========";
+
+ SB_DLOG(INFO) << ss.str();
+ }
+
+ private:
+ typedef std::unordered_map<std::string, Entry> Entries;
+
+ Mutex mutex_;
+ Entries entries_;
+ std::queue<Entries::iterator> fifo_queue_;
+ std::atomic_int max_size_{kDefaultCacheMaxSize};
+};
+
+SB_ONCE_INITIALIZE_FUNCTION(MimeSupportabilityContainer, GetContainer);
+
+} // namespace
+
+// static
+SB_ONCE_INITIALIZE_FUNCTION(MimeSupportabilityCache,
+ MimeSupportabilityCache::GetInstance);
+
+void MimeSupportabilityCache::SetCacheMaxSize(size_t size) {
+ GetContainer()->SetCacheMaxSize(size);
+}
+
+Supportability MimeSupportabilityCache::GetMimeSupportability(
+ const std::string& mime,
+ ParsedMimeInfo* mime_info) {
+ // Get cached parsed mime infos and supportability. If no cache is found,
+ // MimeSupportabilityContainer will parse the mime string, and return a parsed
+ // MimeType and its parsed audio/video information.
+ const MimeSupportabilityContainer::Entry& entry =
+ GetContainer()->GetParsedMimeAndSupportability(mime);
+
+ if (mime_info) {
+ // Return cached ParsedMimeInfo.
+ *mime_info = entry.mime_info;
+ }
+
+ return is_enabled_ ? entry.supportability : kSupportabilityUnknown;
+}
+
+void MimeSupportabilityCache::CacheMimeSupportability(
+ const std::string& mime,
+ Supportability supportability) {
+ if (!is_enabled_) {
+ return;
+ }
+ if (supportability == kSupportabilityUnknown) {
+ SB_LOG(WARNING) << "Rejected unknown supportability.";
+ return;
+ }
+
+ GetContainer()->CacheSupportability(mime, supportability);
+}
+
+void MimeSupportabilityCache::ClearCachedMimeSupportabilities() {
+ GetContainer()->ClearCachedSupportabilities();
+}
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/starboard/shared/starboard/media/mime_supportability_cache.h b/starboard/shared/starboard/media/mime_supportability_cache.h
new file mode 100644
index 0000000..7eb3d4f
--- /dev/null
+++ b/starboard/shared/starboard/media/mime_supportability_cache.h
@@ -0,0 +1,78 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_MIME_SUPPORTABILITY_CACHE_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_MIME_SUPPORTABILITY_CACHE_H_
+
+#include <atomic>
+#include <string>
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/parsed_mime_info.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+typedef enum Supportability {
+ kSupportabilityUnknown,
+ kSupportabilitySupported,
+ kSupportabilityNotSupported,
+} Supportability;
+
+// TODO: add unit tests for MimeSupportabilityCache
+class MimeSupportabilityCache {
+ public:
+ static MimeSupportabilityCache* GetInstance();
+
+ // When cache is not enabled, GetMimeSupportability() will always return
+ // kSupportabilityUnknown, and CacheMimeSupportability() will do nothing,
+ // but GetMimeSupportability() will still return parsed ParsedMimeInfo.
+ bool IsEnabled() const { return is_enabled_; }
+ void SetCacheEnabled(bool enabled) { is_enabled_ = enabled; }
+
+ void SetCacheMaxSize(size_t size);
+
+ // Get cached mime supportability. The parsed mime information would be
+ // returned via |mime_info| if it is not NULL.
+ Supportability GetMimeSupportability(const std::string& mime,
+ ParsedMimeInfo* mime_info);
+ // Cache mime supportability. If there's no cached parsed mime info and
+ // supportability for the mime, the function will parse the mime first and
+ // then update its supportability.
+ void CacheMimeSupportability(const std::string& mime,
+ Supportability supportability);
+
+ // Clear all cached supportabilities. Note that it will not remove cached
+ // parsed mime infos.
+ void ClearCachedMimeSupportabilities();
+
+ private:
+ // Class can only be instanced via the singleton
+ MimeSupportabilityCache() {}
+ ~MimeSupportabilityCache() {}
+
+ MimeSupportabilityCache(const MimeSupportabilityCache&) = delete;
+ MimeSupportabilityCache& operator=(const MimeSupportabilityCache&) = delete;
+
+ std::atomic_bool is_enabled_{false};
+};
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_MIME_SUPPORTABILITY_CACHE_H_
diff --git a/starboard/shared/starboard/media/mime_type.cc b/starboard/shared/starboard/media/mime_type.cc
index 035de69..4954cee 100644
--- a/starboard/shared/starboard/media/mime_type.cc
+++ b/starboard/shared/starboard/media/mime_type.cc
@@ -18,6 +18,7 @@
#include <iosfwd>
#include <locale>
#include <numeric>
+#include <sstream>
#include <string>
#include <vector>
@@ -33,26 +34,44 @@
typedef std::vector<std::string> Strings;
-MimeType::ParamType GetParamTypeByValue(const std::string& value) {
+void ParseParamTypeAndValue(const std::string& name,
+ const std::string& value,
+ MimeType::Param* param) {
+ SB_DCHECK(param);
+
+ param->name = name;
+ if (value.size() >= 2 && value[0] == '\"' && value.back() == '\"') {
+ param->type = MimeType::kParamTypeString;
+ param->string_value = value.substr(1, value.size() - 2);
+ return;
+ }
+
+ param->string_value = value;
+
int count;
int i;
if (SbStringScanF(value.c_str(), "%d%n", &i, &count) == 1 &&
count == value.size()) {
- return MimeType::kParamTypeInteger;
+ param->type = MimeType::kParamTypeInteger;
+ param->int_value = i;
+ return;
}
float f;
std::stringstream buffer(value);
buffer.imbue(std::locale::classic());
buffer >> f;
if (!buffer.fail() && buffer.rdbuf()->in_avail() == 0) {
- return MimeType::kParamTypeFloat;
+ param->type = MimeType::kParamTypeFloat;
+ param->float_value = f;
+ return;
}
-
if (value == "true" || value == "false") {
- return MimeType::kParamTypeBoolean;
+ param->type = MimeType::kParamTypeBoolean;
+ param->bool_value = value == "true";
+ return;
}
- return MimeType::kParamTypeString;
+ param->type = MimeType::kParamTypeString;
}
bool ContainsSpace(const std::string& str) {
@@ -61,7 +80,6 @@
return true;
}
}
-
return false;
}
@@ -94,28 +112,29 @@
return result;
}
-const char* ParamTypeToString(MimeType::ParamType param_type) {
- switch (param_type) {
- case MimeType::kParamTypeInteger:
- return "Integer";
- case MimeType::kParamTypeFloat:
- return "Float";
- case MimeType::kParamTypeString:
- return "String";
- case MimeType::kParamTypeBoolean:
- return "Boolean";
- default:
- SB_NOTREACHED();
- return "Unknown";
- }
-}
-
} // namespace
const int MimeType::kInvalidParamIndex = -1;
-MimeType::MimeType(const std::string& content_type)
- : raw_content_type_(content_type), is_valid_(false) {
+// static
+bool MimeType::ParseParamString(const std::string& param_string, Param* param) {
+ std::vector<std::string> name_and_value = SplitAndTrim(param_string, '=');
+ // The parameter must be on the format 'name=value' and neither |name| nor
+ // |value| can be empty. |value| must also not contain '|' and ';'.
+ if (name_and_value.size() != 2 || name_and_value[0].empty() ||
+ name_and_value[1].empty() ||
+ name_and_value[1].find('|') != std::string::npos ||
+ name_and_value[1].find(';') != std::string::npos) {
+ return false;
+ }
+
+ if (param) {
+ ParseParamTypeAndValue(name_and_value[0], name_and_value[1], param);
+ }
+ return true;
+}
+
+MimeType::MimeType(const std::string& content_type) {
Strings components = SplitAndTrim(content_type, ';');
if (components.empty()) {
@@ -135,38 +154,23 @@
}
type_ = type_and_container[0];
subtype_ = type_and_container[1];
+
components.erase(components.begin());
// 2. Verify the parameters have valid formats, we want to be strict here.
- bool has_codecs = false;
for (Strings::iterator iter = components.begin(); iter != components.end();
++iter) {
- std::vector<std::string> name_and_value = SplitAndTrim(*iter, '=');
- // The parameter must be on the format 'name=value' and neither |name| nor
- // |value| can be empty. |value| must also not contain '|'.
- if (name_and_value.size() != 2 || name_and_value[0].empty() ||
- name_and_value[1].empty() ||
- name_and_value[1].find('|') != std::string::npos) {
+ Param param;
+ if (!ParseParamString(*iter, ¶m)) {
return;
}
- Param param;
- if (name_and_value[1].size() > 2 && name_and_value[1][0] == '\"' &&
- *name_and_value[1].rbegin() == '\"') {
- param.type = kParamTypeString;
- param.value = name_and_value[1].substr(1, name_and_value[1].size() - 2);
- } else {
- param.type = GetParamTypeByValue(name_and_value[1]);
- param.value = name_and_value[1];
- }
- param.name = name_and_value[0];
+ // There can only be no more than one codecs parameter and it has to be
+ // the first parameter if it is present.
if (param.name == "codecs") {
- // There can only be no more than one codecs parameter and it has to be
- // the first parameter if it is present.
- if (!params_.empty() || has_codecs) {
+ if (!params_.empty()) {
return;
- } else {
- has_codecs = true;
}
+ codecs_ = SplitAndTrim(param.string_value, ',');
}
params_.push_back(param);
}
@@ -174,84 +178,65 @@
is_valid_ = true;
}
-const std::vector<std::string>& MimeType::GetCodecs() const {
- if (!codecs_.empty()) {
- return codecs_;
- }
- int codecs_index = GetParamIndexByName("codecs");
- if (codecs_index != 0) {
- return codecs_;
- }
- codecs_ = SplitAndTrim(params_[0].value, ',');
- return codecs_;
-}
-
int MimeType::GetParamCount() const {
- SB_DCHECK(is_valid());
-
return static_cast<int>(params_.size());
}
MimeType::ParamType MimeType::GetParamType(int index) const {
- SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
return params_[index].type;
}
const std::string& MimeType::GetParamName(int index) const {
- SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
return params_[index].name;
}
+int MimeType::GetParamIndexByName(const char* name) const {
+ for (size_t i = 0; i < params_.size(); ++i) {
+ if (SbStringCompareNoCase(params_[i].name.c_str(), name) == 0) {
+ return static_cast<int>(i);
+ }
+ }
+ return kInvalidParamIndex;
+}
+
int MimeType::GetParamIntValue(int index) const {
- SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
- if (GetParamType(index) != kParamTypeInteger) {
- return 0;
+ if (params_[index].type == kParamTypeInteger) {
+ return params_[index].int_value;
}
-
- int i;
- SbStringScanF(params_[index].value.c_str(), "%d", &i);
- return i;
+ return 0;
}
float MimeType::GetParamFloatValue(int index) const {
- SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
- if (GetParamType(index) != kParamTypeInteger &&
- GetParamType(index) != kParamTypeFloat) {
- return 0.0f;
+ if (params_[index].type == kParamTypeInteger) {
+ return params_[index].int_value;
}
-
- float f;
- std::stringstream buffer(params_[index].value.c_str());
- buffer.imbue(std::locale::classic());
- buffer >> f;
-
- return f;
+ if (params_[index].type == kParamTypeFloat) {
+ return params_[index].float_value;
+ }
+ return 0.0f;
}
const std::string& MimeType::GetParamStringValue(int index) const {
- SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
- return params_[index].value;
+ return params_[index].string_value;
}
bool MimeType::GetParamBoolValue(int index) const {
- SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
- if (GetParamType(index) != kParamTypeBoolean) {
- return false;
+ if (params_[index].type == kParamTypeBoolean) {
+ return params_[index].bool_value;
}
-
- return params_[index].value == "true";
+ return false;
}
int MimeType::GetParamIntValue(const char* name, int default_value) const {
@@ -289,23 +274,44 @@
return default_value;
}
-bool MimeType::RegisterBoolParameter(const char* name) {
- return RegisterParameter(name, kParamTypeBoolean);
-}
-
-bool MimeType::RegisterStringParameter(const char* name,
- const std::string& pattern /* = "" */) {
- if (!RegisterParameter(name, kParamTypeString)) {
+bool MimeType::ValidateIntParameter(const char* name) const {
+ if (!is_valid()) {
return false;
}
- int param_index = GetParamIndexByName(name);
- if (param_index == kInvalidParamIndex || pattern.empty()) {
+ int index = GetParamIndexByName(name);
+ if (index == kInvalidParamIndex) {
+ return true;
+ }
+ return GetParamType(index) == kParamTypeInteger;
+}
+
+bool MimeType::ValidateFloatParameter(const char* name) const {
+ if (!is_valid()) {
+ return false;
+ }
+
+ int index = GetParamIndexByName(name);
+ if (index == kInvalidParamIndex) {
+ return true;
+ }
+ ParamType type = GetParamType(index);
+ return type == kParamTypeInteger || type == kParamTypeFloat;
+}
+
+bool MimeType::ValidateStringParameter(const char* name,
+ const std::string& pattern) const {
+ if (!is_valid()) {
+ return false;
+ }
+
+ int index = GetParamIndexByName(name);
+ if (pattern.empty() || index == kInvalidParamIndex) {
return true;
}
- // Compare the parameter value with the provided pattern.
- const std::string& param_value = GetParamStringValue(param_index);
+ const std::string& param_value = params_[index].string_value;
+
bool matches = false;
size_t match_start = 0;
while (!matches) {
@@ -324,27 +330,10 @@
(match_end >= pattern.length() || pattern[match_end] == '|');
match_start = match_end + 1;
}
-
- if (matches) {
- return true;
- }
-
- SB_LOG(INFO) << "Extended Parameter '" << name << "=" << param_value
- << "' does not match the supplied pattern: '" << pattern << "'";
- is_valid_ = false;
- return false;
+ return matches;
}
-int MimeType::GetParamIndexByName(const char* name) const {
- for (size_t i = 0; i < params_.size(); ++i) {
- if (SbStringCompareNoCase(params_[i].name.c_str(), name) == 0) {
- return static_cast<int>(i);
- }
- }
- return kInvalidParamIndex;
-}
-
-bool MimeType::RegisterParameter(const char* name, ParamType param_type) {
+bool MimeType::ValidateBoolParameter(const char* name) const {
if (!is_valid()) {
return false;
}
@@ -353,25 +342,56 @@
if (index == kInvalidParamIndex) {
return true;
}
+ ParamType type = GetParamType(index);
+ return type == kParamTypeBoolean;
+}
- const std::string& param_value = GetParamStringValue(index);
- ParamType parsed_type = GetParamType(index);
-
- // Check that the parameter can be returned as the requested type.
- // Allowed conversions:
- // Any Type -> String, Int -> Float
- bool convertible =
- param_type == parsed_type || param_type == kParamTypeString ||
- (param_type == kParamTypeFloat && parsed_type == kParamTypeInteger);
- if (!convertible) {
- SB_LOG(INFO) << "Extended Parameter '" << name << "=" << param_value
- << "' can't be converted to " << ParamTypeToString(param_type);
- is_valid_ = false;
- return false;
+std::string MimeType::ToString() const {
+ if (!is_valid()) {
+ return "{ InvalidMimeType }; ";
}
-
- // All validations succeeded.
- return true;
+ std::stringstream ss;
+ ss << "{ type: " << type();
+ ss << ", subtype: " << subtype();
+ ss << ", codecs: ";
+ if (codecs_.empty()) {
+ ss << "null";
+ } else {
+ ss << codecs_[0];
+ for (size_t i = 1; i < codecs_.size(); i++) {
+ ss << "|" << codecs_[i];
+ }
+ }
+ ss << ", params: ";
+ if (params_.empty()) {
+ ss << "null";
+ } else {
+ ss << "{ ";
+ for (size_t i = 0; i < params_.size(); i++) {
+ const Param& param = params_[i];
+ if (i != 0) {
+ ss << ",";
+ }
+ ss << param.name << "=";
+ switch (param.type) {
+ case kParamTypeInteger:
+ ss << "(int)" << param.int_value;
+ break;
+ case kParamTypeFloat:
+ ss << "(float)" << param.float_value;
+ break;
+ case kParamTypeString:
+ ss << "(string)" << param.string_value;
+ break;
+ case kParamTypeBoolean:
+ ss << "(bool)" << (param.bool_value ? "true" : "false");
+ break;
+ }
+ }
+ ss << " }";
+ }
+ ss << " }";
+ return ss.str();
}
} // namespace media
diff --git a/starboard/shared/starboard/media/mime_type.h b/starboard/shared/starboard/media/mime_type.h
index a05a301..5390ed0 100644
--- a/starboard/shared/starboard/media/mime_type.h
+++ b/starboard/shared/starboard/media/mime_type.h
@@ -53,21 +53,36 @@
kParamTypeBoolean,
};
+ struct Param {
+ ParamType type;
+ std::string name;
+ std::string string_value;
+ union {
+ int int_value;
+ float float_value;
+ bool bool_value;
+ };
+ };
+
static const int kInvalidParamIndex;
+ // Expose the function as a helper function to parse a mime attribute.
+ static bool ParseParamString(const std::string& param_string, Param* param);
+
explicit MimeType(const std::string& content_type);
- const std::string& raw_content_type() const { return raw_content_type_; }
bool is_valid() const { return is_valid_; }
const std::string& type() const { return type_; }
const std::string& subtype() const { return subtype_; }
-
- const std::vector<std::string>& GetCodecs() const;
+ const std::vector<std::string>& GetCodecs() const { return codecs_; }
int GetParamCount() const;
ParamType GetParamType(int index) const;
const std::string& GetParamName(int index) const;
+ // GetParamIndexByName() will return |kInvalidParamIndex| if the param name is
+ // not found.
+ int GetParamIndexByName(const char* name) const;
int GetParamIntValue(int index) const;
float GetParamFloatValue(int index) const;
@@ -81,42 +96,29 @@
const std::string& default_value) const;
bool GetParamBoolValue(const char* name, bool default_value) const;
- // Pre-register a mime parameter of type boolean.
- // Returns true if the mime type is valid and the value passes validation.
- // If the parameter validation fails this MimeType will be marked invalid.
- // NOTE: The function returns true for missing parameters.
- bool RegisterBoolParameter(const char* name);
-
- // Pre-register a mime parameter of type string.
- // Returns true if the mime type is valid and the value passes validation.
+ // Validate functions will return true if the param contains a valid value or
+ // if param name is not found.
+ bool ValidateIntParameter(const char* name) const;
+ bool ValidateFloatParameter(const char* name) const;
// Allows passing a pattern on the format "value_1|...|value_n"
// where the parameter value must match one of the values in the pattern in
// order to be considered valid.
- // If the parameter validation fails this MimeType will be marked invalid.
- // NOTE: The function returns true for missing parameters.
- bool RegisterStringParameter(const char* name,
- const std::string& pattern = "");
+ bool ValidateStringParameter(const char* name,
+ const std::string& pattern = "") const;
+ bool ValidateBoolParameter(const char* name) const;
+
+ std::string ToString() const;
private:
- struct Param {
- ParamType type;
- std::string name;
- std::string value;
- };
-
// Use std::vector as the number of components are usually small and we'd like
// to keep the order of components.
typedef std::vector<Param> Params;
- int GetParamIndexByName(const char* name) const;
- bool RegisterParameter(const char* name, ParamType type);
-
- const std::string raw_content_type_;
- bool is_valid_;
+ bool is_valid_ = false;
std::string type_;
std::string subtype_;
+ std::vector<std::string> codecs_;
Params params_;
- mutable std::vector<std::string> codecs_;
};
} // namespace media
diff --git a/starboard/shared/starboard/media/mime_type_test.cc b/starboard/shared/starboard/media/mime_type_test.cc
index 854444d..52705f3 100644
--- a/starboard/shared/starboard/media/mime_type_test.cc
+++ b/starboard/shared/starboard/media/mime_type_test.cc
@@ -22,19 +22,6 @@
namespace media {
namespace {
-TEST(MimeTypeTest, RawContentType) {
- {
- const char kContentTypeWithSpace[] = " video/mp4; name0=123; name1=123.4 ";
- MimeType mime_type(kContentTypeWithSpace);
- EXPECT_EQ(mime_type.raw_content_type(), kContentTypeWithSpace);
- }
- {
- const char kInvalidContentType[] = "video /mp4";
- MimeType mime_type(kInvalidContentType);
- EXPECT_EQ(mime_type.raw_content_type(), kInvalidContentType);
- }
-}
-
TEST(MimeTypeTest, EmptyString) {
MimeType mime_type("");
EXPECT_FALSE(mime_type.is_valid());
@@ -343,69 +330,116 @@
EXPECT_FALSE(mime_type.GetParamBoolValue("float", false));
}
-TEST(MimeTypeTest, RegisterAndValidateParamsWithPatterns) {
+TEST(MimeTypeTest, ParseInvalidParamString) {
+ EXPECT_FALSE(MimeType::ParseParamString("", nullptr));
+ EXPECT_FALSE(MimeType::ParseParamString("invalid", nullptr));
+ EXPECT_FALSE(MimeType::ParseParamString("val=0;", nullptr));
+ EXPECT_FALSE(MimeType::ParseParamString("val=a|b", nullptr));
+ EXPECT_FALSE(MimeType::ParseParamString("val=", nullptr));
+}
+
+TEST(MimeTypeTest, ParseValidParamString) {
+ MimeType::Param result;
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=0", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeInteger);
+ EXPECT_EQ(result.int_value, 0);
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=1", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeInteger);
+ EXPECT_EQ(result.int_value, 1);
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=-1", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeInteger);
+ EXPECT_EQ(result.int_value, -1);
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=0.0", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeFloat);
+ EXPECT_EQ(result.float_value, 0.0f);
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=1.0", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeFloat);
+ EXPECT_EQ(result.float_value, 1.0f);
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=-1.0", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeFloat);
+ EXPECT_EQ(result.float_value, -1.0f);
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=true", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeBoolean);
+ EXPECT_EQ(result.bool_value, true);
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=false", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeBoolean);
+ EXPECT_EQ(result.bool_value, false);
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=\"\"", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeString);
+ EXPECT_EQ(result.string_value, "");
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=\"abc\"", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeString);
+ EXPECT_EQ(result.string_value, "abc");
+
+ EXPECT_TRUE(MimeType::ParseParamString("val=abc", &result));
+ EXPECT_EQ(result.name, "val");
+ EXPECT_EQ(result.type, MimeType::kParamTypeString);
+ EXPECT_EQ(result.string_value, "abc");
+}
+
+TEST(MimeTypeTest, ValidateParamsWithPatterns) {
MimeType mime_type("video/mp4; string=yes");
- EXPECT_TRUE(mime_type.RegisterStringParameter("string", "yes"));
- EXPECT_TRUE(mime_type.RegisterStringParameter("string", "yes|no"));
- EXPECT_TRUE(mime_type.RegisterStringParameter("string", "no|yes|no"));
- EXPECT_TRUE(mime_type.RegisterStringParameter("string", "no|no|yes"));
- EXPECT_TRUE(mime_type.RegisterStringParameter("string", "noyes|yes"));
- EXPECT_TRUE(mime_type.is_valid());
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", "yes"));
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", "yes|no"));
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", "no|yes|no"));
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", "no|no|yes"));
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", "noyes|yes"));
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string", "no"));
}
-TEST(MimeTypeTest, RegisterAndValidateParamsWithShortPatterns) {
+TEST(MimeTypeTest, ValidateParamsWithShortPatterns) {
MimeType mime_type("video/mp4; string=y");
- EXPECT_TRUE(mime_type.RegisterStringParameter("string", "y"));
- EXPECT_TRUE(mime_type.RegisterStringParameter("string", "y|n"));
- EXPECT_TRUE(mime_type.RegisterStringParameter("string", "n|y"));
- EXPECT_TRUE(mime_type.is_valid());
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", "y"));
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", "y|n"));
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", "n|y"));
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string", "n"));
}
-TEST(MimeTypeTest, RegisterAndValidateParamsWithPartialMatches) {
- {
- MimeType mime_type("video/mp4; string=yes");
- EXPECT_FALSE(mime_type.RegisterStringParameter("string", "yesno|no"));
- EXPECT_FALSE(mime_type.is_valid());
- }
- {
- MimeType mime_type("video/mp4; string=yes");
- EXPECT_FALSE(mime_type.RegisterStringParameter("string", "noyes|no"));
- EXPECT_FALSE(mime_type.is_valid());
- }
- {
- MimeType mime_type("video/mp4; string=yes");
- EXPECT_FALSE(mime_type.RegisterStringParameter("string", "no|yesno"));
- EXPECT_FALSE(mime_type.is_valid());
- }
- {
- MimeType mime_type("video/mp4; string=yes");
- EXPECT_FALSE(mime_type.RegisterStringParameter("string", "no|noyes"));
- EXPECT_FALSE(mime_type.is_valid());
- }
+TEST(MimeTypeTest, ValidateParamsWithPartialMatches) {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string", "yesno|no"));
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string", "noyes|no"));
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string", "no|yesno"));
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string", "no|noyes"));
}
-TEST(MimeTypeTest, MissingParamReturnsTrueOnRegistration) {
+TEST(MimeTypeTest, ValidateMissingParam) {
MimeType mime_type("video/mp4");
- EXPECT_TRUE(mime_type.RegisterStringParameter("string"));
- EXPECT_TRUE(mime_type.is_valid());
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string"));
EXPECT_EQ(mime_type.GetParamStringValue("string", "default"), "default");
}
-TEST(MimeTypeTest, RegisterAndValidateParamsWithEmptyishPattern) {
- {
- MimeType mime_type("video/mp4; string=yes");
- EXPECT_FALSE(mime_type.RegisterStringParameter("string", "|"));
- }
- {
- MimeType mime_type("video/mp4; string=yes");
- EXPECT_FALSE(mime_type.RegisterStringParameter("string", "||"));
- }
+TEST(MimeTypeTest, ValidateParamsWithEmptyishPattern) {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_TRUE(mime_type.ValidateStringParameter("string", ""));
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string", "|"));
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string", "||"));
}
-TEST(MimeTypeTest, CannotRegisterParamWithInvalidMimeType) {
+TEST(MimeTypeTest, ValidateParamWithInvalidMimeType) {
MimeType mime_type("video/mp4; string=");
ASSERT_FALSE(mime_type.is_valid());
- EXPECT_FALSE(mime_type.RegisterStringParameter("string"));
+ EXPECT_FALSE(mime_type.ValidateStringParameter("string"));
}
} // namespace
diff --git a/starboard/shared/starboard/media/mime_util.cc b/starboard/shared/starboard/media/mime_util.cc
new file mode 100644
index 0000000..518338f
--- /dev/null
+++ b/starboard/shared/starboard/media/mime_util.cc
@@ -0,0 +1,425 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/mime_util.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include "starboard/common/log.h"
+#include "starboard/common/media.h"
+#include "starboard/log.h"
+#include "starboard/shared/starboard/media/bitrate_supportability_cache.h"
+#include "starboard/shared/starboard/media/key_system_supportability_cache.h"
+#include "starboard/shared/starboard/media/media_support_internal.h"
+#include "starboard/shared/starboard/media/mime_supportability_cache.h"
+#include "starboard/shared/starboard/media/mime_type.h"
+#include "starboard/shared/starboard/media/parsed_mime_info.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+// RemoveAttributeFromMime() will return a new mime string with the specified
+// attribute removed. If |attribute_string| is not null, the removed attribute
+// string will be returned via |attribute_string|. Following are some examples:
+// mime: "video/webm; codecs=\"vp9\"; bitrate=300000"
+// attribute_name: "bitrate"
+// return: "video/webm; codecs=\"vp9\""
+// attribute_string: "bitrate=300000"
+//
+// mime: "video/webm; codecs=\"vp9\"; bitrate=300000; eotf=bt709"
+// attribute_name: "bitrate"
+// return: "video/webm; codecs=\"vp9\"; eotf=bt709"
+// attribute_string: "bitrate=300000"
+//
+// mime: "bitrate=300000"
+// attribute_name: "bitrate"
+// return: ""
+// attribute_string: "bitrate=300000"
+std::string RemoveAttributeFromMime(const char* mime,
+ const char* attribute_name,
+ std::string* attribute_string) {
+ size_t name_length = strlen(attribute_name);
+ if (name_length == 0) {
+ return mime;
+ }
+
+ std::string mime_without_attribute;
+ const char* start_pos = strstr(mime, attribute_name);
+ while (start_pos) {
+ if ((start_pos == mime || start_pos[-1] == ';' || isspace(start_pos[-1])) &&
+ (start_pos[name_length] &&
+ (start_pos[name_length] == '=' || isspace(start_pos[name_length])))) {
+ break;
+ }
+ start_pos += name_length;
+ start_pos = strstr(start_pos, attribute_name);
+ }
+
+ if (!start_pos) {
+ // Target attribute is not found.
+ return std::string(mime);
+ }
+ const char* end_pos = strstr(start_pos, ";");
+ if (end_pos) {
+ // There may be other attribute after target attribute.
+ if (attribute_string) {
+ // Returned |attribute_string| will not have a trailing ';'.
+ attribute_string->assign(start_pos, end_pos - start_pos);
+ }
+
+ end_pos++;
+ // Remove leading spaces.
+ while (*end_pos && isspace(*end_pos)) {
+ end_pos++;
+ }
+ if (*end_pos) {
+ // Append the string after target attribute.
+ mime_without_attribute = std::string(mime, start_pos - mime);
+ mime_without_attribute.append(end_pos);
+ } else {
+ // Target attribute is the last one. Remove trailing spaces.
+ size_t mime_length = start_pos - mime;
+ while (mime_length > 0 && (isspace(mime[mime_length - 1]))) {
+ mime_length--;
+ }
+ mime_without_attribute = std::string(mime, mime_length);
+ }
+ } else {
+ // It can't find a trailing ';'. The target attribute must be the last one.
+ size_t mime_length = start_pos - mime;
+ // Remove trailing spaces.
+ while (mime_length > 0 && (isspace(mime[mime_length - 1]))) {
+ mime_length--;
+ }
+ // Remove the trailing ';'.
+ if (mime_length > 0 && mime[mime_length - 1] == ';') {
+ mime_length--;
+ }
+ mime_without_attribute = std::string(mime, mime_length);
+ if (attribute_string) {
+ *attribute_string = std::string(start_pos);
+ }
+ }
+ return mime_without_attribute;
+}
+
+// Use SbMediaGetAudioConfiguration() to check if the platform can support
+// |channels|.
+bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels) {
+ int count = SbMediaGetAudioOutputCount();
+
+ for (int output_index = 0; output_index < count; ++output_index) {
+ SbMediaAudioConfiguration configuration;
+ if (!SbMediaGetAudioConfiguration(output_index, &configuration)) {
+ continue;
+ }
+
+ if (configuration.coding_type == coding_type &&
+ configuration.number_of_channels >= channels) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool IsSupportedKeySystem(SbMediaAudioCodec codec, const char* key_system) {
+ SB_DCHECK(key_system);
+ // KeySystemSupportabilityCache() should always return supported for empty
+ // |key_system|, so here it should always be non empty.
+ SB_DCHECK(strlen(key_system) > 0);
+
+ return SbMediaIsSupported(kSbMediaVideoCodecNone, codec, key_system);
+}
+
+bool IsSupportedKeySystem(SbMediaVideoCodec codec, const char* key_system) {
+ SB_DCHECK(key_system);
+ // KeySystemSupportabilityCache() should always return supported for empty
+ // |key_system|, so here it should always be non empty.
+ SB_DCHECK(strlen(key_system) > 0);
+
+ return SbMediaIsSupported(codec, kSbMediaAudioCodecNone, key_system);
+}
+
+bool IsSupportedAudioCodec(const ParsedMimeInfo& mime_info) {
+ SB_DCHECK(mime_info.is_valid());
+ SB_DCHECK(mime_info.mime_type().is_valid());
+ SB_DCHECK(mime_info.has_audio_info());
+
+ const MimeType& mime_type = mime_info.mime_type();
+ const ParsedMimeInfo::AudioCodecInfo& audio_info = mime_info.audio_info();
+
+ switch (audio_info.codec) {
+ case kSbMediaAudioCodecNone:
+ SB_NOTREACHED();
+ return false;
+ case kSbMediaAudioCodecAac:
+ case kSbMediaAudioCodecAc3:
+ case kSbMediaAudioCodecEac3:
+ if (mime_type.subtype() != "mp4") {
+ return false;
+ }
+ break;
+ case kSbMediaAudioCodecOpus:
+ case kSbMediaAudioCodecVorbis:
+ if (mime_type.subtype() != "webm") {
+ return false;
+ }
+ break;
+#if SB_API_VERSION >= 14
+ case kSbMediaAudioCodecMp3:
+ case kSbMediaAudioCodecFlac:
+ case kSbMediaAudioCodecPcm:
+ return false;
+#endif // SB_API_VERSION >= 14
+ }
+
+ if (!IsAudioOutputSupported(kSbMediaAudioCodingTypePcm,
+ audio_info.channels)) {
+ return false;
+ }
+
+ return SbMediaIsAudioSupported(audio_info.codec, &mime_type,
+ audio_info.bitrate);
+}
+
+bool IsSupportedVideoCodec(const ParsedMimeInfo& mime_info) {
+ SB_DCHECK(mime_info.is_valid());
+ SB_DCHECK(mime_info.mime_type().is_valid());
+ SB_DCHECK(mime_info.has_video_info());
+
+ const MimeType& mime_type = mime_info.mime_type();
+ const ParsedMimeInfo::VideoCodecInfo& video_info = mime_info.video_info();
+
+ switch (video_info.codec) {
+ case kSbMediaVideoCodecNone:
+ SB_NOTREACHED();
+ return false;
+ case kSbMediaVideoCodecH264:
+ case kSbMediaVideoCodecH265:
+ if (mime_type.subtype() != "mp4") {
+ return false;
+ }
+ break;
+ case kSbMediaVideoCodecMpeg2:
+ case kSbMediaVideoCodecTheora:
+ return false; // No associated container in YT.
+ case kSbMediaVideoCodecVc1:
+ case kSbMediaVideoCodecAv1:
+ if (mime_type.subtype() != "mp4") {
+ return false;
+ }
+ break;
+ case kSbMediaVideoCodecVp8:
+ if (mime_type.subtype() != "webm") {
+ return false;
+ }
+ break;
+ case kSbMediaVideoCodecVp9:
+ if (mime_type.subtype() != "mp4" && mime_type.subtype() != "webm") {
+ return false;
+ }
+ break;
+ }
+
+ std::string cryptoblockformat =
+ mime_type.GetParamStringValue("cryptoblockformat", "");
+ if (!cryptoblockformat.empty()) {
+ if (mime_type.subtype() != "webm" || cryptoblockformat != "subsample") {
+ return false;
+ }
+ }
+
+ return SbMediaIsVideoSupported(
+ video_info.codec, &mime_type, video_info.profile, video_info.level,
+ video_info.bit_depth, video_info.primary_id, video_info.transfer_id,
+ video_info.matrix_id, video_info.frame_width, video_info.frame_height,
+ video_info.bitrate, video_info.fps,
+ video_info.decode_to_texture_required);
+}
+
+bool ValidateAndParseBitrate(const std::string& bitrate_string, int* bitrate) {
+ SB_DCHECK(!bitrate_string.empty());
+
+ MimeType::Param param;
+ if (!MimeType::ParseParamString(bitrate_string, ¶m)) {
+ return false;
+ }
+ if (param.type != MimeType::kParamTypeInteger) {
+ return false;
+ }
+ if (bitrate) {
+ *bitrate = param.int_value;
+ }
+ return true;
+}
+
+} // namespace
+
+SbMediaSupportType CanPlayMimeAndKeySystem(const char* mime,
+ const char* key_system) {
+ SB_DCHECK(mime);
+ SB_DCHECK(key_system);
+
+ // Remove bitrate from mime string and read bitrate if presents.
+ std::string bitrate_string;
+ std::string mime_without_bitrate =
+ RemoveAttributeFromMime(mime, "bitrate", &bitrate_string);
+ int bitrate = 0;
+ if (!bitrate_string.empty()) {
+ if (!ValidateAndParseBitrate(bitrate_string, &bitrate)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
+ if (bitrate < 0) {
+ // Reject invalid bitrate.
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ // Get cached parsed mime infos and supportability. If it is not found in the
+ // cache, MimeSupportabilityCache would parse the mime string and return a
+ // ParsedMimeInfo.
+ ParsedMimeInfo mime_info;
+ Supportability mime_supportability =
+ MimeSupportabilityCache::GetInstance()->GetMimeSupportability(
+ mime_without_bitrate, &mime_info);
+ // Overwrite the bitrate.
+ mime_info.SetBitrate(bitrate);
+
+ if (mime_info.disable_cache()) {
+ // Disable all caches if required.
+ mime_supportability = kSupportabilityUnknown;
+ MimeSupportabilityCache::GetInstance()->SetCacheEnabled(false);
+ KeySystemSupportabilityCache::GetInstance()->SetCacheEnabled(false);
+ BitrateSupportabilityCache::GetInstance()->SetCacheEnabled(false);
+ }
+
+ // Reject mime if cached result is not supported.
+ if (mime_supportability == kSupportabilityNotSupported) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ // Reject mime if parsed mime info is invalid.
+ if (!mime_info.is_valid()) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ const MimeType& mime_type = mime_info.mime_type();
+ const std::vector<std::string>& codecs = mime_type.GetCodecs();
+
+ // Quick check for mp4 format.
+ if (codecs.size() == 0) {
+ // This happens when the H5 player is either querying for progressive
+ // playback support, or probing for generic mp4 support without specific
+ // codecs.
+ if (mime_type.subtype() == "mp4") {
+ return kSbMediaSupportTypeMaybe;
+ } else {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
+ // Reject mime if it doesn't have any valid codec info.
+ if (!mime_info.has_audio_info() && !mime_info.has_video_info()) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ // Get cached key system supportability. Note that we check if audio or video
+ // codec supports key system separately.
+ if (mime_info.has_audio_info()) {
+ Supportability key_system_supportability =
+ KeySystemSupportabilityCache::GetInstance()->GetKeySystemSupportability(
+ mime_info.audio_info().codec, key_system);
+ if (key_system_supportability == kSupportabilityUnknown) {
+ key_system_supportability =
+ IsSupportedKeySystem(mime_info.audio_info().codec, key_system)
+ ? kSupportabilitySupported
+ : kSupportabilityNotSupported;
+ KeySystemSupportabilityCache::GetInstance()->CacheKeySystemSupportability(
+ mime_info.audio_info().codec, key_system, key_system_supportability);
+ }
+ // Reject mime if audio codec doesn't support the key system.
+ if (key_system_supportability == kSupportabilityNotSupported) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+ if (mime_info.has_video_info()) {
+ Supportability key_system_supportability =
+ KeySystemSupportabilityCache::GetInstance()->GetKeySystemSupportability(
+ mime_info.video_info().codec, key_system);
+ if (key_system_supportability == kSupportabilityUnknown) {
+ key_system_supportability =
+ IsSupportedKeySystem(mime_info.video_info().codec, key_system)
+ ? kSupportabilitySupported
+ : kSupportabilityNotSupported;
+ KeySystemSupportabilityCache::GetInstance()->CacheKeySystemSupportability(
+ mime_info.video_info().codec, key_system, key_system_supportability);
+ }
+ // Reject mime if video codec doesn't the key system.
+ if (key_system_supportability == kSupportabilityNotSupported) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
+ // Get cached bitrate supportability.
+ Supportability bitrate_supportability =
+ BitrateSupportabilityCache::GetInstance()->GetBitrateSupportability(
+ mime_info);
+
+ // Reject mime if bitrate is not supported.
+ if (bitrate_supportability == kSupportabilityNotSupported) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ // Return supported if mime and bitrate are all supported.
+ if (mime_supportability == kSupportabilitySupported &&
+ bitrate_supportability == kSupportabilitySupported) {
+ return kSbMediaSupportTypeProbably;
+ }
+
+ // At this point, either mime or bitrate supportability must be unknown.
+ // Call platform functions to check if they are supported.
+ SB_DCHECK(mime_supportability == kSupportabilityUnknown ||
+ bitrate_supportability == kSupportabilityUnknown);
+ if (mime_info.has_audio_info() && !IsSupportedAudioCodec(mime_info)) {
+ mime_supportability = kSupportabilityNotSupported;
+ } else if (mime_info.has_video_info() && !IsSupportedVideoCodec(mime_info)) {
+ mime_supportability = kSupportabilityNotSupported;
+ } else {
+ mime_supportability = kSupportabilitySupported;
+ }
+
+ // Cache mime supportability when bitrate supportability is known.
+ if (bitrate_supportability == kSupportabilitySupported) {
+ MimeSupportabilityCache::GetInstance()->CacheMimeSupportability(
+ mime_without_bitrate, mime_supportability);
+ }
+
+ SB_DCHECK(mime_supportability != kSupportabilityUnknown);
+ return mime_supportability == kSupportabilitySupported
+ ? kSbMediaSupportTypeProbably
+ : kSbMediaSupportTypeNotSupported;
+}
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/starboard/shared/starboard/media/mime_util.h b/starboard/shared/starboard/media/mime_util.h
new file mode 100644
index 0000000..e79dac1
--- /dev/null
+++ b/starboard/shared/starboard/media/mime_util.h
@@ -0,0 +1,60 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_MIME_UTIL_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_MIME_UTIL_H_
+
+#include <string>
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+// Calls to canPlayType() and isTypeSupported() are redirected to this function.
+// Following are some example inputs:
+// canPlayType(video/mp4)
+// canPlayType(video/mp4; codecs="avc1.42001E, mp4a.40.2")
+// canPlayType(video/webm)
+// isTypeSupported(video/webm; codecs="vp9")
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; width=640)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; width=99999)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; height=360)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; height=99999)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; framerate=30)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; framerate=9999)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; bitrate=300000)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; bitrate=2000000000)
+// isTypeSupported(audio/mp4; codecs="mp4a.40.2")
+// isTypeSupported(audio/webm; codecs="vorbis")
+// isTypeSupported(video/webm; codecs="vp9")
+// isTypeSupported(video/webm; codecs="vp9")
+// isTypeSupported(audio/webm; codecs="opus")
+// isTypeSupported(audio/mp4; codecs="mp4a.40.2"; channels=2)
+// isTypeSupported(audio/mp4; codecs="mp4a.40.2"; channels=99)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; decode-to-texture=true)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; decode-to-texture=false)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; decode-to-texture=invalid)
+SbMediaSupportType CanPlayMimeAndKeySystem(const char* mime,
+ const char* key_system);
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_MIME_UTIL_H_
diff --git a/starboard/shared/starboard/media/parsed_mime_info.cc b/starboard/shared/starboard/media/parsed_mime_info.cc
new file mode 100644
index 0000000..8894289
--- /dev/null
+++ b/starboard/shared/starboard/media/parsed_mime_info.cc
@@ -0,0 +1,178 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/parsed_mime_info.h"
+
+#include <string>
+
+#include "starboard/common/log.h"
+#include "starboard/common/media.h"
+#include "starboard/shared/starboard/media/codec_util.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+const int64_t kDefaultAudioChannels = 2;
+
+// Turns |eotf| into value of SbMediaTransferId. If |eotf| isn't recognized the
+// function returns kSbMediaTransferIdUnknown.
+// This function supports all eotfs required by YouTube TV HTML5 Technical
+// Requirements.
+SbMediaTransferId GetTransferIdFromString(const std::string& transfer_id) {
+ if (transfer_id == "bt709") {
+ return kSbMediaTransferIdBt709;
+ } else if (transfer_id == "smpte2084") {
+ return kSbMediaTransferIdSmpteSt2084;
+ } else if (transfer_id == "arib-std-b67") {
+ return kSbMediaTransferIdAribStdB67;
+ }
+ return kSbMediaTransferIdUnknown;
+}
+
+} // namespace
+
+ParsedMimeInfo::ParsedMimeInfo(const std::string& mime_string)
+ : mime_type_(mime_string) {
+ ParseMimeInfo();
+}
+
+void ParsedMimeInfo::SetBitrate(int bitrate) {
+ audio_info_.bitrate = bitrate;
+ video_info_.bitrate = bitrate;
+}
+
+void ParsedMimeInfo::ParseMimeInfo() {
+ if (!mime_type_.is_valid()) {
+ is_valid_ = false;
+ return;
+ }
+
+ // Read "disablecache".
+ if (!mime_type_.ValidateBoolParameter("disablecache")) {
+ is_valid_ = false;
+ return;
+ }
+ disable_cache_ = mime_type_.GetParamBoolValue("disablecache", false);
+
+ // We only support audio or video type.
+ if (mime_type_.type() != "audio" && mime_type_.type() != "video") {
+ is_valid_ = false;
+ return;
+ }
+
+ auto codecs = mime_type_.GetCodecs();
+ // We only support up to one audio codec and one video codec.
+ if (codecs.size() > 2) {
+ is_valid_ = false;
+ return;
+ }
+
+ for (const auto& codec : codecs) {
+ if (!has_audio_info() && ParseAudioInfo(codec)) {
+ continue;
+ }
+ if (!has_video_info() && ParseVideoInfo(codec)) {
+ continue;
+ }
+ // It either has an invalid codec or has two codecs of same type.
+ ResetCodecInfos();
+ is_valid_ = false;
+ return;
+ }
+}
+
+bool ParsedMimeInfo::ParseAudioInfo(const std::string& codec) {
+ SB_DCHECK(mime_type_.is_valid());
+ SB_DCHECK(!has_audio_info());
+
+ SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codec.c_str());
+ if (audio_codec == kSbMediaAudioCodecNone) {
+ return false;
+ }
+ if (!mime_type_.ValidateIntParameter("channels") ||
+ !mime_type_.ValidateIntParameter("bitrate")) {
+ return false;
+ }
+ audio_info_.codec = audio_codec;
+ audio_info_.channels =
+ mime_type_.GetParamIntValue("channels", kDefaultAudioChannels);
+ audio_info_.bitrate = mime_type_.GetParamIntValue("bitrate", 0);
+
+ return audio_info_.channels >= 0 && audio_info_.bitrate >= 0;
+}
+
+bool ParsedMimeInfo::ParseVideoInfo(const std::string& codec) {
+ SB_DCHECK(mime_type_.is_valid());
+ SB_DCHECK(!has_video_info());
+
+ if (!ParseVideoCodec(codec.c_str(), &video_info_.codec, &video_info_.profile,
+ &video_info_.level, &video_info_.bit_depth,
+ &video_info_.primary_id, &video_info_.transfer_id,
+ &video_info_.matrix_id)) {
+ return false;
+ }
+
+ if (video_info_.codec == kSbMediaVideoCodecNone) {
+ return false;
+ }
+
+ std::string eotf = mime_type_.GetParamStringValue("eotf", "");
+ if (!eotf.empty()) {
+ SbMediaTransferId transfer_id_from_eotf = GetTransferIdFromString(eotf);
+ if (transfer_id_from_eotf == kSbMediaTransferIdUnknown) {
+ // The eotf is an unknown value, mark the codec info as invalid.
+ SB_LOG(WARNING) << "Unknown eotf " << eotf << ".";
+ return false;
+ }
+ SB_LOG_IF(WARNING,
+ video_info_.transfer_id != kSbMediaTransferIdUnspecified &&
+ video_info_.transfer_id != transfer_id_from_eotf)
+ << "transfer_id " << video_info_.transfer_id
+ << " set by the codec string \"" << video_info_.codec
+ << "\" will be overwritten by the eotf attribute " << eotf;
+ video_info_.transfer_id = transfer_id_from_eotf;
+ }
+
+ if (!mime_type_.ValidateIntParameter("width") ||
+ !mime_type_.ValidateIntParameter("height") ||
+ !mime_type_.ValidateIntParameter("framerate") ||
+ !mime_type_.ValidateIntParameter("bitrate") ||
+ !mime_type_.ValidateBoolParameter("decode-to-texture")) {
+ return false;
+ }
+
+ video_info_.frame_width = mime_type_.GetParamIntValue("width", 0);
+ video_info_.frame_height = mime_type_.GetParamIntValue("height", 0);
+ video_info_.fps = mime_type_.GetParamIntValue("framerate", 0);
+ video_info_.bitrate = mime_type_.GetParamIntValue("bitrate", 0);
+ video_info_.decode_to_texture_required =
+ mime_type_.GetParamBoolValue("decode-to-texture", false);
+
+ return video_info_.frame_width >= 0 && video_info_.frame_height >= 0 &&
+ video_info_.fps >= 0 && video_info_.bitrate >= 0;
+}
+
+void ParsedMimeInfo::ResetCodecInfos() {
+ audio_info_.codec = kSbMediaAudioCodecNone;
+ video_info_.codec = kSbMediaVideoCodecNone;
+}
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/starboard/shared/starboard/media/parsed_mime_info.h b/starboard/shared/starboard/media/parsed_mime_info.h
new file mode 100644
index 0000000..5be03a5
--- /dev/null
+++ b/starboard/shared/starboard/media/parsed_mime_info.h
@@ -0,0 +1,106 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_PARSED_MIME_INFO_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_PARSED_MIME_INFO_H_
+
+#include <string>
+
+#include "starboard/common/log.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/mime_type.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+// TODO: add unit tests for ParsedMimeInfo
+class ParsedMimeInfo {
+ public:
+ struct AudioCodecInfo {
+ SbMediaAudioCodec codec = kSbMediaAudioCodecNone;
+ int channels;
+ int bitrate;
+ };
+
+ struct VideoCodecInfo {
+ SbMediaVideoCodec codec = kSbMediaVideoCodecNone;
+ int profile;
+ int level;
+ int bit_depth;
+ SbMediaPrimaryId primary_id;
+ SbMediaTransferId transfer_id;
+ SbMediaMatrixId matrix_id;
+ int frame_width;
+ int frame_height;
+ int fps;
+ int bitrate;
+ bool decode_to_texture_required;
+ };
+
+ ParsedMimeInfo() : mime_type_("") {}
+ explicit ParsedMimeInfo(const std::string& mime_string);
+
+ const MimeType& mime_type() const { return mime_type_; }
+
+ bool is_valid() const { return is_valid_; }
+
+ // A switch in the mime string to disable caches.
+ bool disable_cache() const { return disable_cache_; }
+
+ bool has_audio_info() const {
+ return audio_info_.codec != kSbMediaAudioCodecNone;
+ }
+ // Extra information for audio codec. Note that audio_info() can only be
+ // used when has_audio_info() returns true.
+ const AudioCodecInfo& audio_info() const {
+ SB_DCHECK(has_audio_info());
+ return audio_info_;
+ }
+
+ bool has_video_info() const {
+ return video_info_.codec != kSbMediaVideoCodecNone;
+ }
+ // Extra information for video codec. Note that video_info() can only be
+ // used when has_video_info() returns true.
+ const VideoCodecInfo& video_info() const {
+ SB_DCHECK(has_video_info());
+ return video_info_;
+ }
+
+ // Allow to overwrite the bitrate.
+ void SetBitrate(int bitrate);
+
+ private:
+ void ParseMimeInfo();
+ bool ParseAudioInfo(const std::string& codec);
+ bool ParseVideoInfo(const std::string& codec);
+
+ void ResetCodecInfos();
+
+ MimeType mime_type_;
+ bool is_valid_ = true;
+ bool disable_cache_ = false;
+ AudioCodecInfo audio_info_;
+ VideoCodecInfo video_info_;
+};
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_PARSED_MIME_INFO_H_
diff --git a/starboard/shared/starboard/player/filter/testing/test_util.cc b/starboard/shared/starboard/player/filter/testing/test_util.cc
index 9bfdb7e..d87f71d 100644
--- a/starboard/shared/starboard/player/filter/testing/test_util.cc
+++ b/starboard/shared/starboard/player/filter/testing/test_util.cc
@@ -18,6 +18,7 @@
#include "starboard/common/log.h"
#include "starboard/directory.h"
#include "starboard/shared/starboard/media/media_support_internal.h"
+#include "starboard/shared/starboard/media/mime_type.h"
#include "starboard/shared/starboard/player/filter/player_components.h"
#include "starboard/shared/starboard/player/filter/stub_player_components_factory.h"
#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
@@ -33,6 +34,7 @@
namespace testing {
namespace {
+using ::starboard::shared::starboard::media::MimeType;
using ::testing::AssertionFailure;
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
@@ -155,12 +157,11 @@
}
// Filter files of unsupported codec.
- if (!SbMediaIsAudioSupported(
- audio_file_info.audio_codec,
- GetContentTypeFromAudioCodec(audio_file_info.audio_codec,
- extra_mime_attributes)
- .c_str(),
- audio_file_info.bitrate)) {
+ const std::string audio_mime = GetContentTypeFromAudioCodec(
+ audio_file_info.audio_codec, extra_mime_attributes);
+ const MimeType audio_mime_type(audio_mime.c_str());
+ if (!SbMediaIsAudioSupported(audio_file_info.audio_codec, &audio_mime_type,
+ audio_file_info.bitrate)) {
continue;
}
@@ -203,13 +204,15 @@
const auto& video_sample_info =
dmp_reader.GetPlayerSampleInfo(kSbMediaTypeVideo, 0)
.video_sample_info;
-
+ const std::string video_mime = dmp_reader.video_mime_type();
+ const MimeType video_mime_type(video_mime.c_str());
if (SbMediaIsVideoSupported(
- dmp_reader.video_codec(), dmp_reader.video_mime_type().c_str(),
- -1, -1, 8, kSbMediaPrimaryIdUnspecified,
- kSbMediaTransferIdUnspecified, kSbMediaMatrixIdUnspecified,
- video_sample_info.frame_width, video_sample_info.frame_height,
- dmp_reader.video_bitrate(), dmp_reader.video_fps(), false)) {
+ dmp_reader.video_codec(),
+ video_mime.size() > 0 ? &video_mime_type : nullptr, -1, -1, 8,
+ kSbMediaPrimaryIdUnspecified, kSbMediaTransferIdUnspecified,
+ kSbMediaMatrixIdUnspecified, video_sample_info.frame_width,
+ video_sample_info.frame_height, dmp_reader.video_bitrate(),
+ dmp_reader.video_fps(), false)) {
test_params.push_back(std::make_tuple(filename, output_mode));
}
}
diff --git a/starboard/shared/starboard/player/player_create.cc b/starboard/shared/starboard/player/player_create.cc
index c2e0a07..16bc57f 100644
--- a/starboard/shared/starboard/player/player_create.cc
+++ b/starboard/shared/starboard/player/player_create.cc
@@ -30,12 +30,13 @@
#include "starboard/shared/starboard/player/video_dmp_writer.h"
#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
-using starboard::shared::media_session::
+using ::starboard::shared::media_session::
UpdateActiveSessionPlatformPlaybackState;
-using starboard::shared::media_session::kPlaying;
-using starboard::shared::starboard::player::filter::
+using ::starboard::shared::media_session::kPlaying;
+using ::starboard::shared::starboard::media::MimeType;
+using ::starboard::shared::starboard::player::filter::
FilterBasedPlayerWorkerHandler;
-using starboard::shared::starboard::player::PlayerWorker;
+using ::starboard::shared::starboard::player::PlayerWorker;
SbPlayer SbPlayerCreate(SbWindow window,
const SbPlayerCreationParam* creation_param,
@@ -135,16 +136,19 @@
}
const int64_t kDefaultBitRate = 0;
- if (audio_codec != kSbMediaAudioCodecNone &&
- !SbMediaIsAudioSupported(audio_codec, audio_mime, kDefaultBitRate)) {
- SB_LOG(ERROR) << "Unsupported audio codec "
- << starboard::GetMediaAudioCodecName(audio_codec) << ".";
- player_error_func(
- kSbPlayerInvalid, context, kSbPlayerErrorDecode,
- starboard::FormatString("Unsupported audio codec: %s",
- starboard::GetMediaAudioCodecName(audio_codec))
- .c_str());
- return kSbPlayerInvalid;
+ if (audio_codec != kSbMediaAudioCodecNone) {
+ const MimeType audio_mime_type(audio_mime);
+ if (!SbMediaIsAudioSupported(audio_codec, &audio_mime_type,
+ kDefaultBitRate)) {
+ SB_LOG(ERROR) << "Unsupported audio codec "
+ << starboard::GetMediaAudioCodecName(audio_codec) << ".";
+ player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+ starboard::FormatString(
+ "Unsupported audio codec: %s",
+ starboard::GetMediaAudioCodecName(audio_codec))
+ .c_str());
+ return kSbPlayerInvalid;
+ }
}
const int kDefaultProfile = -1;
@@ -153,22 +157,24 @@
const int kDefaultFrameWidth = 0;
const int kDefaultFrameHeight = 0;
const int kDefaultFrameRate = 0;
- if (video_codec != kSbMediaVideoCodecNone &&
- !SbMediaIsVideoSupported(
- video_codec, video_mime, kDefaultProfile, kDefaultLevel,
- kDefaultColorDepth, kSbMediaPrimaryIdUnspecified,
- kSbMediaTransferIdUnspecified, kSbMediaMatrixIdUnspecified,
- kDefaultFrameWidth, kDefaultFrameHeight, kDefaultBitRate,
- kDefaultFrameRate,
- output_mode == kSbPlayerOutputModeDecodeToTexture)) {
- SB_LOG(ERROR) << "Unsupported video codec "
- << starboard::GetMediaVideoCodecName(video_codec) << ".";
- player_error_func(
- kSbPlayerInvalid, context, kSbPlayerErrorDecode,
- starboard::FormatString("Unsupported video codec: %s",
- starboard::GetMediaVideoCodecName(video_codec))
- .c_str());
- return kSbPlayerInvalid;
+ if (video_codec != kSbMediaVideoCodecNone) {
+ const MimeType video_mime_type(video_mime);
+ if (!SbMediaIsVideoSupported(
+ video_codec, &video_mime_type, kDefaultProfile, kDefaultLevel,
+ kDefaultColorDepth, kSbMediaPrimaryIdUnspecified,
+ kSbMediaTransferIdUnspecified, kSbMediaMatrixIdUnspecified,
+ kDefaultFrameWidth, kDefaultFrameHeight, kDefaultBitRate,
+ kDefaultFrameRate,
+ output_mode == kSbPlayerOutputModeDecodeToTexture)) {
+ SB_LOG(ERROR) << "Unsupported video codec "
+ << starboard::GetMediaVideoCodecName(video_codec) << ".";
+ player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+ starboard::FormatString(
+ "Unsupported video codec: %s",
+ starboard::GetMediaVideoCodecName(video_codec))
+ .c_str());
+ return kSbPlayerInvalid;
+ }
}
if (audio_codec != kSbMediaAudioCodecNone && !audio_sample_info) {
diff --git a/starboard/shared/starboard/thread_local_storage_external_access_hack_public.cc b/starboard/shared/starboard/thread_local_storage_external_access_hack_public.cc
deleted file mode 100644
index c841163..0000000
--- a/starboard/shared/starboard/thread_local_storage_external_access_hack_public.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/shared/starboard/thread_local_storage_external_access_hack_public.h"
-
-#include "starboard/shared/starboard/thread_local_storage_internal.h"
-
-void StarboardSharedTLSKeyManagerInitializeTLSForThread() {
- starboard::shared::TLSKeyManager::Get()->InitializeTLSForThread();
-}
-
-void StarboardSharedTLSKeyManagerShutdownTLSForThread() {
- starboard::shared::TLSKeyManager::Get()->ShutdownTLSForThread();
-}
diff --git a/starboard/shared/starboard/thread_local_storage_external_access_hack_public.h b/starboard/shared/starboard/thread_local_storage_external_access_hack_public.h
deleted file mode 100644
index 003869b..0000000
--- a/starboard/shared/starboard/thread_local_storage_external_access_hack_public.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2016 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef STARBOARD_SHARED_STARBOARD_THREAD_LOCAL_STORAGE_EXTERNAL_ACCESS_HACK_PUBLIC_H_
-#define STARBOARD_SHARED_STARBOARD_THREAD_LOCAL_STORAGE_EXTERNAL_ACCESS_HACK_PUBLIC_H_
-
-// This file defines "backdoor" functions that allow external libraries access
-// to internal functions. In particular, this is currently being used by Cobalt
-// on some non-Starboard platforms to gain access to thread local storage thread
-// initialization and shutdown functions since some platforms are only "kinda"
-// using Starboard right now, via glimp. Since most of non-Starboard Cobalt
-// creates threads through pthreads, not Starboard, we need to call these
-// functions on newly created pthread-created threads.
-
-// TODO: This file should ABSOLUTELY be removed as soon as all platforms are
-// fully up and running on Starboard.
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void StarboardSharedTLSKeyManagerInitializeTLSForThread();
-void StarboardSharedTLSKeyManagerShutdownTLSForThread();
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // STARBOARD_SHARED_STARBOARD_THREAD_LOCAL_STORAGE_EXTERNAL_ACCESS_HACK_PUBLIC_H_
diff --git a/starboard/shared/starboard/thread_local_storage_internal.cc b/starboard/shared/starboard/thread_local_storage_internal.cc
index 02a1763..55bfb03 100644
--- a/starboard/shared/starboard/thread_local_storage_internal.cc
+++ b/starboard/shared/starboard/thread_local_storage_internal.cc
@@ -284,54 +284,6 @@
return data_->key_table_[key->index].values[current_thread_id];
}
-void TLSKeyManager::InitializeTLSForThread() {
- int current_thread_id = GetCurrentThreadId();
-
- ScopedLock lock(mutex_);
-
- const size_t table_size = data_->key_table_.size();
- for (int i = 0; i < table_size; ++i) {
- KeyRecord* key_record = &data_->key_table_[i];
- if (key_record->valid) {
- key_record->values[current_thread_id] = NULL;
- }
- }
-}
-
-void TLSKeyManager::ShutdownTLSForThread() {
- int current_thread_id = GetCurrentThreadId();
-
- ScopedLock lock(mutex_);
-
- // Apply the destructors multiple times (4 is the minimum value
- // according to the specifications). This is necessary if one of
- // the destructors adds new values to the key map.
- for (int d = 0; d < 4; ++d) {
- // Move the map into a new temporary map so that we can iterate
- // through that while the original s_tls_thread_keys may have more
- // values added to it via destructor calls.
- const size_t table_size = data_->key_table_.size();
-
- for (int i = 0; i < table_size; ++i) {
- KeyRecord* key_record = &data_->key_table_[i];
- if (key_record->valid) {
- void* value = key_record->values[current_thread_id];
- key_record->values[current_thread_id] = NULL;
- SbThreadLocalDestructor destructor = key_record->destructor;
-
- if (value && destructor) {
- mutex_.Release();
- destructor(value);
- mutex_.Acquire();
- }
- }
- }
- }
-
- data_->thread_id_map_.Erase(SbThreadGetId());
- data_->available_thread_ids_.push_back(current_thread_id);
-}
-
bool TLSKeyManager::IsKeyActive(SbThreadLocalKey key) {
return data_->key_table_[key->index].valid;
}
diff --git a/starboard/shared/starboard/thread_local_storage_internal.h b/starboard/shared/starboard/thread_local_storage_internal.h
index ffa225a..13e988a 100644
--- a/starboard/shared/starboard/thread_local_storage_internal.h
+++ b/starboard/shared/starboard/thread_local_storage_internal.h
@@ -46,12 +46,6 @@
// Returns the thread local value for the given key.
void* GetLocalValue(SbThreadLocalKey key);
- // Called whenever a thread is created.
- void InitializeTLSForThread();
-
- // Called whenever a thread is destroyed.
- void ShutdownTLSForThread();
-
private:
// We add 1 to kSbMaxThreads here to account for the main thread.
static const int kMaxThreads = kSbMaxThreads + 1;
diff --git a/starboard/shared/stub/media_is_audio_supported.cc b/starboard/shared/stub/media_is_audio_supported.cc
index d9c70c5..853eca0 100644
--- a/starboard/shared/stub/media_is_audio_supported.cc
+++ b/starboard/shared/stub/media_is_audio_supported.cc
@@ -16,8 +16,10 @@
#include "starboard/media.h"
+using ::starboard::shared::starboard::media::MimeType;
+
bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- const char* content_type,
+ const MimeType* mime_type,
int64_t bitrate) {
return false;
}
diff --git a/starboard/shared/stub/media_is_video_supported.cc b/starboard/shared/stub/media_is_video_supported.cc
index e16ee53..1049791 100644
--- a/starboard/shared/stub/media_is_video_supported.cc
+++ b/starboard/shared/stub/media_is_video_supported.cc
@@ -16,8 +16,10 @@
#include "starboard/media.h"
+using ::starboard::shared::starboard::media::MimeType;
+
bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
- const char* content_type,
+ const MimeType* mime_type,
int profile,
int level,
int bit_depth,
diff --git a/starboard/shared/win32/media_is_audio_supported.cc b/starboard/shared/win32/media_is_audio_supported.cc
index b1c99f3..f838ac3 100644
--- a/starboard/shared/win32/media_is_audio_supported.cc
+++ b/starboard/shared/win32/media_is_audio_supported.cc
@@ -18,8 +18,10 @@
#include "starboard/configuration_constants.h"
#include "starboard/media.h"
+using ::starboard::shared::starboard::media::MimeType;
+
bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- const char* content_type,
+ const MimeType* mime_type,
int64_t bitrate) {
if (audio_codec != kSbMediaAudioCodecAac &&
audio_codec != kSbMediaAudioCodecOpus) {
diff --git a/starboard/shared/win32/media_is_video_supported.cc b/starboard/shared/win32/media_is_video_supported.cc
index 4b49b55..b7af5ce 100644
--- a/starboard/shared/win32/media_is_video_supported.cc
+++ b/starboard/shared/win32/media_is_video_supported.cc
@@ -22,16 +22,14 @@
#include "starboard/configuration_constants.h"
#include "starboard/shared/starboard/media/media_util.h"
+using ::starboard::shared::starboard::media::MimeType;
+
namespace {
#if SB_API_VERSION >= SB_RUNTIME_CONFIGS_VERSION || \
defined(SB_HAS_MEDIA_WEBM_VP9_SUPPORT)
// Cache the VP9 support status since the check may be expensive.
-enum Vp9Support {
- kVp9SupportUnknown,
- kVp9SupportYes,
- kVp9SupportNo
-};
+enum Vp9Support { kVp9SupportUnknown, kVp9SupportYes, kVp9SupportNo };
Vp9Support s_vp9_support = kVp9SupportUnknown;
// Check for VP9 support. Since this is used by a starboard function, it
@@ -76,7 +74,7 @@
return s_vp9_support == kVp9SupportYes;
}
#else // SB_API_VERSION >= SB_RUNTIME_CONFIGS_VERSION ||
- // defined(SB_HAS_MEDIA_WEBM_VP9_SUPPORT)
+// defined(SB_HAS_MEDIA_WEBM_VP9_SUPPORT)
bool IsVp9Supported() {
return false;
}
@@ -86,7 +84,7 @@
} // namespace
bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
- const char* content_type,
+ const MimeType* mime_type,
int profile,
int level,
int bit_depth,
@@ -105,18 +103,18 @@
int max_height = 1080;
if (video_codec == kSbMediaVideoCodecVp9) {
- // Vp9 supports 8k only in whitelisted platforms, up to 4k in the others.
+// Vp9 supports 8k only in whitelisted platforms, up to 4k in the others.
#ifdef ENABLE_VP9_8K_SUPPORT
max_width = 7680;
max_height = 4320;
-#else // ENABLE_VP9_8K_SUPPORT
+#else // ENABLE_VP9_8K_SUPPORT
max_width = 3840;
max_height = 2160;
#endif // ENABLE_VP9_8K_SUPPORT
} else if (video_codec == kSbMediaVideoCodecH264) {
- // Not all devices can support 4k H264; some (e.g. xb1) may crash in
- // the decoder if provided too high of a resolution. Therefore
- // platforms must explicitly opt-in to support 4k H264.
+// Not all devices can support 4k H264; some (e.g. xb1) may crash in
+// the decoder if provided too high of a resolution. Therefore
+// platforms must explicitly opt-in to support 4k H264.
#ifdef ENABLE_H264_4K_SUPPORT
max_width = 3840;
max_height = 2160;
diff --git a/third_party/crashpad/handler/BUILD.gn b/third_party/crashpad/handler/BUILD.gn
index 9442c21..10582a8 100644
--- a/third_party/crashpad/handler/BUILD.gn
+++ b/third_party/crashpad/handler/BUILD.gn
@@ -170,6 +170,7 @@
crashpad_executable("crashpad_handler") {
if (crashpad_is_in_starboard) {
+ install_target = !crashpad_is_android
check_includes = false
data_deps = [ "//third_party/icu:icudata" ]
}
diff --git a/third_party/libvpx/.gitignore b/third_party/libvpx/.gitignore
index 5f26835..aeb6d58 100644
--- a/third_party/libvpx/.gitignore
+++ b/third_party/libvpx/.gitignore
@@ -1,5 +1,6 @@
*.S
*.a
+!/platforms/*/libvpx.a
*.asm.s
*.d
*.gcda
diff --git a/third_party/opus/.gitignore b/third_party/opus/.gitignore
index eccd0a2..8cf3ed1 100644
--- a/third_party/opus/.gitignore
+++ b/third_party/opus/.gitignore
@@ -9,6 +9,7 @@
compile
config.guess
config.h
+!/*/config.h
config.h.in
config.log
config.status
@@ -67,6 +68,7 @@
doc/man
package_version
version.h
+!/*/version.h
celt/Debug
celt/Release
celt/x64