Import Cobalt 20.lts.4.266393
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py index 0154522..400202f 100644 --- a/src/cobalt/black_box_tests/black_box_tests.py +++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -64,14 +64,8 @@ _device_params = None # Binding address used to create the test server. _binding_address = None - - -def GetDeviceParams(): - - global _device_params - _device_params = cobalt_runner.GetDeviceParamsFromCommandLine() - # Keep other modules from seeing these args - sys.argv = sys.argv[:1] +# Port used to create the web platform test http server. +_wpt_http_port = None class BlackBoxTestCase(unittest.TestCase): @@ -104,9 +98,11 @@ def GetBindingAddress(self): return _binding_address + def GetWptHttpPort(self): + return _wpt_http_port + def LoadTests(platform, config, device_id, out_directory): - launcher = abstract_launcher.LauncherFactory( platform, 'cobalt', @@ -134,19 +130,44 @@ class BlackBoxTests(object): """Helper class to run all black box tests and return results.""" - def __init__(self, test_name=None, proxy_port_number=None): + def __init__(self, + server_binding_address, + proxy_address=None, + proxy_port=None, + test_name=None, + wpt_http_port=None, + device_ips=None): logging.basicConfig(level=logging.DEBUG) - GetDeviceParams() - # Port number used to create the proxy server. If the --proxy target param - # is not set, a random free port is used. - if proxy_port_number is None: - proxy_port_number = str(self.GetUnusedPort(_binding_address)) - _device_params.target_params.append('--proxy=%s:%s' % (_binding_address, - proxy_port_number)) + # Setup global variables used by test cases + global _device_params + _device_params = cobalt_runner.GetDeviceParamsFromCommandLine() + # Keep other modules from seeing these args + sys.argv = sys.argv[:1] + global _binding_address + _binding_address = server_binding_address + # Port used to create the web platform test http server. If not specified, + # a random free port is used. + if wpt_http_port is None: + wpt_http_port = str(self.GetUnusedPort([server_binding_address])) + global _wpt_http_port + _wpt_http_port = wpt_http_port + _device_params.target_params.append( + '--web-platform-test-server=http://web-platform.test:%s' % + wpt_http_port) + # Port used to create the proxy server. If not specified, a random free + # port is used. + if proxy_port is None: + proxy_port = str(self.GetUnusedPort([server_binding_address])) + if proxy_address is None: + proxy_address = server_binding_address + _device_params.target_params.append('--proxy=%s:%s' % + (proxy_address, proxy_port)) + + self.proxy_port = proxy_port self.test_name = test_name - self.proxy_port_number = proxy_port_number + self.device_ips = device_ips # Test domains used in web platform tests to be resolved to the server # binding address. @@ -158,15 +179,16 @@ 'xn--n8j6ds53lwwkrqhv28a.web-platform.test', 'xn--lve-6lad.web-platform.test' ] - self.host_resolve_map = dict([(host, _binding_address) for host in hosts]) + self.host_resolve_map = dict([(host, server_binding_address) for host in hosts]) def Run(self): - if self.proxy_port_number == '-1': + if self.proxy_port == '-1': return 1 - logging.info('Using proxy port number: %s', self.proxy_port_number) + logging.info('Using proxy port: %s', self.proxy_port) - with ProxyServer(port=self.proxy_port_number, - host_resolve_map=self.host_resolve_map): + with ProxyServer( + port=self.proxy_port, host_resolve_map=self.host_resolve_map, + client_ips=self.device_ips): if self.test_name: suite = unittest.TestLoader().loadTestsFromModule( importlib.import_module(_TEST_DIR_PATH + self.test_name)) @@ -178,42 +200,77 @@ verbosity=0, stream=sys.stdout).run(suite).wasSuccessful() return return_code - def GetUnusedPort(self, machine_address): - """Find a free port on the machine address by pinging with socket.""" - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + def GetUnusedPort(self, addresses): + """Find a free port on the list of addresses by pinging with sockets.""" + SOCKET_SUCCESS = 0 + + if not addresses: + logging.error('Can not find unused port on invalid addresses.') + return -1 + + socks = [] + for address in addresses: + socks.append((address, socket.socket(socket.AF_INET, socket.SOCK_STREAM))) try: - for i in range(1, _PORT_SELECTION_RETRY_LIMIT): - port_number = random.randint(_PORT_SELECTION_RANGE[0], - _PORT_SELECTION_RANGE[1]) - result = sock.connect_ex((machine_address, port_number)) - if result != 0: - return port_number - if i == _PORT_SELECTION_RETRY_LIMIT - 1: - logging.error( - 'Can not find unused port on target machine within %s attempts.', - _PORT_SELECTION_RETRY_LIMIT) - return -1 + for _ in range(_PORT_SELECTION_RETRY_LIMIT): + port = random.randint(_PORT_SELECTION_RANGE[0], _PORT_SELECTION_RANGE[1]) + unused = True + for sock in socks: + result = sock[1].connect_ex((sock[0], port)) + if result == SOCKET_SUCCESS: + unused = False + break + if unused: + return port + logging.error( + 'Can not find unused port on addresses within %s attempts.' % + _PORT_SELECTION_RETRY_LIMIT) + return -1 finally: - sock.close() + for sock in socks: + sock[1].close() def main(): parser = argparse.ArgumentParser() - parser.add_argument('--test_name', - help=('Name of test to be run. If not specified, all ' - 'tests are run.')) - parser.add_argument('--server_binding_address', - default='127.0.0.1', - help='Binding address used to create the test server.') - parser.add_argument('--proxy_port_number', - help=('Port number used to create the proxy http server' - 'that all black box tests are run through. If not' - 'specified, a random free port is used.')) + parser.add_argument( + '--server_binding_address', + default='127.0.0.1', + help='Binding address used to create the test server.') + parser.add_argument( + '--proxy_address', + default=None, + help=('Address to the proxy server that all black box' + 'tests are run through. If not specified, the' + 'server binding address is used.')) + parser.add_argument( + '--proxy_port', + default=None, + help=('Port used to create the proxy server that all' + 'black box tests are run through. If not' + 'specified, a random free port is used.')) + parser.add_argument( + '--test_name', + default=None, + help=('Name of test to be run. If not specified, all ' + 'tests are run.')) + parser.add_argument( + '--wpt_http_port', + default=None, + help=('Port used to create the web platform test http' + 'server. If not specified, a random free port is' + 'used.')) + parser.add_argument( + '--device_ips', + default=None, + nargs='*', + help=('IPs of test devices that will be allowed to connect. If not' + 'specified, all IPs will be allowed to connect.')) args, _ = parser.parse_known_args() - global _binding_address - _binding_address = args.server_binding_address - test_object = BlackBoxTests(args.test_name, args.proxy_port_number) + test_object = BlackBoxTests(args.server_binding_address, args.proxy_address, + args.proxy_port, args.test_name, + args.wpt_http_port, args.device_ips) sys.exit(test_object.Run())
diff --git a/src/cobalt/black_box_tests/proxy_server.py b/src/cobalt/black_box_tests/proxy_server.py index ee29a39..5b51830 100644 --- a/src/cobalt/black_box_tests/proxy_server.py +++ b/src/cobalt/black_box_tests/proxy_server.py
@@ -24,15 +24,16 @@ import os import signal import subprocess -import sys -SRC_DIR = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir) import tempfile import time +SRC_DIR = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir) + class ProxyServer(object): - def __init__(self, hostname='0.0.0.0', port='8000', host_resolve_map=None): + def __init__(self, hostname='0.0.0.0', port='8000', host_resolve_map=None, + client_ips=None): self.command = [ 'python', os.path.join(SRC_DIR, 'third_party', 'proxy_py', 'proxy.py'), @@ -42,10 +43,14 @@ self.host_resolver_path = None if host_resolve_map: - with tempfile.NamedTemporaryFile(delete=False) as hosts: - json.dump(host_resolve_map, hosts) - self.host_resolver_path = hosts.name - self.command += ['--host_resolver', self.host_resolver_path] + with tempfile.NamedTemporaryFile(delete=False) as hosts: + json.dump(host_resolve_map, hosts) + self.host_resolver_path = hosts.name + self.command += ['--host_resolver', self.host_resolver_path] + + if client_ips: + self.command += ['--client_ips'] + self.command += client_ips def __enter__(self): self.proc = subprocess.Popen(self.command) @@ -55,4 +60,4 @@ def __exit__(self, exc_type, exc_value, traceback): self.proc.send_signal(signal.SIGINT) if self.host_resolver_path: - os.unlink(self.host_resolver_path) + os.unlink(self.host_resolver_path)
diff --git a/src/cobalt/black_box_tests/tests/web_platform_tests.py b/src/cobalt/black_box_tests/tests/web_platform_tests.py index 12adcd0..2355b59 100644 --- a/src/cobalt/black_box_tests/tests/web_platform_tests.py +++ b/src/cobalt/black_box_tests/tests/web_platform_tests.py
@@ -33,7 +33,8 @@ self.skipTest('Can only run web platform tests on debug or devel config.') def test_simple(self): - with WebPlatformTestServer(binding_address=self.GetBindingAddress()): + with WebPlatformTestServer(binding_address=self.GetBindingAddress(), + wpt_http_port=self.GetWptHttpPort()): target_params = [] filters = self.cobalt_config.GetWebPlatformTestFilters()
diff --git a/src/cobalt/black_box_tests/web_platform_test_server.py b/src/cobalt/black_box_tests/web_platform_test_server.py index 5199151..d3a9d96 100644 --- a/src/cobalt/black_box_tests/web_platform_test_server.py +++ b/src/cobalt/black_box_tests/web_platform_test_server.py
@@ -31,12 +31,16 @@ class WebPlatformTestServer(object): """Runs a WPT StashServer on its own thread in a Python context manager.""" - def __init__(self, binding_address=None): + def __init__(self, binding_address=None, wpt_http_port=None): # IP config['host'] should map to either through a dns or the hosts file. if binding_address: self._binding_address = binding_address else: self._binding_address = '127.0.0.1' + if wpt_http_port: + self._wpt_http_port = wpt_http_port + else: + self._wpt_http_port = '8000' def main(self): kwargs = vars(serve.get_parser().parse_args()) @@ -45,6 +49,7 @@ config = serve.load_config(os.path.join(WPT_DIR, 'config.default.json'), os.path.join(WPT_DIR, 'config.json'), **kwargs) + config['ports']['http'][0] = int(self._wpt_http_port) serve.setup_logger(config['log_level'])
diff --git a/src/cobalt/browser/trace_manager.h b/src/cobalt/browser/trace_manager.h deleted file mode 100644 index b774f11..0000000 --- a/src/cobalt/browser/trace_manager.h +++ /dev/null
@@ -1,78 +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 COBALT_BROWSER_TRACE_MANAGER_H_ -#define COBALT_BROWSER_TRACE_MANAGER_H_ - -#include <map> -#include <string> - -#include "base/threading/thread_checker.h" -#include "cobalt/debug/console/command_manager.h" -#include "cobalt/trace_event/event_parser.h" -#include "cobalt/trace_event/scoped_trace_to_file.h" - -namespace cobalt { -namespace browser { - -// Wrapper class which wraps all trace related stuff. -class TraceManager { - public: - // Returns whether there's a trace that is active. - static bool IsTracing(); - - TraceManager(); - - // Called by browser module when an input event is produced. - void OnInputEventProduced(); - - // Message handler callback for trace message from debug console. - void OnTraceMessage(const std::string& message); - - // Message handler callback for input trace message from debug console. - void OnInputTraceMessage(const std::string& message); - - // Called when receiving and finishing receiving parsed trace events. - void OnReceiveTraceEvent( - const scoped_refptr<trace_event::EventParser::ScopedEvent>& event); - void OnFinishReceiveTraceEvent(); - - private: - typedef std::multimap<int64, - scoped_refptr<trace_event::EventParser::ScopedEvent> > - StartTimeToEventMap; - - // The message loop on which we'll do all our work. - MessageLoop* const self_message_loop_; - - // Command handler object for trace command from the debug console. - debug::console::ConsoleCommandManager::CommandHandler trace_command_handler_; - // Command handler object for input trace command from the debug console. - debug::console::ConsoleCommandManager::CommandHandler - input_trace_command_handler_; - // Whether input tracing is enabled. - bool input_tracing_enabled_; - - THREAD_CHECKER(thread_checker_); - // This object can be set to start a trace if a hotkey (like F3) is pressed. - // While initialized, it means that a trace is on-going. - scoped_ptr<trace_event::ScopedTraceToFile> trace_to_file_; - // Record of a list of events we're interested in, ordered by starting time. - StartTimeToEventMap start_time_to_event_map_; -}; - -} // namespace browser -} // namespace cobalt - -#endif // COBALT_BROWSER_TRACE_MANAGER_H_
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index e912fc3..164d0dd 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -244012 \ No newline at end of file +266393 \ No newline at end of file
diff --git a/src/cobalt/content/ssl/certs/5c44d531.0 b/src/cobalt/content/ssl/certs/5c44d531.0 deleted file mode 100644 index cedf596..0000000 --- a/src/cobalt/content/ssl/certs/5c44d531.0 +++ /dev/null
@@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX -DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl -ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv -b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 -qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp -uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU -Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE -pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp -5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M -UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN -GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy -5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv -6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK -eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 -B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ -BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov -L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV -HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG -SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS -CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen -5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 -IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK -gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL -+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL -vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm -bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk -N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC -Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z -ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== ------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/5e98733a.0 b/src/cobalt/content/ssl/certs/5e98733a.0 new file mode 100644 index 0000000..7ac10cc --- /dev/null +++ b/src/cobalt/content/ssl/certs/5e98733a.0
@@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw +gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL +Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg +MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw +BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 +MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 +c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ +bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ +2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E +T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j +5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM +C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T +DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX +wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A +2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm +nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl +N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj +c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS +5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS +Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr +hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/ +B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI +AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw +H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+ +b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk +2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol +IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk +5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY +n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/812e17de.0 b/src/cobalt/content/ssl/certs/812e17de.0 deleted file mode 100644 index 05879ff..0000000 --- a/src/cobalt/content/ssl/certs/812e17de.0 +++ /dev/null
@@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc -MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj -IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB -IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE -RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl -U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 -IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU -ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC -QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr -rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S -NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc -QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH -txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP -BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC -AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp -tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa -IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl -6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ -xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU -Cm26OWMohpLzGITY+9HPBVZkVw== ------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/9f0f5fd6.0 b/src/cobalt/content/ssl/certs/9f0f5fd6.0 deleted file mode 100644 index 7f60447..0000000 --- a/src/cobalt/content/ssl/certs/9f0f5fd6.0 +++ /dev/null
@@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET -MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb -BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz -MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx -FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g -Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 -fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl -LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV -WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF -TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb -5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc -CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri -wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ -wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG -m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 -F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng -WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 -2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF -AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ -0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw -F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS -g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj -qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN -h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ -ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V -btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj -Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ -8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW -gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= ------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/f060240e.0 b/src/cobalt/content/ssl/certs/f060240e.0 deleted file mode 100644 index 6d0133d..0000000 --- a/src/cobalt/content/ssl/certs/f060240e.0 +++ /dev/null
@@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw -PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz -cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 -MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz -IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ -ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR -VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL -kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd -EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas -H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 -HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud -DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 -QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu -Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ -AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 -yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR -FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA -ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB -kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 -l7+ijrRU ------END CERTIFICATE-----
diff --git a/src/cobalt/cssom/combinator.h b/src/cobalt/cssom/combinator.h index 1e54192..d97fe80 100644 --- a/src/cobalt/cssom/combinator.h +++ b/src/cobalt/cssom/combinator.h
@@ -34,9 +34,12 @@ kDescendantCombinator, kNextSiblingCombinator, kFollowingSiblingCombinator, - kCombinatorCount, + kCombinatorTypeCount, }; +// Avoids the clang error: arithmetic between different enumeration types. +constexpr size_t kCombinatorCount = static_cast<size_t>(kCombinatorTypeCount); + // A combinator is punctuation that represents a particular kind of relationship // between the selectors on either side. // https://www.w3.org/TR/selectors4/#combinator
diff --git a/src/cobalt/debug/remote/devtools/front_end/accessibility/AXBreadcrumbsPane.js b/src/cobalt/debug/remote/devtools/front_end/accessibility/AXBreadcrumbsPane.js index 34708e0..9075c5e 100644 --- a/src/cobalt/debug/remote/devtools/front_end/accessibility/AXBreadcrumbsPane.js +++ b/src/cobalt/debug/remote/devtools/front_end/accessibility/AXBreadcrumbsPane.js
@@ -112,7 +112,7 @@ _onKeyDown(event) { if (!this._preselectedBreadcrumb) return; - if (!event.path.some(element => element === this._preselectedBreadcrumb.element())) + if (!event.composedPath().some(element => element === this._preselectedBreadcrumb.element())) return; if (event.shiftKey || event.metaKey || event.ctrlKey) return;
diff --git a/src/cobalt/debug/remote/devtools/front_end/accessibility/AccessibilityNodeView.js b/src/cobalt/debug/remote/devtools/front_end/accessibility/AccessibilityNodeView.js index f0c0a2e..9bb5329 100644 --- a/src/cobalt/debug/remote/devtools/front_end/accessibility/AccessibilityNodeView.js +++ b/src/cobalt/debug/remote/devtools/front_end/accessibility/AccessibilityNodeView.js
@@ -151,7 +151,7 @@ * @return {!Element} */ static createExclamationMark(tooltip) { - const exclamationElement = createElement('label', 'dt-icon-label'); + const exclamationElement = createElement('span', 'dt-icon-label'); exclamationElement.type = 'smallicon-warning'; exclamationElement.title = tooltip; return exclamationElement;
diff --git a/src/cobalt/debug/remote/devtools/front_end/accessibility/accessibilityNode.css b/src/cobalt/debug/remote/devtools/front_end/accessibility/accessibilityNode.css index a6e59ca..7b624a9 100644 --- a/src/cobalt/debug/remote/devtools/front_end/accessibility/accessibilityNode.css +++ b/src/cobalt/debug/remote/devtools/front_end/accessibility/accessibilityNode.css
@@ -47,7 +47,7 @@ padding-left: 4px; } -.tree-outline label[is=dt-icon-label] { +.tree-outline span[is=dt-icon-label] { position: relative; left: -11px; } @@ -74,7 +74,7 @@ left: -2px; } -.tree-outline label[is=dt-icon-label] + .ax-name { +.tree-outline span[is=dt-icon-label] + .ax-name { margin-left: -11px; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/changes/changesView.css b/src/cobalt/debug/remote/devtools/front_end/changes/changesView.css index 8d3ad90..552e9ec 100644 --- a/src/cobalt/debug/remote/devtools/front_end/changes/changesView.css +++ b/src/cobalt/debug/remote/devtools/front_end/changes/changesView.css
@@ -3,14 +3,15 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -.insertion-point-main{ +[slot=insertion-point-main]{ flex-direction: column; display: flex; } -.insertion-point-sidebar { +[slot=insertion-point-sidebar] { overflow: auto; } + .editor-container{ flex: 1; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/console/ConsoleViewMessage.js b/src/cobalt/debug/remote/devtools/front_end/console/ConsoleViewMessage.js index fdc9790..ee0d012 100644 --- a/src/cobalt/debug/remote/devtools/front_end/console/ConsoleViewMessage.js +++ b/src/cobalt/debug/remote/devtools/front_end/console/ConsoleViewMessage.js
@@ -1179,7 +1179,7 @@ return; if (!this._repeatCountElement) { - this._repeatCountElement = createElementWithClass('label', 'console-message-repeat-count', 'dt-small-bubble'); + this._repeatCountElement = createElementWithClass('span', 'console-message-repeat-count', 'dt-small-bubble'); switch (this._message.level) { case SDK.ConsoleMessage.MessageLevel.Warning: this._repeatCountElement.type = 'warning';
diff --git a/src/cobalt/debug/remote/devtools/front_end/console_counters/WarningErrorCounter.js b/src/cobalt/debug/remote/devtools/front_end/console_counters/WarningErrorCounter.js index b51e2ef..b17e8ae 100644 --- a/src/cobalt/debug/remote/devtools/front_end/console_counters/WarningErrorCounter.js +++ b/src/cobalt/debug/remote/devtools/front_end/console_counters/WarningErrorCounter.js
@@ -39,7 +39,7 @@ */ _createItem(shadowRoot, iconType) { const item = createElementWithClass('span', 'counter-item'); - const icon = item.createChild('label', '', 'dt-icon-label'); + const icon = item.createChild('span', '', 'dt-icon-label'); icon.type = iconType; const text = icon.createChild('span'); shadowRoot.appendChild(item);
diff --git a/src/cobalt/debug/remote/devtools/front_end/console_counters/errorWarningCounter.css b/src/cobalt/debug/remote/devtools/front_end/console_counters/errorWarningCounter.css index d6ad447..ee6de7a 100644 --- a/src/cobalt/debug/remote/devtools/front_end/console_counters/errorWarningCounter.css +++ b/src/cobalt/debug/remote/devtools/front_end/console_counters/errorWarningCounter.css
@@ -18,10 +18,6 @@ margin-left: 6px; } -.counter-item label { - cursor: inherit; -} - .counter-item.counter-item-first { margin-left: 0; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/dom_extension/DOMExtension.js b/src/cobalt/debug/remote/devtools/front_end/dom_extension/DOMExtension.js index baa2879..ae9eb89 100644 --- a/src/cobalt/debug/remote/devtools/front_end/dom_extension/DOMExtension.js +++ b/src/cobalt/debug/remote/devtools/front_end/dom_extension/DOMExtension.js
@@ -270,10 +270,12 @@ */ Node.prototype.hasSelection = function() { // TODO(luoe): use contains(node, {includeShadow: true}) when it is fixed for shadow dom. - const contents = this.querySelectorAll('content'); - for (const content of contents) { - if (Array.prototype.some.call(content.getDistributedNodes(), node => node.hasSelection())) - return true; + if (this instanceof Element) { + const slots = this.querySelectorAll('slot'); + for (const slot of slots) { + if (Array.prototype.some.call(slot.assignedNodes(), node => node.hasSelection())) + return true; + } } const selection = this.getComponentSelection(); @@ -299,10 +301,11 @@ * @param {string} tagName * @param {string=} customElementType * @return {!Element} + * @suppress {checkTypes} * @suppressGlobalPropertiesCheck */ function createElement(tagName, customElementType) { - return document.createElement(tagName, customElementType || ''); + return document.createElement(tagName, {is: customElementType}); } /** @@ -318,10 +321,11 @@ * @param {string} elementName * @param {string=} className * @param {string=} customElementType + * @suppress {checkTypes} * @return {!Element} */ Document.prototype.createElementWithClass = function(elementName, className, customElementType) { - const element = this.createElement(elementName, customElementType || ''); + const element = this.createElement(elementName, {is: customElementType}); if (className) element.className = className; return element; @@ -651,7 +655,7 @@ if (this.shadowRoot) return this.shadowRoot; - const distributedNodes = this.getDistributedNodes ? this.getDistributedNodes() : []; + const distributedNodes = this instanceof HTMLSlotElement ? this.assignedNodes() : []; if (distributedNodes.length) return distributedNodes[0]; @@ -668,7 +672,7 @@ if (sibling) return sibling; - node = insertionPoint(node) || node.parentNodeOrShadowHost(); + node = node.assignedSlot || node.parentNodeOrShadowHost(); } /** @@ -676,10 +680,9 @@ * @return {?Node} */ function nextSibling(node) { - const parent = insertionPoint(node); - if (!parent) + if (!node.assignedSlot) return node.nextSibling; - const distributedNodes = parent.getDistributedNodes ? parent.getDistributedNodes() : []; + const distributedNodes = node.assignedSlot.assignedNodes(); const position = Array.prototype.indexOf.call(distributedNodes, node); if (position + 1 < distributedNodes.length) @@ -687,15 +690,6 @@ return null; } - /** - * @param {!Node} node - * @return {?Node} - */ - function insertionPoint(node) { - const insertionPoints = node.getDestinationInsertionPoints ? node.getDestinationInsertionPoints() : []; - return insertionPoints.length > 0 ? insertionPoints[insertionPoints.length - 1] : null; - } - return null; };
diff --git a/src/cobalt/debug/remote/devtools/front_end/elements/ElementsSidebarPane.js b/src/cobalt/debug/remote/devtools/front_end/elements/ElementsSidebarPane.js index 5676b09..1f76f3d 100644 --- a/src/cobalt/debug/remote/devtools/front_end/elements/ElementsSidebarPane.js +++ b/src/cobalt/debug/remote/devtools/front_end/elements/ElementsSidebarPane.js
@@ -5,8 +5,11 @@ * @unrestricted */ Elements.ElementsSidebarPane = class extends UI.VBox { - constructor() { - super(true); + /** + * @param {boolean=} delegatesFocus + */ + constructor(delegatesFocus) { + super(true, delegatesFocus); this.element.classList.add('flex-none'); this._computedStyleModel = new Elements.ComputedStyleModel(); this._computedStyleModel.addEventListener(
diff --git a/src/cobalt/debug/remote/devtools/front_end/elements/StylesSidebarPane.js b/src/cobalt/debug/remote/devtools/front_end/elements/StylesSidebarPane.js index 2845ba4..7a10535 100644 --- a/src/cobalt/debug/remote/devtools/front_end/elements/StylesSidebarPane.js +++ b/src/cobalt/debug/remote/devtools/front_end/elements/StylesSidebarPane.js
@@ -29,10 +29,9 @@ Elements.StylesSidebarPane = class extends Elements.ElementsSidebarPane { constructor() { - super(); + super(true /* delegatesFocus */); this.setMinimumSize(96, 26); this.registerRequiredCSS('elements/stylesSidebarPane.css'); - this.element.tabIndex = -1; Common.moduleSetting('colorFormat').addChangeListener(this.update.bind(this)); Common.moduleSetting('textEditorIndent').addChangeListener(this.update.bind(this)); @@ -94,7 +93,7 @@ * @return {!Element} */ static createExclamationMark(property) { - const exclamationElement = createElement('label', 'dt-icon-label'); + const exclamationElement = createElement('span', 'dt-icon-label'); exclamationElement.className = 'exclamation-mark'; if (!Elements.StylesSidebarPane.ignoreErrorsForProperty(property)) exclamationElement.type = 'smallicon-warning';
diff --git a/src/cobalt/debug/remote/devtools/front_end/elements/classesPaneWidget.css b/src/cobalt/debug/remote/devtools/front_end/elements/classesPaneWidget.css index ee810fe..01aec13 100644 --- a/src/cobalt/debug/remote/devtools/front_end/elements/classesPaneWidget.css +++ b/src/cobalt/debug/remote/devtools/front_end/elements/classesPaneWidget.css
@@ -16,7 +16,7 @@ justify-content: flex-start; } -.styles-element-classes-pane label { +.styles-element-classes-pane [is=dt-checkbox] { margin-right: 15px; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/emulation/DeviceModeView.js b/src/cobalt/debug/remote/devtools/front_end/emulation/DeviceModeView.js index e77cf26..144a61b 100644 --- a/src/cobalt/debug/remote/devtools/front_end/emulation/DeviceModeView.js +++ b/src/cobalt/debug/remote/devtools/front_end/emulation/DeviceModeView.js
@@ -74,7 +74,7 @@ this._bottomResizerElement.title = Common.UIString('Double-click for full height'); this._pageArea = this._screenArea.createChild('div', 'device-mode-page-area'); - this._pageArea.createChild('content'); + this._pageArea.createChild('slot'); } _populatePresetsContainer() {
diff --git a/src/cobalt/debug/remote/devtools/front_end/emulation/sensors.css b/src/cobalt/debug/remote/devtools/front_end/emulation/sensors.css index 03c1311..6960e86 100644 --- a/src/cobalt/debug/remote/devtools/front_end/emulation/sensors.css +++ b/src/cobalt/debug/remote/devtools/front_end/emulation/sensors.css
@@ -9,10 +9,6 @@ display: block; } -.sensors-view label { - margin-bottom: 10px; -} - .sensors-view input { width: 100%; max-width: 100px;
diff --git a/src/cobalt/debug/remote/devtools/front_end/inline_editor/CSSShadowEditor.js b/src/cobalt/debug/remote/devtools/front_end/inline_editor/CSSShadowEditor.js index ddaa9f8..83b8b1a 100644 --- a/src/cobalt/debug/remote/devtools/front_end/inline_editor/CSSShadowEditor.js +++ b/src/cobalt/debug/remote/devtools/front_end/inline_editor/CSSShadowEditor.js
@@ -68,7 +68,7 @@ * @return {!Element} */ _createSlider(field) { - const slider = UI.createSliderLabel(0, InlineEditor.CSSShadowEditor.maxRange, -1); + const slider = UI.createSlider(0, InlineEditor.CSSShadowEditor.maxRange, -1); slider.addEventListener('input', this._onSliderInput.bind(this), false); field.appendChild(slider); return slider;
diff --git a/src/cobalt/debug/remote/devtools/front_end/inline_editor/ColorSwatch.js b/src/cobalt/debug/remote/devtools/front_end/inline_editor/ColorSwatch.js index 5f5eb0b..2dfc68a 100644 --- a/src/cobalt/debug/remote/devtools/front_end/inline_editor/ColorSwatch.js +++ b/src/cobalt/debug/remote/devtools/front_end/inline_editor/ColorSwatch.js
@@ -7,6 +7,17 @@ InlineEditor.ColorSwatch = class extends HTMLSpanElement { constructor() { super(); + const root = UI.createShadowRootWithCoreStyles(this, 'inline_editor/colorSwatch.css'); + + this._iconElement = root.createChild('span', 'color-swatch'); + this._iconElement.title = Common.UIString('Shift-click to change color format'); + this._swatchInner = this._iconElement.createChild('span', 'color-swatch-inner'); + this._swatchInner.addEventListener('dblclick', e => e.consume(), false); + this._swatchInner.addEventListener('mousedown', e => e.consume(), false); + this._swatchInner.addEventListener('click', this._handleClick.bind(this), true); + + root.createChild('slot'); + this._colorValueElement = this.createChild('span'); } /** @@ -15,11 +26,11 @@ static create() { if (!InlineEditor.ColorSwatch._constructor) { InlineEditor.ColorSwatch._constructor = - UI.registerCustomElement('span', 'color-swatch', InlineEditor.ColorSwatch.prototype); + UI.registerCustomElement('span', 'color-swatch', InlineEditor.ColorSwatch); } - return /** @type {!InlineEditor.ColorSwatch} */ (new InlineEditor.ColorSwatch._constructor()); + return /** @type {!InlineEditor.ColorSwatch} */ (InlineEditor.ColorSwatch._constructor()); } /** @@ -134,23 +145,6 @@ } /** - * @override - */ - createdCallback() { - const root = UI.createShadowRootWithCoreStyles(this, 'inline_editor/colorSwatch.css'); - - this._iconElement = root.createChild('span', 'color-swatch'); - this._iconElement.title = Common.UIString('Shift-click to change color format'); - this._swatchInner = this._iconElement.createChild('span', 'color-swatch-inner'); - this._swatchInner.addEventListener('dblclick', e => e.consume(), false); - this._swatchInner.addEventListener('mousedown', e => e.consume(), false); - this._swatchInner.addEventListener('click', this._handleClick.bind(this), true); - - root.createChild('content'); - this._colorValueElement = this.createChild('span'); - } - - /** * @param {!Event} event */ _handleClick(event) { @@ -168,6 +162,11 @@ InlineEditor.BezierSwatch = class extends HTMLSpanElement { constructor() { super(); + const root = UI.createShadowRootWithCoreStyles(this, 'inline_editor/bezierSwatch.css'); + this._iconElement = UI.Icon.create('smallicon-bezier', 'bezier-swatch-icon'); + root.appendChild(this._iconElement); + this._textElement = this.createChild('span'); + root.createChild('slot'); } /** @@ -176,11 +175,11 @@ static create() { if (!InlineEditor.BezierSwatch._constructor) { InlineEditor.BezierSwatch._constructor = - UI.registerCustomElement('span', 'bezier-swatch', InlineEditor.BezierSwatch.prototype); + UI.registerCustomElement('span', 'bezier-swatch', InlineEditor.BezierSwatch); } - return /** @type {!InlineEditor.BezierSwatch} */ (new InlineEditor.BezierSwatch._constructor()); + return /** @type {!InlineEditor.BezierSwatch} */ (InlineEditor.BezierSwatch._constructor()); } /** @@ -210,17 +209,6 @@ iconElement() { return this._iconElement; } - - /** - * @override - */ - createdCallback() { - const root = UI.createShadowRootWithCoreStyles(this, 'inline_editor/bezierSwatch.css'); - this._iconElement = UI.Icon.create('smallicon-bezier', 'bezier-swatch-icon'); - root.appendChild(this._iconElement); - this._textElement = this.createChild('span'); - root.createChild('content'); - } }; /** @@ -229,6 +217,11 @@ InlineEditor.CSSShadowSwatch = class extends HTMLSpanElement { constructor() { super(); + const root = UI.createShadowRootWithCoreStyles(this, 'inline_editor/cssShadowSwatch.css'); + this._iconElement = UI.Icon.create('smallicon-shadow', 'shadow-swatch-icon'); + root.appendChild(this._iconElement); + root.createChild('slot'); + this._contentElement = this.createChild('span'); } /** @@ -237,10 +230,10 @@ static create() { if (!InlineEditor.CSSShadowSwatch._constructor) { InlineEditor.CSSShadowSwatch._constructor = - UI.registerCustomElement('span', 'css-shadow-swatch', InlineEditor.CSSShadowSwatch.prototype); + UI.registerCustomElement('span', 'css-shadow-swatch', InlineEditor.CSSShadowSwatch); } - return /** @type {!InlineEditor.CSSShadowSwatch} */ (new InlineEditor.CSSShadowSwatch._constructor()); + return /** @type {!InlineEditor.CSSShadowSwatch} */ (InlineEditor.CSSShadowSwatch._constructor()); } /** @@ -290,15 +283,4 @@ colorSwatch() { return this._colorSwatch; } - - /** - * @override - */ - createdCallback() { - const root = UI.createShadowRootWithCoreStyles(this, 'inline_editor/cssShadowSwatch.css'); - this._iconElement = UI.Icon.create('smallicon-shadow', 'shadow-swatch-icon'); - root.appendChild(this._iconElement); - root.createChild('content'); - this._contentElement = this.createChild('span'); - } };
diff --git a/src/cobalt/debug/remote/devtools/front_end/inspector_main/renderingOptions.css b/src/cobalt/debug/remote/devtools/front_end/inspector_main/renderingOptions.css index 77eeb51..b478b44 100644 --- a/src/cobalt/debug/remote/devtools/front_end/inspector_main/renderingOptions.css +++ b/src/cobalt/debug/remote/devtools/front_end/inspector_main/renderingOptions.css
@@ -8,7 +8,7 @@ padding: 12px; } -label { +[is=dt-checkbox] { margin: 0px 0px 10px 0px; flex: none; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/layers_test_runner/LayersTestRunner.js b/src/cobalt/debug/remote/devtools/front_end/layers_test_runner/LayersTestRunner.js index d5c69d7..20e51c3 100644 --- a/src/cobalt/debug/remote/devtools/front_end/layers_test_runner/LayersTestRunner.js +++ b/src/cobalt/debug/remote/devtools/front_end/layers_test_runner/LayersTestRunner.js
@@ -106,7 +106,8 @@ screenY: totalOffset.top - element.scrollTop + offsetY, clientX: totalOffset.left + offsetX, clientY: totalOffset.top + offsetY, - button: button + button: button, + composed: true }; if (eventType === 'mouseout') {
diff --git a/src/cobalt/debug/remote/devtools/front_end/network/RequestHeadersView.js b/src/cobalt/debug/remote/devtools/front_end/network/RequestHeadersView.js index 5c5b4de..2d9ee3e 100644 --- a/src/cobalt/debug/remote/devtools/front_end/network/RequestHeadersView.js +++ b/src/cobalt/debug/remote/devtools/front_end/network/RequestHeadersView.js
@@ -379,7 +379,7 @@ statusCodeFragment.createChild('div', 'header-name').textContent = Common.UIString('Status Code') + ': '; statusCodeFragment.createChild('span', 'header-separator'); - const statusCodeImage = statusCodeFragment.createChild('label', 'resource-status-image', 'dt-icon-label'); + const statusCodeImage = statusCodeFragment.createChild('span', 'resource-status-image', 'dt-icon-label'); statusCodeImage.title = this._request.statusCode + ' ' + this._request.statusText; if (this._request.statusCode < 300 || this._request.statusCode === 304) @@ -440,7 +440,7 @@ if (provisionalHeaders) { const cautionText = Common.UIString('Provisional headers are shown'); const cautionFragment = createDocumentFragment(); - cautionFragment.createChild('label', '', 'dt-icon-label').type = 'smallicon-warning'; + cautionFragment.createChild('span', '', 'dt-icon-label').type = 'smallicon-warning'; cautionFragment.createChild('div', 'caution').textContent = cautionText; const cautionTreeElement = new UI.TreeElement(cautionFragment); cautionTreeElement.selectable = false;
diff --git a/src/cobalt/debug/remote/devtools/front_end/network/networkConfigView.css b/src/cobalt/debug/remote/devtools/front_end/network/networkConfigView.css index 5c9f81f..6377126 100644 --- a/src/cobalt/debug/remote/devtools/front_end/network/networkConfigView.css +++ b/src/cobalt/debug/remote/devtools/front_end/network/networkConfigView.css
@@ -57,11 +57,11 @@ line-height: 20px; } -.network-config-ua label[is="dt-radio"].checked > * { +.network-config-ua span[is="dt-radio"].checked > * { display: none } -.network-config-ua input:not(.dt-radio-button) { +.network-config-ua input { display: block; width: calc(100% - 20px); max-width: 250px;
diff --git a/src/cobalt/debug/remote/devtools/front_end/network/networkLogView.css b/src/cobalt/debug/remote/devtools/front_end/network/networkLogView.css index b7240de..bf39802 100644 --- a/src/cobalt/debug/remote/devtools/front_end/network/networkLogView.css +++ b/src/cobalt/debug/remote/devtools/front_end/network/networkLogView.css
@@ -44,7 +44,7 @@ overflow: hidden; } -.network-summary-bar label[is=dt-icon-label] { +.network-summary-bar span[is=dt-icon-label] { margin-right: 6px; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/object_ui/ObjectPropertiesSection.js b/src/cobalt/debug/remote/devtools/front_end/object_ui/ObjectPropertiesSection.js index 88a7f85..5cdc53f 100644 --- a/src/cobalt/debug/remote/devtools/front_end/object_ui/ObjectPropertiesSection.js +++ b/src/cobalt/debug/remote/devtools/front_end/object_ui/ObjectPropertiesSection.js
@@ -426,7 +426,7 @@ * @param {!Array.<!SDK.RemoteObjectProperty>=} extraProperties */ constructor(object, linkifier, emptyPlaceholder, ignoreHasOwnProperty, extraProperties) { - const contentElement = createElement('content'); + const contentElement = createElement('slot'); super(contentElement); this._object = object;
diff --git a/src/cobalt/debug/remote/devtools/front_end/profiler/profilesPanel.css b/src/cobalt/debug/remote/devtools/front_end/profiler/profilesPanel.css index eab0f1d..4a5384a 100644 --- a/src/cobalt/debug/remote/devtools/front_end/profiler/profilesPanel.css +++ b/src/cobalt/debug/remote/devtools/front_end/profiler/profilesPanel.css
@@ -128,7 +128,7 @@ margin: 0 0 5px 0; } -.profile-launcher-view-content label { +.profile-launcher-view-content [is=dt-radio] { font-size: 13px; } @@ -165,7 +165,7 @@ margin-left: 22px; } -.profile-launcher-view-content p label { +.profile-launcher-view-content p [is=dt-checkbox] { display: flex; } @@ -190,7 +190,7 @@ to { background-color: rgba(255, 255, 120, 0); } } -.profile-canvas-decoration label[is=dt-icon-label] { +.profile-canvas-decoration span[is=dt-icon-label] { margin-right: 4px; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/resources/ApplicationCacheItemsView.js b/src/cobalt/debug/remote/devtools/front_end/resources/ApplicationCacheItemsView.js index 6c2373b..4f9dcfd 100644 --- a/src/cobalt/debug/remote/devtools/front_end/resources/ApplicationCacheItemsView.js +++ b/src/cobalt/debug/remote/devtools/front_end/resources/ApplicationCacheItemsView.js
@@ -38,9 +38,9 @@ this._deleteButton.setVisible(false); this._deleteButton.addEventListener(UI.ToolbarButton.Events.Click, this._deleteButtonClicked, this); - this._connectivityIcon = createElement('label', 'dt-icon-label'); + this._connectivityIcon = createElement('span', 'dt-icon-label'); this._connectivityIcon.style.margin = '0 2px 0 5px'; - this._statusIcon = createElement('label', 'dt-icon-label'); + this._statusIcon = createElement('span', 'dt-icon-label'); this._statusIcon.style.margin = '0 2px 0 5px'; this._frameId = frameId;
diff --git a/src/cobalt/debug/remote/devtools/front_end/security/SecurityPanel.js b/src/cobalt/debug/remote/devtools/front_end/security/SecurityPanel.js index 44f890f..693249d 100644 --- a/src/cobalt/debug/remote/devtools/front_end/security/SecurityPanel.js +++ b/src/cobalt/debug/remote/devtools/front_end/security/SecurityPanel.js
@@ -78,7 +78,7 @@ return text; } - const highlightedUrl = createElement('span', 'url-text'); + const highlightedUrl = createElement('span'); const scheme = url.substr(0, index); const content = url.substr(index + schemeSeparator.length);
diff --git a/src/cobalt/debug/remote/devtools/front_end/settings/settingsScreen.css b/src/cobalt/debug/remote/devtools/front_end/settings/settingsScreen.css index 47c80a7..9989041 100644 --- a/src/cobalt/debug/remote/devtools/front_end/settings/settingsScreen.css +++ b/src/cobalt/debug/remote/devtools/front_end/settings/settingsScreen.css
@@ -211,15 +211,11 @@ margin-left: 10px; } -.settings-indent-labels label { - padding-left: 10px; -} - .settings-experiment-hidden { display: none; } -.settings-experiment-hidden label { +.settings-experiment-hidden [is=dt-checkbox] { background-color: #ddd; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/sources/UISourceCodeFrame.js b/src/cobalt/debug/remote/devtools/front_end/sources/UISourceCodeFrame.js index 47de620..27ee88e 100644 --- a/src/cobalt/debug/remote/devtools/front_end/sources/UISourceCodeFrame.js +++ b/src/cobalt/debug/remote/devtools/front_end/sources/UISourceCodeFrame.js
@@ -582,7 +582,7 @@ this._icon = this.element.createChild('label', '', 'dt-icon-label'); this._icon.type = Sources.UISourceCodeFrame._iconClassPerLevel[message.level()]; this._repeatCountElement = - this.element.createChild('label', 'text-editor-row-message-repeat-count hidden', 'dt-small-bubble'); + this.element.createChild('span', 'text-editor-row-message-repeat-count hidden', 'dt-small-bubble'); this._repeatCountElement.type = Sources.UISourceCodeFrame._bubbleTypePerLevel[message.level()]; const linesContainer = this.element.createChild('div'); const lines = this._message.text().split('\n'); @@ -634,7 +634,7 @@ this._decoration = createElementWithClass('div', 'text-editor-line-decoration'); this._decoration._messageBucket = this; this._wave = this._decoration.createChild('div', 'text-editor-line-decoration-wave'); - this._icon = this._wave.createChild('label', 'text-editor-line-decoration-icon', 'dt-icon-label'); + this._icon = this._wave.createChild('span', 'text-editor-line-decoration-icon', 'dt-icon-label'); /** @type {?number} */ this._decorationStartColumn = null;
diff --git a/src/cobalt/debug/remote/devtools/front_end/sources/sourcesPanel.css b/src/cobalt/debug/remote/devtools/front_end/sources/sourcesPanel.css index 4b49787..17d4d4a 100644 --- a/src/cobalt/debug/remote/devtools/front_end/sources/sourcesPanel.css +++ b/src/cobalt/debug/remote/devtools/front_end/sources/sourcesPanel.css
@@ -50,7 +50,7 @@ margin-top: 0; } -.scripts-debug-toolbar-drawer > label { +.scripts-debug-toolbar-drawer > [is=dt-checkbox] { display: flex; padding-left: 3px; height: 28px;
diff --git a/src/cobalt/debug/remote/devtools/front_end/timeline/TimelineHistoryManager.js b/src/cobalt/debug/remote/devtools/front_end/timeline/TimelineHistoryManager.js index 7bd3fc0..ecd5308 100644 --- a/src/cobalt/debug/remote/devtools/front_end/timeline/TimelineHistoryManager.js +++ b/src/cobalt/debug/remote/devtools/front_end/timeline/TimelineHistoryManager.js
@@ -426,12 +426,11 @@ * @param {!UI.Action} action */ constructor(action) { - super(createElementWithClass('button', 'dropdown-button')); - const shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'timeline/historyToolbarButton.css'); - - this._contentElement = shadowRoot.createChild('span', 'content'); + super(createElementWithClass('button', 'history-dropdown-button')); + UI.appendStyle(this.element, 'timeline/historyToolbarButton.css'); + this._contentElement = this.element.createChild('span', 'content'); const dropdownArrowIcon = UI.Icon.create('smallicon-triangle-down'); - shadowRoot.appendChild(dropdownArrowIcon); + this.element.appendChild(dropdownArrowIcon); this.element.addEventListener('click', () => void action.execute(), false); this.setEnabled(action.enabled()); action.addEventListener(UI.Action.Events.Enabled, event => this.setEnabled(/** @type {boolean} */ (event.data)));
diff --git a/src/cobalt/debug/remote/devtools/front_end/timeline/historyToolbarButton.css b/src/cobalt/debug/remote/devtools/front_end/timeline/historyToolbarButton.css index f66188b..0259abf 100644 --- a/src/cobalt/debug/remote/devtools/front_end/timeline/historyToolbarButton.css +++ b/src/cobalt/debug/remote/devtools/front_end/timeline/historyToolbarButton.css
@@ -4,18 +4,18 @@ * found in the LICENSE file. */ -:host { +.history-dropdown-button { width: 160px; height: 26px; text-align: left; display: flex; } -:host([disabled]) { +.history-dropdown-button[disabled] { opacity: .5; } -.content { +.history-dropdown-button > .content { padding-right: 5px; overflow: hidden; text-overflow: ellipsis;
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/HistoryInput.js b/src/cobalt/debug/remote/devtools/front_end/ui/HistoryInput.js index b5cefa7..81784fc 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/HistoryInput.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/HistoryInput.js
@@ -5,24 +5,21 @@ * @unrestricted */ UI.HistoryInput = class extends HTMLInputElement { + constructor() { + super(); + this._history = ['']; + this._historyPosition = 0; + this.addEventListener('keydown', this._onKeyDown.bind(this), false); + this.addEventListener('input', this._onInput.bind(this), false); + } /** * @return {!UI.HistoryInput} */ static create() { if (!UI.HistoryInput._constructor) - UI.HistoryInput._constructor = UI.registerCustomElement('input', 'history-input', UI.HistoryInput.prototype); + UI.HistoryInput._constructor = UI.registerCustomElement('input', 'history-input', UI.HistoryInput); - return /** @type {!UI.HistoryInput} */ (new UI.HistoryInput._constructor()); - } - - /** - * @override - */ - createdCallback() { - this._history = ['']; - this._historyPosition = 0; - this.addEventListener('keydown', this._onKeyDown.bind(this), false); - this.addEventListener('input', this._onInput.bind(this), false); + return /** @type {!UI.HistoryInput} */ (UI.HistoryInput._constructor()); } /**
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/Icon.js b/src/cobalt/debug/remote/devtools/front_end/ui/Icon.js index 9ba57c8..2df6469 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/Icon.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/Icon.js
@@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** - * @constructor - * @extends {HTMLSpanElement} - */ UI.Icon = class extends HTMLSpanElement { constructor() { super(); - throw new Error('icon must be created via factory method.'); + /** @type {?UI.Icon.Descriptor} */ + this._descriptor = null; + /** @type {?UI.Icon.SpriteSheet} */ + this._spriteSheet = null; + /** @type {string} */ + this._iconType = ''; } /** @@ -19,9 +20,9 @@ */ static create(iconType, className) { if (!UI.Icon._constructor) - UI.Icon._constructor = UI.registerCustomElement('span', 'ui-icon', UI.Icon.prototype); + UI.Icon._constructor = UI.registerCustomElement('span', 'ui-icon', UI.Icon); - const icon = /** @type {!UI.Icon} */ (new UI.Icon._constructor()); + const icon = /** @type {!UI.Icon} */ (UI.Icon._constructor()); if (className) icon.className = className; if (iconType) @@ -30,18 +31,6 @@ } /** - * @override - */ - createdCallback() { - /** @type {?UI.Icon.Descriptor} */ - this._descriptor = null; - /** @type {?UI.Icon.SpriteSheet} */ - this._spriteSheet = null; - /** @type {string} */ - this._iconType = ''; - } - - /** * @param {string} iconType */ setIconType(iconType) {
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/ListWidget.js b/src/cobalt/debug/remote/devtools/front_end/ui/ListWidget.js index c11ae3a..0ba4777 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/ListWidget.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/ListWidget.js
@@ -9,12 +9,11 @@ * @param {!UI.ListWidget.Delegate<T>} delegate */ constructor(delegate) { - super(true); + super(true, true /* delegatesFocus */); this.registerRequiredCSS('ui/listWidget.css'); this._delegate = delegate; this._list = this.contentElement.createChild('div', 'list'); - this.element.tabIndex = -1; this._lastSeparator = false; /** @type {?UI.ElementFocusRestorer} */
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/SearchableView.js b/src/cobalt/debug/remote/devtools/front_end/ui/SearchableView.js index a0fa78f..f03af0f 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/SearchableView.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/SearchableView.js
@@ -46,7 +46,7 @@ this._setting = settingName ? Common.settings.createSetting(settingName, {}) : null; this._replaceable = false; - this.contentElement.createChild('content'); + this.contentElement.createChild('slot'); this._footerElementContainer = this.contentElement.createChild('div', 'search-bar hidden'); this._footerElementContainer.style.order = 100; this._footerElement = this._footerElementContainer.createChild('div', 'toolbar-search');
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/SoftDropDown.js b/src/cobalt/debug/remote/devtools/front_end/ui/SoftDropDown.js index 88e8e97..6d21c34 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/SoftDropDown.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/SoftDropDown.js
@@ -16,10 +16,10 @@ this._model = model; this.element = createElementWithClass('button', 'soft-dropdown'); - const shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'ui/softDropDownButton.css'); - this._titleElement = shadowRoot.createChild('span', 'title'); + UI.appendStyle(this.element, 'ui/softDropDownButton.css'); + this._titleElement = this.element.createChild('span', 'title'); const dropdownArrowIcon = UI.Icon.create('smallicon-triangle-down'); - shadowRoot.appendChild(dropdownArrowIcon); + this.element.appendChild(dropdownArrowIcon); this._glassPane = new UI.GlassPane(); this._glassPane.setMarginBehavior(UI.GlassPane.MarginBehavior.NoMargin);
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/SplitWidget.js b/src/cobalt/debug/remote/devtools/front_end/ui/SplitWidget.js index f0b55ed..950b230 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/SplitWidget.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/SplitWidget.js
@@ -46,10 +46,10 @@ this.contentElement.classList.add('shadow-split-widget'); this._mainElement = this.contentElement.createChild('div', 'shadow-split-widget-contents shadow-split-widget-main vbox'); - this._mainElement.createChild('content').select = '.insertion-point-main'; + this._mainElement.createChild('slot').name = 'insertion-point-main'; this._sidebarElement = this.contentElement.createChild('div', 'shadow-split-widget-contents shadow-split-widget-sidebar vbox'); - this._sidebarElement.createChild('content').select = '.insertion-point-sidebar'; + this._sidebarElement.createChild('slot').name = 'insertion-point-sidebar'; this._resizerElement = this.contentElement.createChild('div', 'shadow-split-widget-resizer'); this._resizerElementSize = null; @@ -165,8 +165,7 @@ this._mainWidget.detach(); this._mainWidget = widget; if (widget) { - widget.element.classList.add('insertion-point-main'); - widget.element.classList.remove('insertion-point-sidebar'); + widget.element.slot = 'insertion-point-main'; if (this._showMode === UI.SplitWidget.ShowMode.OnlyMain || this._showMode === UI.SplitWidget.ShowMode.Both) widget.show(this.element); } @@ -184,8 +183,7 @@ this._sidebarWidget.detach(); this._sidebarWidget = widget; if (widget) { - widget.element.classList.add('insertion-point-sidebar'); - widget.element.classList.remove('insertion-point-main'); + widget.element.slot = 'insertion-point-sidebar'; if (this._showMode === UI.SplitWidget.ShowMode.OnlySidebar || this._showMode === UI.SplitWidget.ShowMode.Both) widget.show(this.element); }
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/TabbedPane.js b/src/cobalt/debug/remote/devtools/front_end/ui/TabbedPane.js index 44db82d..dfad749 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/TabbedPane.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/TabbedPane.js
@@ -46,7 +46,7 @@ this._tabsElement.addEventListener('keydown', this._keyDown.bind(this), false); this._contentElement = this.contentElement.createChild('div', 'tabbed-pane-content'); this._contentElement.setAttribute('role', 'tabpanel'); - this._contentElement.createChild('content'); + this._contentElement.createChild('slot'); /** @type {!Array.<!UI.TabbedPaneTab>} */ this._tabs = []; /** @type {!Array.<!UI.TabbedPaneTab>} */
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/Toolbar.js b/src/cobalt/debug/remote/devtools/front_end/ui/Toolbar.js index 790a753..4f8d0bf 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/Toolbar.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/Toolbar.js
@@ -45,7 +45,7 @@ this._enabled = true; this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'ui/toolbar.css'); this._contentElement = this._shadowRoot.createChild('div', 'toolbar-shadow'); - this._insertionPoint = this._contentElement.createChild('content'); + this._insertionPoint = this._contentElement.createChild('slot'); } /** @@ -265,7 +265,7 @@ delete item._toolbar; this._items = []; this._contentElement.removeChildren(); - this._insertionPoint = this._contentElement.createChild('content'); + this._insertionPoint = this._contentElement.createChild('slot'); } /**
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/Tooltip.js b/src/cobalt/debug/remote/devtools/front_end/ui/Tooltip.js index a8034a0..253f22f 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/Tooltip.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/Tooltip.js
@@ -54,7 +54,7 @@ */ _mouseMove(event) { const mouseEvent = /** @type {!MouseEvent} */ (event); - const path = mouseEvent.path; + const path = mouseEvent.composedPath(); if (!path || mouseEvent.buttons !== 0 || (mouseEvent.movementX === 0 && mouseEvent.movementY === 0)) return; @@ -63,7 +63,8 @@ for (const element of path) { // The offsetParent is null when the element or an ancestor has 'display: none'. - if (element === this._anchorElement || (element.nodeName !== 'CONTENT' && element.offsetParent === null)) { + if (!(element instanceof Element) || element === this._anchorElement || + (element.nodeName !== 'SLOT' && element.offsetParent === null)) { return; } else if (element[UI.Tooltip._symbol]) { this._show(element, mouseEvent);
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/UIUtils.js b/src/cobalt/debug/remote/devtools/front_end/ui/UIUtils.js index 9c2761c..21e6412 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/UIUtils.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/UIUtils.js
@@ -674,9 +674,7 @@ * @param {!Element} element */ UI.installComponentRootStyles = function(element) { - UI.appendStyle(element, 'ui/inspectorCommon.css'); - UI.themeSupport.injectHighlightStyleSheets(element); - UI.themeSupport.injectCustomStyleSheets(element); + UI._injectCoreStyles(element); element.classList.add('platform-' + Host.platform()); // Detect overlay scrollbar enable by checking for nonzero scrollbar width. @@ -704,13 +702,12 @@ /** * @param {!Element} element * @param {string=} cssFile + * @param {boolean=} delegatesFocus * @return {!DocumentFragment} */ -UI.createShadowRootWithCoreStyles = function(element, cssFile) { - const shadowRoot = element.createShadowRoot(); - UI.appendStyle(shadowRoot, 'ui/inspectorCommon.css'); - UI.themeSupport.injectHighlightStyleSheets(shadowRoot); - UI.themeSupport.injectCustomStyleSheets(shadowRoot); +UI.createShadowRootWithCoreStyles = function(element, cssFile, delegatesFocus) { + const shadowRoot = element.attachShadow({mode: 'open', delegatesFocus}); + UI._injectCoreStyles(shadowRoot); if (cssFile) UI.appendStyle(shadowRoot, cssFile); shadowRoot.addEventListener('focus', UI._focusChanged.bind(UI), true); @@ -718,6 +715,16 @@ }; /** + * @param {!Element|!ShadowRoot} root + */ +UI._injectCoreStyles = function(root) { + UI.appendStyle(root, 'ui/inspectorCommon.css'); + UI.appendStyle(root, 'ui/textButton.css'); + UI.themeSupport.injectHighlightStyleSheets(root); + UI.themeSupport.injectCustomStyleSheets(root); +}; + +/** * @param {!Document} document * @param {!Event} event */ @@ -1166,13 +1173,19 @@ /** * @param {string} localName * @param {string} typeExtension - * @param {!Object} prototype + * @param {function(new:HTMLElement, *)} definition * @return {function()} * @suppressGlobalPropertiesCheck - * @template T */ -UI.registerCustomElement = function(localName, typeExtension, prototype) { - return document.registerElement(typeExtension, {prototype: Object.create(prototype), extends: localName}); +UI.registerCustomElement = function(localName, typeExtension, definition) { + self.customElements.define(typeExtension, class extends definition { + constructor() { + super(); + // TODO(einbinder) convert to classes and custom element tags + this.setAttribute('is', typeExtension); + } + }, {extends: localName}); + return () => createElement(localName, typeExtension); }; /** @@ -1183,12 +1196,14 @@ * @return {!Element} */ UI.createTextButton = function(text, clickHandler, className, primary) { - const element = createElementWithClass('button', className || '', 'text-button'); + const element = createElementWithClass('button', className || ''); element.textContent = text; + element.classList.add('text-button'); if (primary) element.classList.add('primary-button'); if (clickHandler) element.addEventListener('click', clickHandler, false); + element.type = 'button'; return element; }; @@ -1213,10 +1228,10 @@ * @return {!Element} */ UI.createRadioLabel = function(name, title, checked) { - const element = createElement('label', 'dt-radio'); + const element = createElement('span', 'dt-radio'); element.radioElement.name = name; element.radioElement.checked = !!checked; - element.createTextChild(title); + element.labelElement.createTextChild(title); return element; }; @@ -1226,7 +1241,7 @@ * @return {!Element} */ UI.createLabel = function(title, iconClass) { - const element = createElement('label', 'dt-icon-label'); + const element = createElement('span', 'dt-icon-label'); element.createChild('span').textContent = title; element.type = iconClass; return element; @@ -1238,8 +1253,8 @@ * @param {number} max * @param {number} tabIndex */ -UI.createSliderLabel = function(min, max, tabIndex) { - const element = createElement('label', 'dt-slider'); +UI.createSlider = function(min, max, tabIndex) { + const element = createElement('span', 'dt-slider'); element.sliderElement.min = min; element.sliderElement.max = max; element.sliderElement.step = 1; @@ -1270,10 +1285,7 @@ } }; -/** - * @extends {HTMLLabelElement} - */ -UI.CheckboxLabel = class extends HTMLLabelElement { +UI.CheckboxLabel = class extends HTMLSpanElement { constructor() { super(); /** @type {!DocumentFragment} */ @@ -1282,13 +1294,6 @@ this.checkboxElement; /** @type {!Element} */ this.textElement; - throw new Error('Checkbox must be created via factory method.'); - } - - /** - * @override - */ - createdCallback() { UI.CheckboxLabel._lastId = (UI.CheckboxLabel._lastId || 0) + 1; const id = 'ui-checkbox-label' + UI.CheckboxLabel._lastId; this._shadowRoot = UI.createShadowRootWithCoreStyles(this, 'ui/checkboxTextLabel.css'); @@ -1297,7 +1302,7 @@ this.checkboxElement.setAttribute('id', id); this.textElement = this._shadowRoot.createChild('label', 'dt-checkbox-text'); this.textElement.setAttribute('for', id); - this._shadowRoot.createChild('content'); + this._shadowRoot.createChild('slot'); } /** @@ -1308,8 +1313,8 @@ */ static create(title, checked, subtitle) { if (!UI.CheckboxLabel._constructor) - UI.CheckboxLabel._constructor = UI.registerCustomElement('label', 'dt-checkbox', UI.CheckboxLabel.prototype); - const element = /** @type {!UI.CheckboxLabel} */ (new UI.CheckboxLabel._constructor()); + UI.CheckboxLabel._constructor = UI.registerCustomElement('span', 'dt-checkbox', UI.CheckboxLabel); + const element = /** @type {!UI.CheckboxLabel} */ (UI.CheckboxLabel._constructor()); element.checkboxElement.checked = !!checked; if (title !== undefined) { element.textElement.textContent = title; @@ -1350,152 +1355,124 @@ }; (function() { - UI.registerCustomElement('button', 'text-button', { - /** - * @this {Element} - */ - createdCallback: function() { - this.type = 'button'; - const root = UI.createShadowRootWithCoreStyles(this, 'ui/textButton.css'); - root.createChild('content'); - }, +let labelId = 0; +UI.registerCustomElement('span', 'dt-radio', class extends HTMLSpanElement { + constructor() { + super(); + this.radioElement = this.createChild('input', 'dt-radio-button'); + this.labelElement = this.createChild('label'); - __proto__: HTMLButtonElement.prototype - }); + const id = 'dt-radio-button-id' + (++labelId); + this.radioElement.id = id; + this.radioElement.type = 'radio'; + this.labelElement.htmlFor = id; + const root = UI.createShadowRootWithCoreStyles(this, 'ui/radioButton.css'); + root.createChild('slot'); + this.addEventListener('click', radioClickHandler, false); + } +}); - UI.registerCustomElement('label', 'dt-radio', { - /** - * @this {Element} - */ - createdCallback: function() { - this.radioElement = this.createChild('input', 'dt-radio-button'); - this.radioElement.type = 'radio'; - const root = UI.createShadowRootWithCoreStyles(this, 'ui/radioButton.css'); - root.createChild('content').select = '.dt-radio-button'; - root.createChild('content'); - this.addEventListener('click', radioClickHandler, false); - }, - - __proto__: HTMLLabelElement.prototype - }); - - /** +/** * @param {!Event} event * @suppressReceiverCheck * @this {Element} */ - function radioClickHandler(event) { - if (this.radioElement.checked || this.radioElement.disabled) - return; - this.radioElement.checked = true; - this.radioElement.dispatchEvent(new Event('change')); +function radioClickHandler(event) { + if (this.radioElement.checked || this.radioElement.disabled) + return; + this.radioElement.checked = true; + this.radioElement.dispatchEvent(new Event('change')); +} + +UI.registerCustomElement('span', 'dt-icon-label', class extends HTMLSpanElement { + constructor() { + super(); + const root = UI.createShadowRootWithCoreStyles(this); + this._iconElement = UI.Icon.create(); + this._iconElement.style.setProperty('margin-right', '4px'); + root.appendChild(this._iconElement); + root.createChild('slot'); } - UI.registerCustomElement('label', 'dt-icon-label', { - /** - * @this {Element} - */ - createdCallback: function() { - const root = UI.createShadowRootWithCoreStyles(this); - this._iconElement = UI.Icon.create(); - this._iconElement.style.setProperty('margin-right', '4px'); - root.appendChild(this._iconElement); - root.createChild('content'); - }, - - /** + /** * @param {string} type * @this {Element} */ - set type(type) { - this._iconElement.setIconType(type); - }, + set type(type) { + this._iconElement.setIconType(type); + } +}); - __proto__: HTMLLabelElement.prototype - }); +UI.registerCustomElement('span', 'dt-slider', class extends HTMLSpanElement { + constructor() { + super(); + const root = UI.createShadowRootWithCoreStyles(this, 'ui/slider.css'); + this.sliderElement = createElementWithClass('input', 'dt-range-input'); + this.sliderElement.type = 'range'; + root.appendChild(this.sliderElement); + } - UI.registerCustomElement('label', 'dt-slider', { - /** - * @this {Element} - */ - createdCallback: function() { - const root = UI.createShadowRootWithCoreStyles(this, 'ui/slider.css'); - this.sliderElement = createElementWithClass('input', 'dt-range-input'); - this.sliderElement.type = 'range'; - root.appendChild(this.sliderElement); - }, - - /** + /** * @param {number} amount * @this {Element} */ - set value(amount) { - this.sliderElement.value = amount; - }, + set value(amount) { + this.sliderElement.value = amount; + } - /** + /** * @this {Element} */ - get value() { - return this.sliderElement.value; - }, + get value() { + return this.sliderElement.value; + } +}); - __proto__: HTMLLabelElement.prototype - }); +UI.registerCustomElement('span', 'dt-small-bubble', class extends HTMLSpanElement { + constructor() { + super(); + const root = UI.createShadowRootWithCoreStyles(this, 'ui/smallBubble.css'); + this._textElement = root.createChild('div'); + this._textElement.className = 'info'; + this._textElement.createChild('slot'); + } - UI.registerCustomElement('label', 'dt-small-bubble', { - /** - * @this {Element} - */ - createdCallback: function() { - const root = UI.createShadowRootWithCoreStyles(this, 'ui/smallBubble.css'); - this._textElement = root.createChild('div'); - this._textElement.className = 'info'; - this._textElement.createChild('content'); - }, - - /** + /** * @param {string} type * @this {Element} */ - set type(type) { - this._textElement.className = type; - }, + set type(type) { + this._textElement.className = type; + } +}); - __proto__: HTMLLabelElement.prototype - }); +UI.registerCustomElement('div', 'dt-close-button', class extends HTMLDivElement { + constructor() { + super(); + const root = UI.createShadowRootWithCoreStyles(this, 'ui/closeButton.css'); + this._buttonElement = root.createChild('div', 'close-button'); + const regularIcon = UI.Icon.create('smallicon-cross', 'default-icon'); + this._hoverIcon = UI.Icon.create('mediumicon-red-cross-hover', 'hover-icon'); + this._activeIcon = UI.Icon.create('mediumicon-red-cross-active', 'active-icon'); + this._buttonElement.appendChild(regularIcon); + this._buttonElement.appendChild(this._hoverIcon); + this._buttonElement.appendChild(this._activeIcon); + } - UI.registerCustomElement('div', 'dt-close-button', { - /** - * @this {Element} - */ - createdCallback: function() { - const root = UI.createShadowRootWithCoreStyles(this, 'ui/closeButton.css'); - this._buttonElement = root.createChild('div', 'close-button'); - const regularIcon = UI.Icon.create('smallicon-cross', 'default-icon'); - this._hoverIcon = UI.Icon.create('mediumicon-red-cross-hover', 'hover-icon'); - this._activeIcon = UI.Icon.create('mediumicon-red-cross-active', 'active-icon'); - this._buttonElement.appendChild(regularIcon); - this._buttonElement.appendChild(this._hoverIcon); - this._buttonElement.appendChild(this._activeIcon); - }, - - /** + /** * @param {boolean} gray * @this {Element} */ - set gray(gray) { - if (gray) { - this._hoverIcon.setIconType('mediumicon-gray-cross-hover'); - this._activeIcon.setIconType('mediumicon-gray-cross-active'); - } else { - this._hoverIcon.setIconType('mediumicon-red-cross-hover'); - this._activeIcon.setIconType('mediumicon-red-cross-active'); - } - }, - - __proto__: HTMLDivElement.prototype - }); + set gray(gray) { + if (gray) { + this._hoverIcon.setIconType('mediumicon-gray-cross-hover'); + this._activeIcon.setIconType('mediumicon-gray-cross-active'); + } else { + this._hoverIcon.setIconType('mediumicon-red-cross-hover'); + this._activeIcon.setIconType('mediumicon-red-cross-active'); + } + } +}); })(); /** @@ -1681,7 +1658,7 @@ } /** - * @param {!Element} element + * @param {!Element|!ShadowRoot} element */ injectHighlightStyleSheets(element) { this._injectingStyleSheet = true;
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/View.js b/src/cobalt/debug/remote/devtools/front_end/ui/View.js index bc3d3be..96a784c 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/View.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/View.js
@@ -506,7 +506,7 @@ this._titleElement.addEventListener('keydown', this._onTitleKeyDown.bind(this), false); this.contentElement.insertBefore(this._titleElement, this.contentElement.firstChild); - this.contentElement.createChild('content'); + this.contentElement.createChild('slot'); this._view = view; view[UI.ViewManager._ExpandableContainerWidget._symbol] = this; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/Widget.js b/src/cobalt/debug/remote/devtools/front_end/ui/Widget.js index 861f157..611890d 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/Widget.js +++ b/src/cobalt/debug/remote/devtools/front_end/ui/Widget.js
@@ -30,13 +30,14 @@ UI.Widget = class extends Common.Object { /** * @param {boolean=} isWebComponent + * @param {boolean=} delegatesFocus */ - constructor(isWebComponent) { + constructor(isWebComponent, delegatesFocus) { super(); this.contentElement = createElementWithClass('div', 'widget'); if (isWebComponent) { this.element = createElementWithClass('div', 'vbox flex-auto'); - this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element); + this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element, undefined, delegatesFocus); this._shadowRoot.appendChild(this.contentElement); } else { this.element = this.contentElement; @@ -599,9 +600,10 @@ UI.VBox = class extends UI.Widget { /** * @param {boolean=} isWebComponent + * @param {boolean=} delegatesFocus */ - constructor(isWebComponent) { - super(isWebComponent); + constructor(isWebComponent, delegatesFocus) { + super(isWebComponent, delegatesFocus); this.contentElement.classList.add('vbox'); }
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/filter.css b/src/cobalt/debug/remote/devtools/front_end/ui/filter.css index 025789e..b09df81 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/filter.css +++ b/src/cobalt/debug/remote/devtools/front_end/ui/filter.css
@@ -46,10 +46,6 @@ align-items: center; } -.filter-text-filter label { - margin: auto 0; -} - .filter-bitset-filter { padding: 2px; display: inline-flex; @@ -116,7 +112,7 @@ position: relative; } -.filter-checkbox-filter > label { +.filter-checkbox-filter > [is=dt-checkbox] { display: flex; margin: auto 0; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/inspectorCommon.css b/src/cobalt/debug/remote/devtools/front_end/ui/inspectorCommon.css index 2e7fa6e..a65be85 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/inspectorCommon.css +++ b/src/cobalt/debug/remote/devtools/front_end/ui/inspectorCommon.css
@@ -306,7 +306,7 @@ white-space: nowrap; } -label[is=dt-icon-label] { +span[is=dt-icon-label] { flex: none; }
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/radioButton.css b/src/cobalt/debug/remote/devtools/front_end/ui/radioButton.css index 47f4775..cadf3a7 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/radioButton.css +++ b/src/cobalt/debug/remote/devtools/front_end/ui/radioButton.css
@@ -4,7 +4,7 @@ * found in the LICENSE file. */ -::content .dt-radio-button { +::slotted(input.dt-radio-button) { height: 17px; width: 17px; min-width: 17px; @@ -16,16 +16,16 @@ margin: 0 5px 5px 0; } -::content .dt-radio-button:active:not(:disabled) { +::slotted(input.dt-radio-button:active:not(:disabled)) { background-image: linear-gradient(to bottom, rgb(194, 194, 194), rgb(239, 239, 239)); } -::content .dt-radio-button:checked { +::slotted(input.dt-radio-button:checked) { background: url(Images/radioDot.png) center no-repeat, linear-gradient(to bottom, rgb(252, 252, 252), rgb(223, 223, 223)); } -::content .dt-radio-button:checked:active { +::slotted(input.dt-radio-button:checked:active) { background: url(Images/radioDot.png) center no-repeat, linear-gradient(to bottom, rgb(194, 194, 194), rgb(239, 239, 239)); }
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/softDropDownButton.css b/src/cobalt/debug/remote/devtools/front_end/ui/softDropDownButton.css index 3812ef1..2270d27 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/softDropDownButton.css +++ b/src/cobalt/debug/remote/devtools/front_end/ui/softDropDownButton.css
@@ -3,7 +3,7 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -:host { +button.soft-dropdown { height: 26px; text-align: left; position: relative; @@ -11,18 +11,18 @@ background: none; } -:host([disabled]) { +button.soft-dropdown[disabled] { opacity: .5; } -:host .title { +button.soft-dropdown > .title { padding-right: 5px; width: 120px; overflow: hidden; text-overflow: ellipsis; } -:host([data-keyboard-focus="true"]:focus)::before { +button.soft-dropdown[data-keyboard-focus="true"]:focus::before { content: ""; position: absolute; top: 2px;
diff --git a/src/cobalt/debug/remote/devtools/front_end/ui/textButton.css b/src/cobalt/debug/remote/devtools/front_end/ui/textButton.css index 001189a..cce75fa 100644 --- a/src/cobalt/debug/remote/devtools/front_end/ui/textButton.css +++ b/src/cobalt/debug/remote/devtools/front_end/ui/textButton.css
@@ -4,7 +4,7 @@ * found in the LICENSE file. */ -:host { + .text-button { margin: 2px; height: 24px; font-size: 12px; @@ -18,52 +18,49 @@ white-space: nowrap; } -:host(:not(:disabled):focus), -:host(:not(:disabled):hover), -:host(:not(:disabled):active) { +.text-button:not(:disabled):focus, +.text-button:not(:disabled):hover, +.text-button:not(:disabled):active { background-color: var(--toolbar-bg-color); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); cursor: pointer; } -:host(:not(:disabled):active) { +.text-button:not(:disabled):active { background-color: #f2f2f2; } -:host(:not(:disabled):focus) { +.text-button:not(:disabled):focus { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(66, 133, 244, 0.4); } -:host(:disabled) { +.text-button:disabled { opacity: 0.38; } -:host(.primary-button), -theme-preserve { +.text-button.primary-button, -theme-preserve { background-color: #4285F4; border: none; color: #fff; } -:host(.primary-button:not(:disabled):focus), -:host(.primary-button:not(:disabled):hover), -theme-preserve { - background-color: #3B78E7; +.text-button.primary-button:not(:disabled):focus, +.text-button.primary-button:not(:disabled):hover, +.text-button.primary-button:not(:disabled):active, -theme-preserve { + background-color: var(--accent-color-hover); } -:host(.primary-button:not(:disabled):active), -theme-preserve { - background-color: #3367D6; -} - -:host-context(.-theme-with-dark-background):host(:not(.primary-button):not(:disabled):focus), -:host-context(.-theme-with-dark-background):host(:not(.primary-button):not(:disabled):hover), -:host-context(.-theme-with-dark-background):host(:not(.primary-button):not(:disabled):active) { +.-theme-with-dark-background .text-button:not(.primary-button):not(:disabled):focus, +.-theme-with-dark-background .text-button:not(.primary-button):not(:disabled):hover, +.-theme-with-dark-background .text-button:not(.primary-button):not(:disabled):active { background-color: #313131; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } -:host-context(.-theme-with-dark-background):host(:not(.primary-button):not(:disabled):focus) { +.-theme-with-dark-background .text-button:not(.primary-button):not(:disabled):focus { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(94, 151, 246, 0.6); } -:host-context(.-theme-with-dark-background):host(:not(.primary-button):not(:disabled):active) { +.-theme-with-dark-background .text-button:not(.primary-button):not(:disabled):active { background-color: #3e3e3e; }
diff --git a/src/cobalt/doc/performance_tuning.md b/src/cobalt/doc/performance_tuning.md index c9fc55d..e8336f3 100644 --- a/src/cobalt/doc/performance_tuning.md +++ b/src/cobalt/doc/performance_tuning.md
@@ -421,11 +421,12 @@ 1. The command line option, "--timed_trace=XX" will instruct Cobalt to trace upon startup, for XX seconds (e.g. "--timed_trace=25"). When completed, the output will be written to the file `timed_trace.json`. -2. Using the debug console (hit CTRL+O on a keyboard once or twice), type in the - command "d.trace()" and hit enter. Cobalt will begin a trace. After - some time has passed (and presumably you have performed some actions), you - can open the debug console again and type "d.trace()" again to end the trace. - The trace output will be written to the file `triggered_trace.json`. +2. Using the debug console (hit CTRL+O on a keyboard once or twice), type in + the command "h5vcc.traceEvent.start()" and hit enter. Cobalt will begin a + trace. After some time has passed (and presumably you have performed some + actions), you can open the debug console again and type + "h5vcc.traceEvent.stop()" again to end the trace. + The trace output will be written to the file `h5vcc_trace_event.json`. The directory the output files will be placed within is the directory that the Starboard function `SbSystemGetPath()` returns with a `path_id` of
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc index d803d68..e8fcc53 100644 --- a/src/cobalt/dom/html_media_element.cc +++ b/src/cobalt/dom/html_media_element.cc
@@ -91,7 +91,7 @@ if (resource_url.SchemeIs("data")) { return true; } - // Check if resource_url is an hls url. Hls url must contain "hls_variant" + // Check if resource_url is an hls url. Hls url must contain "hls_variant". return resource_url.spec().find("hls_variant") != std::string::npos; } #endif // SB_HAS(PLAYER_WITH_URL)
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc index 57bc8c0..3b01e26 100644 --- a/src/cobalt/dom/media_source.cc +++ b/src/cobalt/dom/media_source.cc
@@ -51,8 +51,6 @@ #include "base/compiler_specific.h" #include "base/guid.h" #include "base/logging.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" #include "cobalt/base/tokens.h" #include "cobalt/dom/dom_exception.h" #include "cobalt/dom/dom_settings.h" @@ -69,39 +67,6 @@ using media::CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR; using media::PIPELINE_OK; -namespace { - -// Parse mime and codecs from content type. It will return "video/mp4" and -// "avc1.42E01E, mp4a.40.2" for "video/mp4; codecs="avc1.42E01E, mp4a.40.2". -// Note that this function does minimum validation as the media stack will check -// the mime type and codecs strictly. -bool ParseContentType(const std::string& content_type, std::string* mime, - std::string* codecs) { - DCHECK(mime); - DCHECK(codecs); - static const char kCodecs[] = "codecs="; - - // SplitString will also trim the results. - std::vector<std::string> tokens = ::base::SplitString( - content_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - // The first one has to be mime type with delimiter '/' like 'video/mp4'. - if (tokens.size() < 2 || tokens[0].find('/') == tokens[0].npos) { - return false; - } - *mime = tokens[0]; - for (size_t i = 1; i < tokens.size(); ++i) { - if (base::strncasecmp(tokens[i].c_str(), kCodecs, strlen(kCodecs))) { - continue; - } - *codecs = tokens[i].substr(strlen("codecs=")); - base::TrimString(*codecs, " \"", codecs); - break; - } - return !codecs->empty(); -} - -} // namespace - MediaSource::MediaSource() : chunk_demuxer_(NULL), ready_state_(kMediaSourceReadyStateClosed), @@ -203,18 +168,9 @@ return NULL; } - std::string mime; - std::string codecs; - - if (!ParseContentType(type, &mime, &codecs)) { - DOMException::Raise(DOMException::kNotSupportedErr, exception_state); - // Return value should be ignored. - return NULL; - } - std::string guid = base::GenerateGUID(); scoped_refptr<SourceBuffer> source_buffer; - ChunkDemuxer::Status status = chunk_demuxer_->AddId(guid, mime, codecs); + ChunkDemuxer::Status status = chunk_demuxer_->AddId(guid, type); switch (status) { case ChunkDemuxer::kOk: source_buffer =
diff --git a/src/cobalt/media/base/audio_decoder_config.h b/src/cobalt/media/base/audio_decoder_config.h index 06c4b5a..5c15879 100644 --- a/src/cobalt/media/base/audio_decoder_config.h +++ b/src/cobalt/media/base/audio_decoder_config.h
@@ -80,6 +80,9 @@ return encryption_scheme_; } + void set_mime(const std::string& mime) { mime_ = mime; } + const std::string& mime() const { return mime_; } + private: AudioCodec codec_; SampleFormat sample_format_; @@ -99,6 +102,13 @@ // as padding added during encoding. int codec_delay_; + // |mime_| contains the mime type string specified when creating the stream. + // For example, when the stream is created via MediaSource, it is the mime + // string passed to addSourceBuffer(). It can be an empty string when the + // appropriate mime string is unknown. It is provided as an extra information + // and can be inconsistent with the other member variables. + std::string mime_; + // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler // generated copy constructor and assignment operator. Since the extra data is // typically small, the performance impact is minimal.
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc index d0c7e92..0880efb 100644 --- a/src/cobalt/media/base/starboard_player.cc +++ b/src/cobalt/media/base/starboard_player.cc
@@ -150,6 +150,11 @@ DCHECK(set_bounds_helper_); DCHECK(video_frame_provider_); +#if SB_API_VERSION >= 11 + audio_sample_info_.codec = kSbMediaAudioCodecNone; + video_sample_info_.codec = kSbMediaVideoCodecNone; +#endif // SB_API_VERSION >= 11 + if (audio_config.IsValidConfig()) { UpdateAudioConfig(audio_config); } @@ -157,9 +162,7 @@ UpdateVideoConfig(video_config); } - output_mode_ = ComputeSbPlayerOutputMode( - MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), drm_system, - prefer_decode_to_texture); + output_mode_ = ComputeSbPlayerOutputMode(prefer_decode_to_texture); CreatePlayer(); @@ -554,8 +557,30 @@ } #endif // SB_API_VERSION >= 11 - DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_)); bool has_audio = audio_codec != kSbMediaAudioCodecNone; + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + + SbPlayerCreationParam creation_param = {}; + creation_param.audio_mime = + audio_config_.IsValidConfig() ? audio_config_.mime().c_str() : ""; + creation_param.video_mime = + video_config_.IsValidConfig() ? video_config_.mime().c_str() : ""; + creation_param.drm_system = drm_system_; + creation_param.audio_sample_info = audio_sample_info_; + creation_param.video_sample_info = video_sample_info_; + creation_param.output_mode = output_mode_; + creation_param.max_video_capabilities = max_video_capabilities_.c_str(); + DCHECK_EQ(SbPlayerGetPreferredOutputMode(&creation_param), output_mode_); + player_ = SbPlayerCreate( + window_, &creation_param, &StarboardPlayer::DeallocateSampleCB, + &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, + &StarboardPlayer::PlayerErrorCB, this, + get_decode_target_graphics_context_provider_func_.Run()); + +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + + DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_)); player_ = SbPlayerCreate( window_, video_codec, audio_codec, #if SB_API_VERSION < 10 @@ -573,6 +598,9 @@ #endif // SB_HAS(PLAYER_ERROR_MESSAGE) this, output_mode_, get_decode_target_graphics_context_provider_func_.Run()); + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + DCHECK(SbPlayerIsValid(player_)); if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { @@ -935,24 +963,54 @@ // static SbPlayerOutputMode StarboardPlayer::ComputeSbPlayerOutputMode( - SbMediaVideoCodec codec, SbDrmSystem drm_system, - bool prefer_decode_to_texture) { - // Try to choose the output mode according to the passed in value of - // |prefer_decode_to_texture|. If the preferred output mode is unavailable - // though, fallback to an output mode that is available. - SbPlayerOutputMode output_mode = kSbPlayerOutputModeInvalid; - if (SbPlayerOutputModeSupported(kSbPlayerOutputModePunchOut, codec, - drm_system)) { - output_mode = kSbPlayerOutputModePunchOut; - } - if ((prefer_decode_to_texture || output_mode == kSbPlayerOutputModeInvalid) && - SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, codec, - drm_system)) { - output_mode = kSbPlayerOutputModeDecodeToTexture; - } - CHECK_NE(kSbPlayerOutputModeInvalid, output_mode); + bool prefer_decode_to_texture) const { +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + SbPlayerCreationParam creation_param = {}; + creation_param.audio_mime = + audio_config_.IsValidConfig() ? audio_config_.mime().c_str() : ""; + creation_param.video_mime = + video_config_.IsValidConfig() ? video_config_.mime().c_str() : ""; + creation_param.drm_system = drm_system_; + creation_param.audio_sample_info = audio_sample_info_; + creation_param.video_sample_info = video_sample_info_; + creation_param.max_video_capabilities = max_video_capabilities_.c_str(); + // Try to choose |kSbPlayerOutputModeDecodeToTexture| when + // |prefer_decode_to_texture| is true. + if (prefer_decode_to_texture) { + creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture; + } else { + creation_param.output_mode = kSbPlayerOutputModePunchOut; + } + auto output_mode = SbPlayerGetPreferredOutputMode(&creation_param); + CHECK_NE(kSbPlayerOutputModeInvalid, output_mode); return output_mode; +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + SbMediaVideoCodec video_codec = kSbMediaVideoCodecNone; + +#if SB_API_VERSION >= 11 + video_codec = video_sample_info_.codec; +#else // SB_API_VERSION >= 11 + video_codec = MediaVideoCodecToSbMediaVideoCodec(video_config_.codec()); +#endif // SB_API_VERSION >= 11 + + // Try to choose |kSbPlayerOutputModeDecodeToTexture| when + // |prefer_decode_to_texture| is true. + if (prefer_decode_to_texture) { + if (SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, + video_codec, drm_system_)) { + return kSbPlayerOutputModeDecodeToTexture; + } + } + + if (SbPlayerOutputModeSupported(kSbPlayerOutputModePunchOut, video_codec, + drm_system_)) { + return kSbPlayerOutputModePunchOut; + } + CHECK(SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, + video_codec, drm_system_)); + return kSbPlayerOutputModeDecodeToTexture; +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) } } // namespace media
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h index 13ef299..2074cde 100644 --- a/src/cobalt/media/base/starboard_player.h +++ b/src/cobalt/media/base/starboard_player.h
@@ -210,9 +210,8 @@ #endif // SB_HAS(PLAYER_WITH_URL) // Returns the output mode that should be used for a video with the given // specifications. - static SbPlayerOutputMode ComputeSbPlayerOutputMode( - SbMediaVideoCodec codec, SbDrmSystem drm_system, - bool prefer_decode_to_texture); + SbPlayerOutputMode ComputeSbPlayerOutputMode( + bool prefer_decode_to_texture) const; // The following variables are initialized in the ctor and never changed. #if SB_HAS(PLAYER_WITH_URL)
diff --git a/src/cobalt/media/base/video_decoder_config.h b/src/cobalt/media/base/video_decoder_config.h index ffcdb8a..ae8cf34 100644 --- a/src/cobalt/media/base/video_decoder_config.h +++ b/src/cobalt/media/base/video_decoder_config.h
@@ -112,6 +112,9 @@ return webm_color_metadata_; } + void set_mime(const std::string& mime) { mime_ = mime; } + const std::string& mime() const { return mime_; } + private: VideoCodec codec_; VideoCodecProfile profile_; @@ -131,6 +134,13 @@ WebMColorMetadata webm_color_metadata_; + // |mime_| contains the mime type string specified when creating the stream. + // For example, when the stream is created via MediaSource, it is the mime + // string passed to addSourceBuffer(). It can be an empty string when the + // appropriate mime string is unknown. It is provided as an extra information + // and can be inconsistent with the other member variables. + std::string mime_; + // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler // generated copy constructor and assignment operator. Since the extra data is // typically small, the performance impact is minimal.
diff --git a/src/cobalt/media/filters/chunk_demuxer.cc b/src/cobalt/media/filters/chunk_demuxer.cc index bdc048d..e88c81d 100644 --- a/src/cobalt/media/filters/chunk_demuxer.cc +++ b/src/cobalt/media/filters/chunk_demuxer.cc
@@ -16,6 +16,8 @@ #include "base/metrics/histogram.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "base/trace_event/trace_event.h" #include "cobalt/media/base/audio_decoder_config.h" #include "cobalt/media/base/bind_to_current_loop.h" @@ -34,9 +36,44 @@ namespace cobalt { namespace media { -ChunkDemuxerStream::ChunkDemuxerStream(Type type, bool splice_frames_enabled, +namespace { + +// Parse type and codecs from mime type. It will return "video/mp4" and +// "avc1.42E01E, mp4a.40.2" for "video/mp4; codecs="avc1.42E01E, mp4a.40.2". +// Note that this function does minimum validation as the media stack will check +// the type and codecs strictly. +bool ParseMimeType(const std::string& mime_type, std::string* type, + std::string* codecs) { + DCHECK(type); + DCHECK(codecs); + static const char kCodecs[] = "codecs="; + + // SplitString will also trim the results. + std::vector<std::string> tokens = ::base::SplitString( + mime_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + // The first one has to be mime type with delimiter '/' like 'video/mp4'. + if (tokens.size() < 2 || tokens[0].find('/') == tokens[0].npos) { + return false; + } + *type = tokens[0]; + for (size_t i = 1; i < tokens.size(); ++i) { + if (base::strncasecmp(tokens[i].c_str(), kCodecs, strlen(kCodecs))) { + continue; + } + *codecs = tokens[i].substr(strlen("codecs=")); + base::TrimString(*codecs, " \"", codecs); + break; + } + return !codecs->empty(); +} + +} // namespace + +ChunkDemuxerStream::ChunkDemuxerStream(Type type, const std::string& mime, + bool splice_frames_enabled, MediaTrack::Id media_track_id) : type_(type), + mime_(mime), liveness_(DemuxerStream::LIVENESS_UNKNOWN), media_track_id_(media_track_id), state_(UNINITIALIZED), @@ -272,13 +309,17 @@ AudioDecoderConfig ChunkDemuxerStream::audio_decoder_config() { CHECK_EQ(type_, AUDIO); base::AutoLock auto_lock(lock_); - return stream_->GetCurrentAudioDecoderConfig(); + auto config = stream_->GetCurrentAudioDecoderConfig(); + config.set_mime(mime_); + return config; } VideoDecoderConfig ChunkDemuxerStream::video_decoder_config() { CHECK_EQ(type_, VIDEO); base::AutoLock auto_lock(lock_); - return stream_->GetCurrentVideoDecoderConfig(); + auto config = stream_->GetCurrentVideoDecoderConfig(); + config.set_mime(mime_); + return config; } bool ChunkDemuxerStream::SupportsConfigChanges() { return true; } @@ -607,10 +648,16 @@ } ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, - const std::string& type, - const std::string& codecs) { - DVLOG(1) << __func__ << " id=" << id << " mime_type=" << type - << " codecs=" << codecs; + const std::string& mime) { + DVLOG(1) << __func__ << " id=" << id << " mime_type=" << mime; + + std::string type; + std::string codecs; + + if (!ParseMimeType(mime, &type, &codecs)) { + return kNotSupported; + } + base::AutoLock auto_lock(lock_); if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id)) @@ -632,8 +679,8 @@ std::unique_ptr<SourceBufferState> source_state(new SourceBufferState( std::move(stream_parser), std::move(frame_processor), - base::Bind(&ChunkDemuxer::CreateDemuxerStream, base::Unretained(this), - id), + base::Bind(&ChunkDemuxer::CreateDemuxerStream, base::Unretained(this), id, + mime), media_log_, buffer_allocator_)); SourceBufferState::NewTextTrackCB new_text_track_cb; @@ -1195,7 +1242,8 @@ } ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream( - const std::string& source_id, DemuxerStream::Type type) { + const std::string& source_id, const std::string& mime, + DemuxerStream::Type type) { // New ChunkDemuxerStreams can be created only during initialization segment // processing, which happens when a new chunk of data is appended and the // lock_ must be held by ChunkDemuxer::AppendData. @@ -1223,8 +1271,8 @@ return NULL; } - std::unique_ptr<ChunkDemuxerStream> stream( - new ChunkDemuxerStream(type, splice_frames_enabled_, media_track_id)); + std::unique_ptr<ChunkDemuxerStream> stream(new ChunkDemuxerStream( + type, mime, splice_frames_enabled_, media_track_id)); DCHECK(track_id_to_demux_stream_map_.find(media_track_id) == track_id_to_demux_stream_map_.end()); track_id_to_demux_stream_map_[media_track_id] = stream.get();
diff --git a/src/cobalt/media/filters/chunk_demuxer.h b/src/cobalt/media/filters/chunk_demuxer.h index 574e82e..f0d2428 100644 --- a/src/cobalt/media/filters/chunk_demuxer.h +++ b/src/cobalt/media/filters/chunk_demuxer.h
@@ -35,8 +35,8 @@ public: typedef std::deque<scoped_refptr<StreamParserBuffer>> BufferQueue; - ChunkDemuxerStream(Type type, bool splice_frames_enabled, - MediaTrack::Id media_track_id); + ChunkDemuxerStream(Type type, const std::string& mime, + bool splice_frames_enabled, MediaTrack::Id media_track_id); ~ChunkDemuxerStream() override; // ChunkDemuxerStream control methods. @@ -148,6 +148,8 @@ // Specifies the type of the stream. Type type_; + const std::string mime_; + Liveness liveness_; std::unique_ptr<SourceBufferStream> stream_; @@ -212,15 +214,14 @@ void StartWaitingForSeek(base::TimeDelta seek_time) override; void CancelPendingSeek(base::TimeDelta seek_time) override; - // Registers a new |id| to use for AppendData() calls. |type| indicates + // Registers a new |id| to use for AppendData() calls. |mime| indicates // the MIME type for the data that we intend to append for this ID. // kOk is returned if the demuxer has enough resources to support another ID // and supports the format indicated by |type|. // kNotSupported is returned if |type| is not a supported format. // kReachedIdLimit is returned if the demuxer cannot handle another ID right // now. - Status AddId(const std::string& id, const std::string& type, - const std::string& codecs); + Status AddId(const std::string& id, const std::string& mime); // Notifies a caller via |tracks_updated_cb| that the set of media tracks // for a given |id| has changed. @@ -351,6 +352,7 @@ // Returns a pointer to a new ChunkDemuxerStream instance, which is owned by // ChunkDemuxer. ChunkDemuxerStream* CreateDemuxerStream(const std::string& source_id, + const std::string& mime, DemuxerStream::Type type); void OnNewTextTrack(ChunkDemuxerStream* text_stream,
diff --git a/src/cobalt/media/filters/shell_demuxer.cc b/src/cobalt/media/filters/shell_demuxer.cc index a68a82a..6f7f226 100644 --- a/src/cobalt/media/filters/shell_demuxer.cc +++ b/src/cobalt/media/filters/shell_demuxer.cc
@@ -402,6 +402,7 @@ requested_au_->GetMaxSize())); if (decoder_buffer) { decoder_buffer->set_is_key_frame(requested_au_->IsKeyframe()); + buffer_allocator_->UpdateVideoConfig(VideoConfig()); Download(decoder_buffer); } else { // As the buffer is full of media data, it is safe to delay 100
diff --git a/src/cobalt/media/formats/mp4/box_definitions.cc b/src/cobalt/media/formats/mp4/box_definitions.cc index 01cb776..aee8f2c 100644 --- a/src/cobalt/media/formats/mp4/box_definitions.cc +++ b/src/cobalt/media/formats/mp4/box_definitions.cc
@@ -23,6 +23,31 @@ namespace media { namespace mp4 { +namespace { + +// Read color coordinate value as defined in the MasteringDisplayColorVolume +// ('mdcv') box. Each coordinate is a float encoded in uint16_t, with upper +// bound set to 50000. +bool ReadMdcvColorCoordinate(BoxReader* reader, + float* normalized_value_in_float) { + const float kColorCoordinateUpperBound = 50000.; + + uint16_t value_in_uint16; + RCHECK(reader->Read2(&value_in_uint16)); + + float value_in_float = static_cast<float>(value_in_uint16); + + if (value_in_float >= kColorCoordinateUpperBound) { + *normalized_value_in_float = 1.f; + return true; + } + + *normalized_value_in_float = value_in_float / kColorCoordinateUpperBound; + return true; +} + +} // namespace + FileType::FileType() {} FileType::~FileType() {} FourCC FileType::BoxType() const { return FOURCC_FTYP; } @@ -620,6 +645,60 @@ return true; } +ColorParameterInformation::ColorParameterInformation() {} +ColorParameterInformation::~ColorParameterInformation() {} +FourCC ColorParameterInformation::BoxType() const { return FOURCC_COLR; } + +bool ColorParameterInformation::Parse(BoxReader* reader) { + FourCC type; + RCHECK(reader->ReadFourCC(&type)); + // TODO: Support 'nclc', 'rICC', and 'prof'. + RCHECK(type == FOURCC_NCLX); + + uint8_t full_range_byte; + RCHECK(reader->Read2(&colour_primaries) && + reader->Read2(&transfer_characteristics) && + reader->Read2(&matrix_coefficients) && + reader->Read1(&full_range_byte)); + full_range = full_range_byte & 0x80; + + return true; +} + +MasteringDisplayColorVolume::MasteringDisplayColorVolume() {} +MasteringDisplayColorVolume::~MasteringDisplayColorVolume() {} +FourCC MasteringDisplayColorVolume::BoxType() const { return FOURCC_MDCV; } + +bool MasteringDisplayColorVolume::Parse(BoxReader* reader) { + // Technically the color coordinates may be in any order. The spec recommends + // GBR and it is assumed that the color coordinates are in such order. + RCHECK(ReadMdcvColorCoordinate(reader, &display_primaries_gx) && + ReadMdcvColorCoordinate(reader, &display_primaries_gy) && + ReadMdcvColorCoordinate(reader, &display_primaries_bx) && + ReadMdcvColorCoordinate(reader, &display_primaries_by) && + ReadMdcvColorCoordinate(reader, &display_primaries_rx) && + ReadMdcvColorCoordinate(reader, &display_primaries_ry) && + ReadMdcvColorCoordinate(reader, &white_point_x) && + ReadMdcvColorCoordinate(reader, &white_point_y) && + reader->Read4(&max_display_mastering_luminance) && + reader->Read4(&min_display_mastering_luminance)); + + const uint32_t kUnitOfMasteringLuminance = 10000; + max_display_mastering_luminance /= kUnitOfMasteringLuminance; + min_display_mastering_luminance /= kUnitOfMasteringLuminance; + + return true; +} + +ContentLightLevelInformation::ContentLightLevelInformation() {} +ContentLightLevelInformation::~ContentLightLevelInformation() {} +FourCC ContentLightLevelInformation::BoxType() const { return FOURCC_CLLI; } + +bool ContentLightLevelInformation::Parse(BoxReader* reader) { + return reader->Read2(&max_content_light_level) && + reader->Read2(&max_pic_average_light_level); +} + VideoSampleEntry::VideoSampleEntry() : format(FOURCC_NULL), data_reference_index(0), @@ -688,6 +767,24 @@ frame_bitstream_converter = nullptr; video_codec = kCodecAV1; video_codec_profile = av1_config.profile; + + ColorParameterInformation color_parameter_information; + if (reader->HasChild(&color_parameter_information)) { + RCHECK(reader->ReadChild(&color_parameter_information)); + this->color_parameter_information = color_parameter_information; + } + + MasteringDisplayColorVolume mastering_display_color_volume; + if (reader->HasChild(&mastering_display_color_volume)) { + RCHECK(reader->ReadChild(&mastering_display_color_volume)); + this->mastering_display_color_volume = mastering_display_color_volume; + } + + ContentLightLevelInformation content_light_level_information; + if (reader->HasChild(&content_light_level_information)) { + RCHECK(reader->ReadChild(&content_light_level_information)); + this->content_light_level_information = content_light_level_information; + } break; } default:
diff --git a/src/cobalt/media/formats/mp4/box_definitions.h b/src/cobalt/media/formats/mp4/box_definitions.h index 90415d9..db69017 100644 --- a/src/cobalt/media/formats/mp4/box_definitions.h +++ b/src/cobalt/media/formats/mp4/box_definitions.h
@@ -9,6 +9,7 @@ #include <vector> #include "base/compiler_specific.h" +#include "base/optional.h" #include "cobalt/media/base/decrypt_config.h" #include "cobalt/media/base/media_export.h" #include "cobalt/media/base/media_log.h" @@ -239,6 +240,37 @@ uint32_t v_spacing; }; +struct MEDIA_EXPORT ColorParameterInformation : Box { + DECLARE_BOX_METHODS(ColorParameterInformation); + + uint16 colour_primaries; + uint16 transfer_characteristics; + uint16 matrix_coefficients; + bool full_range; +}; + +struct MEDIA_EXPORT MasteringDisplayColorVolume : Box { + DECLARE_BOX_METHODS(MasteringDisplayColorVolume); + + float display_primaries_gx; + float display_primaries_gy; + float display_primaries_bx; + float display_primaries_by; + float display_primaries_rx; + float display_primaries_ry; + float white_point_x; + float white_point_y; + uint32 max_display_mastering_luminance; + uint32 min_display_mastering_luminance; +}; + +struct MEDIA_EXPORT ContentLightLevelInformation : Box { + DECLARE_BOX_METHODS(ContentLightLevelInformation); + + uint16 max_content_light_level; + uint16 max_pic_average_light_level; +}; + struct MEDIA_EXPORT VideoSampleEntry : Box { DECLARE_BOX_METHODS(VideoSampleEntry); @@ -253,6 +285,10 @@ VideoCodec video_codec; VideoCodecProfile video_codec_profile; + base::Optional<ColorParameterInformation> color_parameter_information; + base::Optional<MasteringDisplayColorVolume> mastering_display_color_volume; + base::Optional<ContentLightLevelInformation> content_light_level_information; + bool IsFormatValid() const; scoped_refptr<BitstreamConverter> frame_bitstream_converter;
diff --git a/src/cobalt/media/formats/mp4/fourccs.h b/src/cobalt/media/formats/mp4/fourccs.h index e79b003..497ac68 100644 --- a/src/cobalt/media/formats/mp4/fourccs.h +++ b/src/cobalt/media/formats/mp4/fourccs.h
@@ -22,7 +22,9 @@ FOURCC_AVCC = 0x61766343, FOURCC_BLOC = 0x626C6F63, FOURCC_CENC = 0x63656e63, + FOURCC_CLLI = 0x636c6c69, FOURCC_CO64 = 0x636f3634, + FOURCC_COLR = 0x636f6c72, FOURCC_CTTS = 0x63747473, FOURCC_DINF = 0x64696e66, FOURCC_EDTS = 0x65647473, @@ -41,6 +43,7 @@ FOURCC_HVCC = 0x68766343, FOURCC_IODS = 0x696f6473, FOURCC_MDAT = 0x6d646174, + FOURCC_MDCV = 0x6d646376, FOURCC_MDHD = 0x6d646864, FOURCC_MDIA = 0x6d646961, FOURCC_MECO = 0x6d65636f, @@ -55,6 +58,7 @@ FOURCC_MP4V = 0x6d703476, FOURCC_MVEX = 0x6d766578, FOURCC_MVHD = 0x6d766864, + FOURCC_NCLX = 0x6e636c78, FOURCC_PASP = 0x70617370, FOURCC_PDIN = 0x7064696e, FOURCC_PRFT = 0x70726674,
diff --git a/src/cobalt/media/formats/mp4/mp4_stream_parser.cc b/src/cobalt/media/formats/mp4/mp4_stream_parser.cc index 391318b..1ab9371 100644 --- a/src/cobalt/media/formats/mp4/mp4_stream_parser.cc +++ b/src/cobalt/media/formats/mp4/mp4_stream_parser.cc
@@ -16,6 +16,8 @@ #include "base/time/time.h" #include "build/build_config.h" #include "cobalt/media/base/audio_decoder_config.h" +#include "cobalt/media/base/color_space.h" +#include "cobalt/media/base/hdr_metadata.h" #include "cobalt/media/base/media_tracks.h" #include "cobalt/media/base/media_util.h" #include "cobalt/media/base/stream_parser_buffer.h" @@ -28,6 +30,7 @@ #include "cobalt/media/formats/mp4/es_descriptor.h" #include "cobalt/media/formats/mp4/rcheck.h" #include "cobalt/media/formats/mpeg/adts_constants.h" +#include "cobalt/media/formats/webm/webm_colour_parser.h" #include "starboard/memory.h" #include "starboard/types.h" @@ -36,7 +39,45 @@ namespace mp4 { namespace { + +using gfx::ColorSpace; + const int kMaxEmptySampleLogs = 20; + +gfx::ColorSpace ConvertColorParameterInformationToColorSpace( + const ColorParameterInformation& info) { + auto primary_id = static_cast<ColorSpace::PrimaryID>(info.colour_primaries); + auto transfer_id = + static_cast<ColorSpace::TransferID>(info.transfer_characteristics); + auto matrix_id = static_cast<ColorSpace::MatrixID>(info.matrix_coefficients); + + // Note that we don't check whether the embedded ids are valid. We rely on + // the underlying video decoder to reject any ids that it doesn't support. + return gfx::ColorSpace( + primary_id, transfer_id, matrix_id, + info.full_range ? ColorSpace::kRangeIdFull : ColorSpace::kRangeIdLimited); +} + +MasteringMetadata ConvertMdcvToMasteringMetadata( + const MasteringDisplayColorVolume& mdcv) { + MasteringMetadata mastering_metadata; + + mastering_metadata.primary_r_chromaticity_x = mdcv.display_primaries_rx; + mastering_metadata.primary_r_chromaticity_y = mdcv.display_primaries_ry; + mastering_metadata.primary_g_chromaticity_x = mdcv.display_primaries_gx; + mastering_metadata.primary_g_chromaticity_y = mdcv.display_primaries_gy; + mastering_metadata.primary_b_chromaticity_x = mdcv.display_primaries_bx; + mastering_metadata.primary_b_chromaticity_y = mdcv.display_primaries_by; + mastering_metadata.white_point_chromaticity_x = mdcv.white_point_x; + mastering_metadata.white_point_chromaticity_y = mdcv.white_point_y; + mastering_metadata.luminance_max = + static_cast<float>(mdcv.max_display_mastering_luminance); + mastering_metadata.luminance_min = + static_cast<float>(mdcv.min_display_mastering_luminance); + + return mastering_metadata; +} + } // namespace MP4StreamParser::MP4StreamParser(DecoderBuffer::Allocator* buffer_allocator, @@ -371,6 +412,30 @@ // SPS/PPS are embedded in the video stream EmptyExtraData(), is_track_encrypted ? AesCtrEncryptionScheme() : Unencrypted()); + if (entry.color_parameter_information) { + WebMColorMetadata color_metadata = {}; + + color_metadata.color_space = + ConvertColorParameterInformationToColorSpace( + *entry.color_parameter_information); + + if (entry.mastering_display_color_volume) { + color_metadata.hdr_metadata.mastering_metadata = + ConvertMdcvToMasteringMetadata( + *entry.mastering_display_color_volume); + } + + if (entry.content_light_level_information) { + color_metadata.hdr_metadata.max_cll = + entry.content_light_level_information->max_content_light_level; + color_metadata.hdr_metadata.max_fall = + entry.content_light_level_information + ->max_pic_average_light_level; + } + + video_config.set_webm_color_metadata(color_metadata); + } + DVLOG(1) << "video_track_id=" << video_track_id << " config=" << video_config.AsHumanReadableString(); if (!video_config.IsValidConfig()) {
diff --git a/src/cobalt/media/sandbox/format_guesstimator.cc b/src/cobalt/media/sandbox/format_guesstimator.cc index ad5c97f..adc856b 100644 --- a/src/cobalt/media/sandbox/format_guesstimator.cc +++ b/src/cobalt/media/sandbox/format_guesstimator.cc
@@ -19,6 +19,8 @@ #include "base/bind.h" #include "base/path_service.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "base/time/time.h" #include "cobalt/media/base/audio_codecs.h" #include "cobalt/media/base/audio_decoder_config.h" @@ -42,20 +44,18 @@ namespace { -// Container to organize the pairs of supported mime types and their codecs. -struct SupportedTypeCodecInfo { - std::string mime; - std::string codecs; -}; +// The possible mime type configurations that are supported by cobalt. +const std::vector<std::string> kSupportedMimeTypes = { + "audio/mp4; codecs=\"ac-3\"", + "audio/mp4; codecs=\"ec-3\"", + "audio/mp4; codecs=\"mp4a.40.2\"", + "audio/webm; codecs=\"opus\"", -// The possible mime and codec configurations that are supported by cobalt. -const std::vector<SupportedTypeCodecInfo> kSupportedTypesAndCodecs = { - {"audio/mp4", "ac-3"}, {"audio/mp4", "ec-3"}, - {"audio/mp4", "mp4a.40.2"}, {"audio/webm", "opus"}, - - {"video/mp4", "av01.0.05M.08"}, {"video/mp4", "avc1.640028, mp4a.40.2"}, - {"video/mp4", "avc1.640028"}, {"video/mp4", "hvc1.1.6.H150.90"}, - {"video/webm", "vp9"}, + "video/mp4; codecs=\"av01.0.05M.08\"", + "video/mp4; codecs=\"avc1.640028, mp4a.40.2\"", + "video/mp4; codecs=\"avc1.640028\"", + "video/mp4; codecs=\"hvc1.1.6.H150.90\"", + "video/webm; codecs=\"vp9\"", }; // Can be called as: @@ -103,6 +103,28 @@ SB_UNREFERENCED_PARAMETER(tracks); } +// Extract the value of "codecs" parameter from content type. It will return +// "avc1.42E01E" for "video/mp4; codecs="avc1.42E01E". +// Note that this function assumes that the input is always valid and does +// minimum validation.. +std::string ExtractCodec(const std::string& content_type) { + static const char kCodecs[] = "codecs="; + + // SplitString will also trim the results. + std::vector<std::string> tokens = ::base::SplitString( + content_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + for (size_t i = 1; i < tokens.size(); ++i) { + if (base::strncasecmp(tokens[i].c_str(), kCodecs, strlen(kCodecs))) { + continue; + } + auto codec = tokens[i].substr(strlen("codecs=")); + base::TrimString(codec, " \"", &codec); + return codec; + } + NOTREACHED(); + return ""; +} + } // namespace FormatGuesstimator::FormatGuesstimator(const std::string& path_or_url, @@ -126,17 +148,15 @@ return; } progressive_url_ = url; - mime_ = "video/mp4"; - codecs_ = "avc1.640028, mp4a.40.2"; + mime_type_ = "video/mp4; codecs=\"avc1.640028, mp4a.40.2\""; } void FormatGuesstimator::InitializeAsAdaptive(const base::FilePath& path, MediaModule* media_module) { std::vector<uint8_t> header = ReadHeader(path); - for (const auto& expected_supported_info : kSupportedTypesAndCodecs) { - DCHECK(mime_.empty()); - DCHECK(codecs_.empty()); + for (const auto& expected_supported_mime_type : kSupportedMimeTypes) { + DCHECK(mime_type_.empty()); ChunkDemuxer* chunk_demuxer = NULL; WebMediaPlayerHelper::ChunkDemuxerOpenCB open_cb = base::Bind( @@ -157,8 +177,7 @@ } const std::string id = "stream"; - if (chunk_demuxer->AddId(id, expected_supported_info.mime, - expected_supported_info.codecs) != + if (chunk_demuxer->AddId(id, expected_supported_mime_type) != ChunkDemuxer::kOk) { continue; } @@ -182,11 +201,10 @@ chunk_demuxer->GetStream(DemuxerStream::Type::AUDIO)) { const AudioDecoderConfig& decoder_config = demuxer_stream->audio_decoder_config(); - if (StringToAudioCodec(expected_supported_info.codecs) == + if (StringToAudioCodec(ExtractCodec(expected_supported_mime_type)) == decoder_config.codec()) { adaptive_path_ = path; - mime_ = expected_supported_info.mime; - codecs_ = expected_supported_info.codecs; + mime_type_ = expected_supported_mime_type; break; } continue; @@ -195,11 +213,10 @@ DCHECK(demuxer_stream); const VideoDecoderConfig& decoder_config = demuxer_stream->video_decoder_config(); - if (StringToVideoCodec(expected_supported_info.codecs) == + if (StringToVideoCodec(ExtractCodec(expected_supported_mime_type)) == decoder_config.codec()) { adaptive_path_ = path; - mime_ = expected_supported_info.mime; - codecs_ = expected_supported_info.codecs; + mime_type_ = expected_supported_mime_type; break; } }
diff --git a/src/cobalt/media/sandbox/format_guesstimator.h b/src/cobalt/media/sandbox/format_guesstimator.h index dd95e9c..f9b25fb 100644 --- a/src/cobalt/media/sandbox/format_guesstimator.h +++ b/src/cobalt/media/sandbox/format_guesstimator.h
@@ -38,7 +38,7 @@ bool is_adaptive() const { return !adaptive_path_.empty(); } bool is_audio() const { DCHECK(is_adaptive()); - return mime_.find("audio/") == 0; + return mime_type_.find("audio/") == 0; } const GURL& progressive_url() const { @@ -53,13 +53,9 @@ return adaptive_path_.value(); } - const std::string& mime() const { + const std::string& mime_type() const { DCHECK(is_valid()); - return mime_; - } - const std::string& codecs() const { - DCHECK(is_valid()); - return codecs_; + return mime_type_; } private: @@ -69,8 +65,7 @@ GURL progressive_url_; base::FilePath adaptive_path_; - std::string mime_; - std::string codecs_; + std::string mime_type_; }; } // namespace sandbox
diff --git a/src/cobalt/media/sandbox/media2_sandbox.cc b/src/cobalt/media/sandbox/media2_sandbox.cc index fdbb7df..0228744 100644 --- a/src/cobalt/media/sandbox/media2_sandbox.cc +++ b/src/cobalt/media/sandbox/media2_sandbox.cc
@@ -118,15 +118,15 @@ demuxer->Initialize(&demuxer_host, base::Bind(OnDemuxerStatus), false); ChunkDemuxer::Status status = - demuxer->AddId("audio", "audio/mp4", "mp4a.40.2"); + demuxer->AddId("audio", "audio/mp4; codecs=\"mp4a.40.2\""); DCHECK_EQ(status, ChunkDemuxer::kOk); int video_url_length = SbStringGetLength(argv[2]); if (video_url_length > 5 && SbStringCompare(argv[2] + video_url_length - 5, ".webm", 5) == 0) { - status = demuxer->AddId("video", "video/webm", "vp9"); + status = demuxer->AddId("video", "video/webm; codecs=\"vp9\""); } else { - status = demuxer->AddId("video", "video/mp4", "avc1.640028"); + status = demuxer->AddId("video", "video/mp4; codecs=\"avc1.640028\""); } DCHECK_EQ(status, ChunkDemuxer::kOk);
diff --git a/src/cobalt/media/sandbox/web_media_player_sandbox.cc b/src/cobalt/media/sandbox/web_media_player_sandbox.cc index 7d28698..45f5a3f 100644 --- a/src/cobalt/media/sandbox/web_media_player_sandbox.cc +++ b/src/cobalt/media/sandbox/web_media_player_sandbox.cc
@@ -90,8 +90,6 @@ SbLogRaw(ss.str().c_str()); } -std::string MakeCodecParameter(const std::string& string) { return string; } - void OnInitSegmentReceived(std::unique_ptr<MediaTracks> tracks) { SB_UNREFERENCED_PARAMETER(tracks); } @@ -182,8 +180,7 @@ LOG(INFO) << "Playing " << guesstimator.adaptive_path(); std::string id = guesstimator.is_audio() ? kAudioId : kVideoId; - auto codecs = MakeCodecParameter(guesstimator.codecs()); - auto status = chunk_demuxer_->AddId(id, guesstimator.mime(), codecs); + auto status = chunk_demuxer_->AddId(id, guesstimator.mime_type()); CHECK_EQ(status, ChunkDemuxer::kOk); chunk_demuxer_->SetTracksWatcher(id, base::Bind(OnInitSegmentReceived)); @@ -234,13 +231,11 @@ LOG(INFO) << "Playing " << audio_guesstimator.adaptive_path() << " and " << video_guesstimator.adaptive_path(); - auto codecs = MakeCodecParameter(audio_guesstimator.codecs()); auto status = - chunk_demuxer_->AddId(kAudioId, audio_guesstimator.mime(), codecs); + chunk_demuxer_->AddId(kAudioId, audio_guesstimator.mime_type()); CHECK_EQ(status, ChunkDemuxer::kOk); - codecs = MakeCodecParameter(video_guesstimator.codecs()); - status = chunk_demuxer_->AddId(kVideoId, video_guesstimator.mime(), codecs); + status = chunk_demuxer_->AddId(kVideoId, video_guesstimator.mime_type()); CHECK_EQ(status, ChunkDemuxer::kOk); chunk_demuxer_->SetTracksWatcher(kAudioId,
diff --git a/src/cobalt/network/url_request_context.cc b/src/cobalt/network/url_request_context.cc index 7b532ae..671de56 100644 --- a/src/cobalt/network/url_request_context.cc +++ b/src/cobalt/network/url_request_context.cc
@@ -37,6 +37,7 @@ #include "net/http/http_transaction_factory.h" #include "net/proxy_resolution/proxy_config.h" #include "net/proxy_resolution/proxy_resolution_service.h" +#include "net/third_party/quic/platform/api/quic_flags.h" #include "net/ssl/ssl_config_service.h" #include "net/ssl/ssl_config_service_defaults.h" #include "net/url_request/data_protocol_handler.h" @@ -93,6 +94,10 @@ new ProxyConfigService(proxy_config)), net_log)); + // ack decimation significantly increases download bandwidth on low-end + // android devices. + SetQuicFlag(&FLAGS_quic_reloadable_flag_quic_enable_ack_decimation, true); + net::HostResolver::Options options; options.max_concurrent_resolves = net::HostResolver::kDefaultParallelism; options.max_retry_attempts = net::HostResolver::kDefaultRetryAttempts;
diff --git a/src/cobalt/renderer/backend/backend.gyp b/src/cobalt/renderer/backend/backend.gyp index 3ff2de4..3064909 100644 --- a/src/cobalt/renderer/backend/backend.gyp +++ b/src/cobalt/renderer/backend/backend.gyp
@@ -30,5 +30,31 @@ '<(DEPTH)/cobalt/renderer/backend/starboard/platform_backend.gyp:renderer_platform_backend', ], }, + { + 'target_name': 'graphics_system_test', + 'type': '<(gtest_target_type)', + 'sources': [ + 'graphics_system_test.cc', + ], + 'dependencies': [ + '<(DEPTH)/cobalt/base/base.gyp:base', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/cobalt/renderer/backend/backend.gyp:renderer_backend', + '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window', + ], + }, + { + 'target_name': 'graphics_system_test_deploy', + 'type': 'none', + 'dependencies': [ + 'graphics_system_test', + ], + 'variables': { + 'executable_name': 'graphics_system_test', + }, + 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ], + }, ], }
diff --git a/src/cobalt/renderer/backend/graphics_system_test.cc b/src/cobalt/renderer/backend/graphics_system_test.cc new file mode 100644 index 0000000..f6ad30c --- /dev/null +++ b/src/cobalt/renderer/backend/graphics_system_test.cc
@@ -0,0 +1,123 @@ +// Copyright 2020 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 <algorithm> +#include <memory> + +#include "base/optional.h" +#include "cobalt/renderer/backend/default_graphics_system.h" +#include "cobalt/renderer/backend/graphics_context.h" +#include "cobalt/renderer/backend/graphics_system.h" +#include "starboard/log.h" +#include "starboard/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cobalt { +namespace renderer { +namespace backend { + +namespace { + +// Number of initializations to use for measuring the reference creation time. +const int kReferenceCount = 5; +} // namespace + +TEST(GraphicsSystemTest, GraphicsSystemCanBeInitializedOften) { + // Test whether the graphics system can be initialized often without slowing + // down. + std::unique_ptr<GraphicsSystem> graphics_system; + + // Treat the first initialization as a 'warm up'. + graphics_system = CreateDefaultGraphicsSystem(); + graphics_system.reset(); + + SbTimeMonotonic start = SbTimeGetMonotonicNow(); + for (int i = 0; i < kReferenceCount; ++i) { + graphics_system = CreateDefaultGraphicsSystem(); + graphics_system.reset(); + } + SbTimeMonotonic time_per_initialization = + (SbTimeGetMonotonicNow() - start) / kReferenceCount; + SB_LOG(INFO) << "Measured duration " + << time_per_initialization / kSbTimeMillisecond + << "ms per initialization."; + + // Graphics system initializations should not take more than the maximum of + // 200ms or three times as long as the time we just measured. + SbTimeMonotonic maximum_time_per_initialization = + std::max(3 * time_per_initialization, 200 * kSbTimeMillisecond); + + SbTimeMonotonic last = SbTimeGetMonotonicNow(); + for (int i = 0; i < 20; ++i) { + graphics_system = CreateDefaultGraphicsSystem(); + graphics_system.reset(); + SbTimeMonotonic now = SbTimeGetMonotonicNow(); + SB_LOG(INFO) << "Test duration " << (now - last) / kSbTimeMillisecond + << "ms."; + ASSERT_LT(now - last, maximum_time_per_initialization); + last = now; + } +} + +TEST(GraphicsSystemTest, GraphicsContextCanBeInitializedOften) { + // Test whether the graphics system and graphics context can be initialized + // often without slowing down. + std::unique_ptr<GraphicsSystem> graphics_system; + std::unique_ptr<GraphicsContext> graphics_context; + + // Treat the first initialization as a 'warm up'. + graphics_system = CreateDefaultGraphicsSystem(); + graphics_context = graphics_system->CreateGraphicsContext(); + + graphics_context.reset(); + graphics_system.reset(); + + SbTimeMonotonic start = SbTimeGetMonotonicNow(); + for (int i = 0; i < kReferenceCount; ++i) { + graphics_system = CreateDefaultGraphicsSystem(); + graphics_context = graphics_system->CreateGraphicsContext(); + + graphics_context.reset(); + graphics_system.reset(); + } + SbTimeMonotonic time_per_initialization = kSbTimeMillisecond + + (SbTimeGetMonotonicNow() - start) / kReferenceCount; + SB_LOG(INFO) << "Measured duration " + << time_per_initialization / kSbTimeMillisecond + << "ms per initialization."; + + // Graphics system and context initializations should not take more than the + // maximum of 200ms or three times as long as the time we just measured. + SbTimeMonotonic maximum_time_per_initialization = + std::max(3 * time_per_initialization, 200 * kSbTimeMillisecond); + + SbTimeMonotonic last = SbTimeGetMonotonicNow(); + for (int i = 0; i < 20; ++i) { + graphics_system = CreateDefaultGraphicsSystem(); + graphics_context = graphics_system->CreateGraphicsContext(); + + graphics_context.reset(); + graphics_system.reset(); + + SbTimeMonotonic now = SbTimeGetMonotonicNow(); + SB_LOG(INFO) << "Test duration " << (now - last) / kSbTimeMillisecond + << "ms."; + ASSERT_LT(now - last, maximum_time_per_initialization); + last = now; + } +} + +} // namespace backend +} // namespace renderer +} // namespace cobalt
diff --git a/src/cobalt/script/global_environment.h b/src/cobalt/script/global_environment.h index 93b10a6..946211b 100644 --- a/src/cobalt/script/global_environment.h +++ b/src/cobalt/script/global_environment.h
@@ -19,6 +19,7 @@ #include <vector> #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/optional.h" #include "cobalt/script/error_report.h" #include "cobalt/script/script_value.h" @@ -35,7 +36,8 @@ class SourceCode; // Manages a handle to a JavaScript engine's global object. -class GlobalEnvironment : public base::RefCounted<GlobalEnvironment> { +class GlobalEnvironment : public base::RefCounted<GlobalEnvironment>, + public base::SupportsWeakPtr<GlobalEnvironment> { public: typedef base::Callback<bool(const ErrorReport& error_report)> ReportErrorCallback; @@ -126,17 +128,19 @@ public: ScopedPreventGarbageCollection(GlobalEnvironment* global_environment, Wrappable* wrappable) - : global_environment(global_environment), wrappable(wrappable) { + : global_environment(global_environment->AsWeakPtr()), wrappable(wrappable) { global_environment->PreventGarbageCollection( base::WrapRefCounted(wrappable)); } ~ScopedPreventGarbageCollection() { - global_environment->AllowGarbageCollection(wrappable); + if (global_environment) { + global_environment->AllowGarbageCollection(wrappable); + } } private: - GlobalEnvironment* global_environment; + base::WeakPtr<GlobalEnvironment> global_environment; Wrappable* wrappable; };
diff --git a/src/cobalt/version.h b/src/cobalt/version.h index 98d2e93..b7fa7b2 100644 --- a/src/cobalt/version.h +++ b/src/cobalt/version.h
@@ -35,6 +35,6 @@ // release is cut. //. -#define COBALT_VERSION "20.lts.3" +#define COBALT_VERSION "20.lts.4" #endif // COBALT_VERSION_H_
diff --git a/src/starboard/android/apk/app/build.gradle b/src/starboard/android/apk/app/build.gradle index ad09589..231fbb9 100644 --- a/src/starboard/android/apk/app/build.gradle +++ b/src/starboard/android/apk/app/build.gradle
@@ -33,8 +33,8 @@ println "TARGET: ${cobaltTarget}" android { - compileSdkVersion 28 - buildToolsVersion "28.0.3" + compileSdkVersion 29 + buildToolsVersion "29.0.2" signingConfigs { // A signing config that matches what is implicitly used for the "debug" build type. @@ -48,7 +48,7 @@ defaultConfig { applicationId "dev.cobalt.coat" minSdkVersion 21 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 1 versionName "${buildId}" manifestPlaceholders = [applicationName: "CoAT: ${cobaltTarget}"] @@ -130,10 +130,10 @@ externalNativeBuild { cmake { path 'CMakeLists.txt' - if (project.hasProperty('cobaltGradleDir')) { - // Resolve relative to the current dir at config time by getting a canonical File. - buildStagingDirectory new File(cobaltGradleDir, 'externalNativeBuild').canonicalFile - } + // Move the staging directory to be a sibling of the build directory, which we moved + // in the top-level build.gradle to be in a common top-level 'build' directory. Get + // the canonical file at config time so that it's still right at build time. + buildStagingDirectory new File("${buildDir}.cxx").canonicalFile } } } @@ -154,9 +154,9 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:support-annotations:28.0.0' - implementation 'com.android.support:leanback-v17:28.0.0' - implementation 'com.android.support:support-v4:28.0.0' - implementation 'com.google.android.gms:play-services-auth:16.0.1' + implementation 'androidx.annotation:annotation:1.1.0' + implementation 'androidx.leanback:leanback:1.0.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'com.google.android.gms:play-services-auth:17.0.0' implementation 'com.google.protobuf:protobuf-lite:3.0.1' }
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizerImpl.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizerImpl.java index 136da8b..1902635 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizerImpl.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizerImpl.java
@@ -27,10 +27,10 @@ import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.widget.Toast; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import com.google.android.gms.auth.GoogleAuthException; import com.google.android.gms.auth.GoogleAuthUtil; import com.google.android.gms.auth.UserRecoverableAuthException;
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/AudioPermissionRequester.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/AudioPermissionRequester.java index f8640d8..59ae6ad 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/AudioPermissionRequester.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/AudioPermissionRequester.java
@@ -18,8 +18,8 @@ import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import dev.cobalt.util.Holder; import dev.cobalt.util.UsedByNative;
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltA11yHelper.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltA11yHelper.java index 8c07518..b0fcb50 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltA11yHelper.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltA11yHelper.java
@@ -19,11 +19,11 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; -import android.support.v4.view.ViewCompat; -import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; -import android.support.v4.widget.ExploreByTouchHelper; import android.view.View; import android.view.accessibility.AccessibilityEvent; +import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.customview.widget.ExploreByTouchHelper; import dev.cobalt.util.Log; import java.util.BitSet; import java.util.List; @@ -45,11 +45,19 @@ private static final int FAKE_VIEW_HEIGHT = 10; private static final int FAKE_VIEW_WIDTH = 10; - private int previousFocusedViewId = 1; + private static final int CENTER_VIEW_ID = 5; + private static final int UP_VIEW_ID = 2; + private static final int DOWN_VIEW_ID = 8; + private static final int LEFT_VIEW_ID = 4; + private static final int RIGHT_VIEW_ID = 6; + + private static final int INPUT_FOCUS_CHANGE_DELAY = 1500; // milliseconds + // This set tracks whether onPopulateNodeForVirtualView has been // called for each virtual view id. private final BitSet nodePopulatedSet = new BitSet(9); private final Handler handler = new Handler(); + private boolean unhandledInput; private boolean hasInitialFocusBeenSet; public CobaltA11yHelper(View view) { @@ -77,70 +85,53 @@ } } - /** - * Returns the "patch number" for a given view id, given a focused view id. - * - * <p>A "patch number" is a 1-9 number that describes where the requestedViewId is now located on - * an X-Y grid, given the focusedViewId. - * - * <p>Patch number grid: - * (0,0)----->X - * |+-+-+-+ - * ||1|2|3| - * |+-+-+-| - * ||4|5|6| - * |+-+-+-| - * ||7|8|9| - * |+-+-+-+ - * \./ Y - * - * <p>As focus changes, the locations of the views are moved so the focused view is always in the - * middle (patch number 5) and all of the other views always in the same relative position with - * respect to each other (with those on the edges adjacent to those on the opposite edges -- - * wrapping around). - * - * <p>5 is returned whenever focusedViewId = requestedViewId - */ - private static int getPatchNumber(int focusedViewId, int requestedViewId) { - // The (x,y) the focused view has in the 9 patch where 5 is in the middle. - int focusedX = (focusedViewId - 1) % 3; - int focusedY = (focusedViewId - 1) / 3; + private void focusOnCenter() { + // Setting Accessibility focus to CENTER_VIEW_ID will make TalkBack focus + // on CENTER_VIEW_ID immediately, but the actual mouse focus is either + // unchanged or return INVALID_ID. + handler.post( + new Runnable() { + @Override + public void run() { + sendEventForVirtualView( + CENTER_VIEW_ID, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + } + }); - // x and y offsets of focused view where middle is (0, 0) - int focusedRelativeToCenterX = focusedX - 1; - int focusedRelativeToCenterY = focusedY - 1; - - // The (x,y) the requested view has in the 9 patch where 5 is in the middle. - int requestedX = (requestedViewId - 1) % 3; - int requestedY = (requestedViewId - 1) / 3; - - // x and y offsets of requested view where middle is (0, 0) - int requestedRelativeToCenterX = requestedX - 1; - int requestedRelativeToCenterY = requestedY - 1; - - // The (x,y) that the requested view has in the 9 patch when focusedViewId - // is in the middle. - int translatedRequestedX = (1 + 3 + requestedRelativeToCenterX - focusedRelativeToCenterX) % 3; - int translatedRequestedY = (1 + 3 + requestedRelativeToCenterY - focusedRelativeToCenterY) % 3; - - return (translatedRequestedY * 3) + translatedRequestedX + 1; + // There is a knwon Android bug about setting focus too early + // taking no effect. The impact for Cobalt is that sometimes after + // we click on a video, TalkBack sees nothing in focus in the watch + // page if no user input happens. To avoid this bug we have to + // delay the focus long enough for all the TalkBack movements to settle + // down. More details here: https://stackoverflow.com/questions/28472985. + handler.postDelayed( + new Runnable() { + @Override + public void run() { + sendEventForVirtualView( + CENTER_VIEW_ID, AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + }, INPUT_FOCUS_CHANGE_DELAY); } private void maybeInjectEvent(int currentFocusedViewId) { - switch (getPatchNumber(previousFocusedViewId, currentFocusedViewId)) { - case 5: + if (!unhandledInput) { + return; + } + switch (currentFocusedViewId) { + case CENTER_VIEW_ID: // no move; break; - case 2: + case UP_VIEW_ID: nativeInjectKeyEvent(SB_KEY_GAMEPAD_DPAD_UP); break; - case 4: + case LEFT_VIEW_ID: nativeInjectKeyEvent(SB_KEY_GAMEPAD_DPAD_LEFT); break; - case 6: + case RIGHT_VIEW_ID: nativeInjectKeyEvent(SB_KEY_GAMEPAD_DPAD_RIGHT); break; - case 8: + case DOWN_VIEW_ID: nativeInjectKeyEvent(SB_KEY_GAMEPAD_DPAD_DOWN); break; default: @@ -148,9 +139,26 @@ // not possible to reach this. break; } - previousFocusedViewId = currentFocusedViewId; + unhandledInput = false; + focusOnCenter(); } + /** + * <p>Fake number grid: + * |+-+-+-+ + * ||1|2|3| + * |+-+-+-| + * ||4|5|6| + * |+-+-+-| + * ||7|8|9| + * |+-+-+-+ + * + * <p>The focus always starts from the middle number 5. When user changes + * focus, the focus is then moved to either 2, 4, 6 or 8 and we can capture + * the movement this way. The focus is then quickly switched back to the + * center 5 to be ready for the next movement. + * + */ @Override protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfoCompat node) { int focusedViewId = getAccessibilityFocusedVirtualViewId(); @@ -158,18 +166,20 @@ if (focusedViewId < 1 || focusedViewId > 9) { // If this is not one of our nine-patch views, it's probably HOST_ID // In any case, assume there is no focus change. - focusedViewId = previousFocusedViewId; + focusedViewId = CENTER_VIEW_ID; } // onPopulateNodeForVirtualView() gets called at least once every // time the focused view changes. So see if it's changed since the // last time we've been called and inject an event if so. - maybeInjectEvent(focusedViewId); + if (focusedViewId != CENTER_VIEW_ID) { + maybeInjectEvent(focusedViewId); + } else { + unhandledInput = true; + } - int patchNumber = getPatchNumber(focusedViewId, virtualViewId); - - int x = (patchNumber - 1) % 3; - int y = (patchNumber - 1) / 3; + int x = (virtualViewId - 1) % 3; + int y = (virtualViewId - 1) / 3; // Note that the specific bounds here are arbitrary. The importance // is the relative bounds to each other. @@ -188,14 +198,7 @@ // but not before, ask that the accessibility focus be moved from // it's initial position on HOST_ID to the one we want to start with. hasInitialFocusBeenSet = true; - handler.post( - new Runnable() { - @Override - public void run() { - sendEventForVirtualView( - previousFocusedViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); - } - }); + focusOnCenter(); } } @@ -203,59 +206,4 @@ protected boolean onPerformActionForVirtualView(int virtualViewId, int action, Bundle arguments) { return false; } - - /** A simple equivilent to Assert.assertEquals so we don't depend on junit */ - private static void assertEquals(int expected, int actual) { - if (expected != actual) { - throw new RuntimeException("Expected " + expected + " actual " + actual); - } - } - - /** - * Unit test for getPatchNumber(). - * - * <p>As of this writing, the Java portion of the Cobalt build has no unit test mechanism. - * - * <p>To run this test, simply call it from application start and start the application. - * - * <p>TODO: Move this to a real unit test location when one exists. - */ - private static void testGetPatchNumber() { - Log.i(TAG, "+testGetPatchNumber"); - - assertEquals(1, getPatchNumber(5, 1)); - assertEquals(2, getPatchNumber(5, 2)); - assertEquals(3, getPatchNumber(5, 3)); - assertEquals(4, getPatchNumber(5, 4)); - assertEquals(5, getPatchNumber(5, 5)); - assertEquals(6, getPatchNumber(5, 6)); - assertEquals(7, getPatchNumber(5, 7)); - assertEquals(8, getPatchNumber(5, 8)); - assertEquals(9, getPatchNumber(5, 9)); - - for (int i = 1; i <= 9; i++) { - assertEquals(5, getPatchNumber(i, i)); - } - - assertEquals(5, getPatchNumber(1, 1)); - assertEquals(6, getPatchNumber(1, 2)); - assertEquals(4, getPatchNumber(1, 3)); - assertEquals(8, getPatchNumber(1, 4)); - assertEquals(9, getPatchNumber(1, 5)); - assertEquals(7, getPatchNumber(1, 6)); - assertEquals(2, getPatchNumber(1, 7)); - assertEquals(3, getPatchNumber(1, 8)); - assertEquals(1, getPatchNumber(1, 9)); - - assertEquals(9, getPatchNumber(9, 1)); - assertEquals(7, getPatchNumber(9, 2)); - assertEquals(8, getPatchNumber(9, 3)); - assertEquals(3, getPatchNumber(9, 4)); - assertEquals(1, getPatchNumber(9, 5)); - assertEquals(2, getPatchNumber(9, 6)); - assertEquals(6, getPatchNumber(9, 7)); - assertEquals(4, getPatchNumber(9, 8)); - assertEquals(5, getPatchNumber(9, 9)); - Log.i(TAG, "-testGetPatchNumber"); - } }
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/PlatformError.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/PlatformError.java index db0cae7..999b349 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/PlatformError.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/PlatformError.java
@@ -23,7 +23,7 @@ import android.os.Handler; import android.os.Looper; import android.provider.Settings; -import android.support.annotation.IntDef; +import androidx.annotation.IntDef; import dev.cobalt.util.Holder; import dev.cobalt.util.Log; import java.lang.annotation.Retention;
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java index 85c7c56..ee77d2d 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -18,7 +18,6 @@ import static android.media.AudioManager.GET_DEVICES_INPUTS; import static dev.cobalt.util.Log.TAG; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -34,6 +33,7 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.CaptioningManager; +import androidx.annotation.RequiresApi; import dev.cobalt.account.UserAuthorizer; import dev.cobalt.feedback.FeedbackService; import dev.cobalt.media.AudioOutputManager; @@ -146,7 +146,7 @@ @SuppressWarnings("unused") @UsedByNative - void beforeStartOrResume() { + protected void beforeStartOrResume() { Log.i(TAG, "Prepare to resume"); // Bring our platform services to life before resuming so that they're ready to deal with // whatever the web app wants to do with them as part of its start/resume logic. @@ -159,7 +159,7 @@ @SuppressWarnings("unused") @UsedByNative - void beforeSuspend() { + protected void beforeSuspend() { Log.i(TAG, "Prepare to suspend"); // We want the MediaSession to be deactivated immediately before suspending so that by the time // the launcher is visible our "Now Playing" card is already gone. Then Cobalt and the web app @@ -173,7 +173,7 @@ @SuppressWarnings("unused") @UsedByNative - void afterStopped() { + protected void afterStopped() { starboardStopped = true; ttsHelper.shutdown(); userAuthorizer.shutdown(); @@ -326,7 +326,7 @@ } } - @TargetApi(23) + @RequiresApi(23) private boolean isMicrophoneConnectedV23() { // A check specifically for microphones is not available before API 28, so it is assumed that a // connected input audio device is a microphone. @@ -430,7 +430,7 @@ /** Returns string for kSbSystemPropertyUserAgentAuxField */ @SuppressWarnings("unused") @UsedByNative - String getUserAgentAuxField() { + protected String getUserAgentAuxField() { StringBuilder sb = new StringBuilder(); String packageName = appContext.getApplicationInfo().packageName; @@ -497,7 +497,7 @@ * https://developer.android.com/reference/android/view/Display.HdrCapabilities.html for valid * values. */ - @TargetApi(24) + @RequiresApi(24) @SuppressWarnings("unused") @UsedByNative public boolean isHdrTypeSupported(int hdrType) {
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/ArtworkLoader.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/ArtworkLoader.java index d76266f..8fb4945 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/ArtworkLoader.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/ArtworkLoader.java
@@ -19,9 +19,9 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; -import android.support.annotation.NonNull; import android.util.Pair; import android.util.Size; +import androidx.annotation.NonNull; import dev.cobalt.util.Log; import java.io.IOException; import java.io.InputStream;
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java index 2105e5b..0508e27 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
@@ -16,11 +16,11 @@ import static dev.cobalt.media.Log.TAG; -import android.annotation.TargetApi; import android.content.Context; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.os.Build; +import androidx.annotation.RequiresApi; import dev.cobalt.util.Log; import dev.cobalt.util.UsedByNative; import java.util.ArrayList; @@ -85,7 +85,7 @@ } /** Returns the maximum number of HDMI channels for API 23 and above. */ - @TargetApi(23) + @RequiresApi(23) private int getMaxChannelsV23() { AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); AudioDeviceInfo[] deviceInfos = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java index ec1fd88..419f5d8 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
@@ -16,13 +16,13 @@ import static dev.cobalt.media.Log.TAG; -import android.annotation.TargetApi; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTimestamp; import android.media.AudioTrack; import android.os.Build; +import androidx.annotation.RequiresApi; import dev.cobalt.util.Log; import dev.cobalt.util.UsedByNative; import java.nio.ByteBuffer; @@ -213,7 +213,7 @@ return 0; } - @TargetApi(24) + @RequiresApi(24) private int getUnderrunCountV24() { if (audioTrack == null) { Log.e(TAG, "Unable to call getUnderrunCount() with NULL audio track.");
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java index ae511c9..2bfce23 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
@@ -16,7 +16,6 @@ import static dev.cobalt.media.Log.TAG; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; @@ -31,6 +30,7 @@ import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.view.WindowManager; +import androidx.annotation.RequiresApi; import dev.cobalt.util.DisplayUtil; import dev.cobalt.util.Holder; import dev.cobalt.util.Log; @@ -262,7 +262,7 @@ .requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); } - @TargetApi(26) + @RequiresApi(26) private int requestAudioFocusV26() { if (audioFocusRequest == null) { AudioAttributes audioAtrributes = @@ -281,7 +281,7 @@ getAudioManager().abandonAudioFocus(this); } - @TargetApi(26) + @RequiresApi(26) private void abandonAudioFocusV26() { if (audioFocusRequest != null) { getAudioManager().abandonAudioFocusRequest(audioFocusRequest);
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java index 1c597b2..064fc65 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -327,6 +327,7 @@ hdrStaticInfo.putShort((short) (minMasteringLuminance + 0.5f)); hdrStaticInfo.putShort((short) DEFAULT_MAX_CLL); hdrStaticInfo.putShort((short) DEFAULT_MAX_FALL); + hdrStaticInfo.rewind(); this.hdrStaticInfo = hdrStaticInfo; } } @@ -495,7 +496,7 @@ boolean shouldConfigureHdr = android.os.Build.VERSION.SDK_INT >= 24 && colorInfo != null - && MediaCodecUtil.isHdrCapableVp9Decoder(findVideoDecoderResult); + && MediaCodecUtil.isHdrCapableVideoDecoder(mime, findVideoDecoderResult); if (shouldConfigureHdr) { Log.d(TAG, "Setting HDR info."); mediaFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer);
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java index b7fdad8..8991611 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
@@ -44,6 +44,7 @@ private static boolean isVp9WhiteListed; private static final String SECURE_DECODER_SUFFIX = ".secure"; 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 @@ -392,7 +393,7 @@ FindVideoDecoderResult findVideoDecoderResult = findVideoDecoder(mimeType, secure, frameWidth, frameHeight, bitrate, fps, mustSupportHdr); return !findVideoDecoderResult.name.equals("") - && (!mustSupportHdr || isHdrCapableVp9Decoder(findVideoDecoderResult)); + && (!mustSupportHdr || isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult)); } /** @@ -405,23 +406,32 @@ return !findAudioDecoder(mimeType, bitrate).equals(""); } - /** Determine whether the system has a decoder capable of playing HDR VP9. */ + /** + * Determine whether the system has a decoder capable of playing HDR. Currently VP9 and AV1 are + * HDR supported codecs + */ @SuppressWarnings("unused") @UsedByNative - public static boolean hasHdrCapableVp9Decoder() { + public static boolean hasHdrCapableVideoDecoder(String mimeType) { // 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) { return false; } + // AV1ProfileMain10HDR10 value was not added until API level 29. See + // https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel.html. + if (mimeType.equals(AV1_MIME_TYPE) && Build.VERSION.SDK_INT < 29) { + return false; + } FindVideoDecoderResult findVideoDecoderResult = - findVideoDecoder(VP9_MIME_TYPE, false, 0, 0, 0, 0, true); - return isHdrCapableVp9Decoder(findVideoDecoderResult); + findVideoDecoder(mimeType, false, 0, 0, 0, 0, true); + return isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult); } - /** Determine whether findVideoDecoderResult is capable of playing HDR VP9 */ - public static boolean isHdrCapableVp9Decoder(FindVideoDecoderResult findVideoDecoderResult) { + /** Determine whether findVideoDecoderResult is capable of playing HDR */ + public static boolean isHdrCapableVideoDecoder( + String mimeType, FindVideoDecoderResult findVideoDecoderResult) { CodecCapabilities codecCapabilities = findVideoDecoderResult.codecCapabilities; if (codecCapabilities == null) { return false; @@ -431,9 +441,15 @@ return false; } for (CodecProfileLevel codecProfileLevel : codecProfileLevels) { - if (codecProfileLevel.profile == CodecProfileLevel.VP9Profile2HDR - || codecProfileLevel.profile == CodecProfileLevel.VP9Profile3HDR) { - return true; + if (mimeType.equals(VP9_MIME_TYPE)) { + if (codecProfileLevel.profile == CodecProfileLevel.VP9Profile2HDR + || codecProfileLevel.profile == CodecProfileLevel.VP9Profile3HDR) { + return true; + } + } else if (mimeType.equals(AV1_MIME_TYPE)) { + if (codecProfileLevel.profile == CodecProfileLevel.AV1ProfileMain10HDR10) { + return true; + } } } @@ -569,7 +585,8 @@ : name; FindVideoDecoderResult findVideoDecoderResult = new FindVideoDecoderResult(resultName, videoCapabilities, codecCapabilities); - if (hdr && !isHdrCapableVp9Decoder(findVideoDecoderResult)) { + if (hdr && !isHdrCapableVideoDecoder(mimeType, findVideoDecoderResult)) { + Log.v(TAG, String.format("Rejecting %s, reason: codec does not support HDR", name)); continue; } Log.v(TAG, String.format("Found suitable decoder, %s", name));
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java index 24fe96f..3af5ad5 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
@@ -20,7 +20,6 @@ import static dev.cobalt.media.Log.TAG; -import android.annotation.TargetApi; import android.media.DeniedByServerException; import android.media.MediaCrypto; import android.media.MediaCryptoException; @@ -30,6 +29,7 @@ import android.media.NotProvisionedException; import android.media.UnsupportedSchemeException; import android.os.Build; +import androidx.annotation.RequiresApi; import dev.cobalt.coat.CobaltHttpHelper; import dev.cobalt.util.Log; import dev.cobalt.util.UsedByNative; @@ -429,7 +429,7 @@ mMediaDrm.setPropertyString("sessionSharing", "enable"); } - @TargetApi(23) + @RequiresApi(23) private void setOnKeyStatusChangeListenerV23() { mMediaDrm.setOnKeyStatusChangeListener( new MediaDrm.OnKeyStatusChangeListener() { @@ -546,7 +546,12 @@ Log.e(TAG, "Failed to provision device during MediaCrypto creation."); return false; } - return true; + try { + mMediaCryptoSession = openSession(); + } catch (NotProvisionedException e2) { + Log.e(TAG, "Device still not provisioned after supposedly successful provisioning", e2); + return false; + } } if (mMediaCryptoSession == null) {
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java index 4287c8c..0274811 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
@@ -29,7 +29,6 @@ package dev.cobalt.media; -import android.annotation.TargetApi; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; @@ -37,10 +36,11 @@ import android.view.Choreographer; import android.view.Choreographer.FrameCallback; import android.view.WindowManager; +import androidx.annotation.RequiresApi; import dev.cobalt.util.UsedByNative; /** Makes a best effort to adjust frame release timestamps for a smoother visual result. */ -@TargetApi(16) +@RequiresApi(16) @SuppressWarnings("unused") @UsedByNative public final class VideoFrameReleaseTimeHelper {
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/storage/CobaltStorageLoader.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/storage/CobaltStorageLoader.java index c471b0c..680445f 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/storage/CobaltStorageLoader.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/storage/CobaltStorageLoader.java
@@ -17,7 +17,7 @@ import static dev.cobalt.util.Log.TAG; import android.os.FileObserver; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.google.protobuf.InvalidProtocolBufferException; import dev.cobalt.storage.StorageProto.Storage; import dev.cobalt.util.Log;
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/Holder.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/Holder.java index 7fa3407..ad48bbd 100644 --- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/Holder.java +++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/Holder.java
@@ -14,7 +14,7 @@ package dev.cobalt.util; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; /** Holds a mutable reference to an object, or null. */ public class Holder<T> {
diff --git a/src/starboard/android/apk/build.gradle b/src/starboard/android/apk/build.gradle index b50ae22..525c56b 100644 --- a/src/starboard/android/apk/build.gradle +++ b/src/starboard/android/apk/build.gradle
@@ -20,7 +20,7 @@ jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.5.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -53,3 +53,10 @@ // 'buildDir' which can end up putting it at the wrong depth in the file system. def rootBuildDir = hasProperty('cobaltGradleDir') ? new File(cobaltGradleDir, 'build') : buildDir allprojects { buildDir = new File(rootBuildDir, project.name).canonicalFile } + +// Android Studio Gradle plugin 3.5+ builds all supported ABIs for a connected device. Override the +// property it sets to build just the first one, which is the preferred ABI for the device. +if (hasProperty('android.injected.build.abi')) { + def firstAbi = properties.get('android.injected.build.abi').split(',').first() + allprojects { setProperty('android.injected.build.abi', firstAbi) } +}
diff --git a/src/starboard/android/apk/build.id b/src/starboard/android/apk/build.id new file mode 100644 index 0000000..2a2074e --- /dev/null +++ b/src/starboard/android/apk/build.id
@@ -0,0 +1 @@ +263411 \ No newline at end of file
diff --git a/src/starboard/android/apk/cobalt-gradle.sh b/src/starboard/android/apk/cobalt-gradle.sh index d135b4b..4a37c4f 100755 --- a/src/starboard/android/apk/cobalt-gradle.sh +++ b/src/starboard/android/apk/cobalt-gradle.sh
@@ -22,7 +22,6 @@ while [ "$1" ]; do case "$1" in --sdk) shift; ANDROID_HOME="$1" ;; - --ndk) shift; ANDROID_NDK_HOME="$1" ;; --cache) shift; mkdir -p "$1"; GRADLE_ARGS+=("--project-cache-dir" $(cd "$1"; pwd)) ;; --reset) RESET_GRADLE=1 ;; @@ -50,9 +49,7 @@ fi export ANDROID_HOME -export ANDROID_NDK_HOME echo "ANDROID_HOME=${ANDROID_HOME}" -echo "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" echo "TASK: ${GRADLE_ARGS[-1]}" # Allow parallel gradle builds, as defined by a COBALT_GRADLE_BUILD_COUNT envvar
diff --git a/src/starboard/android/apk/gradle.properties b/src/starboard/android/apk/gradle.properties index 2d5bfe3..d9f30ec 100644 --- a/src/starboard/android/apk/gradle.properties +++ b/src/starboard/android/apk/gradle.properties
@@ -23,6 +23,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx4g # When configured, Gradle will run in incubating parallel mode.
diff --git a/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties b/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties index b17c1fc..cb820ac 100644 --- a/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties +++ b/src/starboard/android/apk/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@ -#Tue Jan 29 11:03:00 PST 2019 +#Thu Jan 02 11:15:25 PST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/src/starboard/android/shared/application_android.cc b/src/starboard/android/shared/application_android.cc index cae801e..24b5517 100644 --- a/src/starboard/android/shared/application_android.cc +++ b/src/starboard/android/shared/application_android.cc
@@ -362,7 +362,6 @@ } void ApplicationAndroid::ProcessAndroidInput() { - SB_DCHECK(input_events_generator_); AInputEvent* android_event = NULL; while (AInputQueue_getEvent(input_queue_, &android_event) >= 0) { SB_LOG(INFO) << "Android input: type=" @@ -370,6 +369,11 @@ if (AInputQueue_preDispatchEvent(input_queue_, android_event)) { continue; } + if (!input_events_generator_) { + SB_DLOG(WARNING) << "Android input event ignored without an SbWindow."; + AInputQueue_finishEvent(input_queue_, android_event, false); + continue; + } InputEventsGenerator::Events app_events; bool handled = input_events_generator_->CreateInputEventsFromAndroidEvent( android_event, &app_events); @@ -385,7 +389,10 @@ int err = read(keyboard_inject_readfd_, &key, sizeof(key)); SB_DCHECK(err >= 0) << "Keyboard inject read failed: errno=" << errno; SB_LOG(INFO) << "Keyboard inject: " << key; - + if (!input_events_generator_) { + SB_DLOG(WARNING) << "Injected input event ignored without an SbWindow."; + return; + } InputEventsGenerator::Events app_events; input_events_generator_->CreateInputEventsFromSbKey(key, &app_events); for (int i = 0; i < app_events.size(); ++i) {
diff --git a/src/starboard/android/shared/cobalt/android_media_session_client.cc b/src/starboard/android/shared/cobalt/android_media_session_client.cc index 1861af9..30ec630 100644 --- a/src/starboard/android/shared/cobalt/android_media_session_client.cc +++ b/src/starboard/android/shared/cobalt/android_media_session_client.cc
@@ -289,12 +289,16 @@ } } - jlong duration = session_state.duration(); - // Set duration to negative if duration is unknown or infinite, as with live - // playback. - // https://developer.android.com/reference/android/support/v4/media/MediaMetadataCompat#METADATA_KEY_DURATION - if (duration == kSbTimeMax) { - duration = -1; + jlong durationInMilliseconds; + if (session_state.duration() == kSbTimeMax) { + // Set duration to negative if duration is unknown or infinite, as with live + // playback. + // https://developer.android.com/reference/android/support/v4/media/MediaMetadataCompat#METADATA_KEY_DURATION + durationInMilliseconds = -1; + } else { + // SbTime is measured in microseconds while Android MediaSession expects + // duration in milliseconds. + durationInMilliseconds = session_state.duration() / kSbTimeMillisecond; } env->CallStarboardVoidMethodOrAbort( @@ -305,7 +309,7 @@ session_state.current_playback_position() / kSbTimeMillisecond, static_cast<jfloat>(session_state.actual_playback_rate()), j_title.Get(), j_artist.Get(), j_album.Get(), j_artwork.Get(), - duration); + durationInMilliseconds); } };
diff --git a/src/starboard/android/shared/cobalt/configuration.py b/src/starboard/android/shared/cobalt/configuration.py index 3633045..8fac1a4 100644 --- a/src/starboard/android/shared/cobalt/configuration.py +++ b/src/starboard/android/shared/cobalt/configuration.py
@@ -60,6 +60,7 @@ # Disabled because of: Fail (Tests do not account for player with url?). ('csp/WebPlatformTest.Run/' 'content_security_policy_media_src_media_src_allowed_html'), + ('websockets/WebPlatformTest.Run/websockets_*'), ] return filters
diff --git a/src/starboard/android/shared/configuration_public.h b/src/starboard/android/shared/configuration_public.h index 8236b6e..33e0714 100644 --- a/src/starboard/android/shared/configuration_public.h +++ b/src/starboard/android/shared/configuration_public.h
@@ -330,6 +330,11 @@ // stack size for media stack threads. #define SB_MEDIA_THREAD_STACK_SIZE 0U +// Specifies whether this platform support the improved player creation and +// output mode query. Please see comments of SbPlayerCreate() and +// SbPlayerGetPreferredOutputMode() in player.h for more details. +#define SB_HAS_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT 1 + // --- Decoder-only Params --- // Specifies how media buffers must be aligned on this platform as some
diff --git a/src/starboard/android/shared/log.cc b/src/starboard/android/shared/log.cc index 35ed73d..6cbc17f 100644 --- a/src/starboard/android/shared/log.cc +++ b/src/starboard/android/shared/log.cc
@@ -25,11 +25,8 @@ #include "starboard/common/log.h" #include "starboard/common/string.h" #include "starboard/configuration.h" -#include "starboard/thread.h" - -#if SB_API_VERSION >= 11 #include "starboard/shared/starboard/log_mutex.h" -#endif // SB_API_VERSION >= 11 +#include "starboard/thread.h" using starboard::android::shared::JniEnvExt; using starboard::android::shared::ScopedLocalJavaRef;
diff --git a/src/starboard/android/shared/media_decoder.cc b/src/starboard/android/shared/media_decoder.cc index 482b6a8..f1a3dfb 100644 --- a/src/starboard/android/shared/media_decoder.cc +++ b/src/starboard/android/shared/media_decoder.cc
@@ -18,11 +18,9 @@ #include "starboard/android/shared/jni_utils.h" #include "starboard/android/shared/media_common.h" #include "starboard/audio_sink.h" -#if SB_API_VERSION >= 11 -#include "starboard/format_string.h" -#endif // SB_API_VERSION >= 11 #include "starboard/common/log.h" #include "starboard/common/string.h" +#include "starboard/format_string.h" #include "starboard/shared/pthread/thread_create_priority.h" namespace starboard {
diff --git a/src/starboard/android/shared/media_is_video_supported.cc b/src/starboard/android/shared/media_is_video_supported.cc index 239130a..d5260c9 100644 --- a/src/starboard/android/shared/media_is_video_supported.cc +++ b/src/starboard/android/shared/media_is_video_supported.cc
@@ -33,15 +33,21 @@ const jint HDR_TYPE_HDR10 = 2; const jint HDR_TYPE_HLG = 3; -bool IsHDRTransferCharacteristicsSupported(SbMediaTransferId transfer_id) { - SB_DCHECK(transfer_id != kSbMediaTransferIdBt709 && - transfer_id != kSbMediaTransferIdUnspecified); - // An HDR capable VP9 decoder is needed to handle HDR at all. - bool has_hdr_capable_vp9_decoder = +bool IsHDRTransferCharacteristicsSupported(SbMediaVideoCodec video_codec, + SbMediaTransferId transfer_id) { + const char* mime = SupportedVideoCodecToMimeType(video_codec); + if (!mime) { + return false; + } + JniEnvExt* env = JniEnvExt::Get(); + + // An HDR capable VP9 or AV1 decoder is needed to handle HDR at all. + bool has_hdr_capable_decoder = JniEnvExt::Get()->CallStaticBooleanMethodOrAbort( - "dev/cobalt/media/MediaCodecUtil", "hasHdrCapableVp9Decoder", - "()Z") == JNI_TRUE; - if (!has_hdr_capable_vp9_decoder) { + "dev/cobalt/media/MediaCodecUtil", "hasHdrCapableVideoDecoder", + "(Ljava/lang/String;)Z", + env->NewStringStandardUTFOrAbort(mime)) == JNI_TRUE; + if (!has_hdr_capable_decoder) { return false; } @@ -75,7 +81,7 @@ int fps, bool decode_to_texture_required) { if (!IsSDRVideo(bit_depth, primary_id, transfer_id, matrix_id)) { - if (!IsHDRTransferCharacteristicsSupported(transfer_id)) { + if (!IsHDRTransferCharacteristicsSupported(video_codec, transfer_id)) { return false; } }
diff --git a/src/starboard/android/shared/platform_deploy.gypi b/src/starboard/android/shared/platform_deploy.gypi index 3d6e60f..ecc7699 100644 --- a/src/starboard/android/shared/platform_deploy.gypi +++ b/src/starboard/android/shared/platform_deploy.gypi
@@ -40,14 +40,13 @@ 'action': [ '<(DEPTH)/starboard/android/apk/cobalt-gradle.sh', '--sdk', '<(ANDROID_HOME)', - '--ndk', '<(NDK_HOME)', - '--cache', '<(GRADLE_FILES_DIR)/cache', - '--project-dir', '<(DEPTH)/starboard/android/apk/', + '--cache', '$${PWD}/<(GRADLE_FILES_DIR)/cache', + '--project-dir', '$${PWD}/<(DEPTH)/starboard/android/apk/', '-P', 'cobaltBuildAbi=<(ANDROID_ABI)', - '-P', 'cobaltDeployApk=<(apk)', - '-P', 'cobaltContentDir=<(content_deploy_dir)', - '-P', 'cobaltGradleDir=<(GRADLE_FILES_DIR)', - '-P', 'cobaltProductDir=<(PRODUCT_DIR)', + '-P', 'cobaltDeployApk=$${PWD}/<(apk)', + '-P', 'cobaltContentDir=$${PWD}/<(content_deploy_dir)', + '-P', 'cobaltGradleDir=$${PWD}/<(GRADLE_FILES_DIR)', + '-P', 'cobaltProductDir=$${PWD}/<(PRODUCT_DIR)', '-P', 'cobaltTarget=<(executable_name)', 'assembleCobalt_<(GRADLE_BUILD_TYPE)', ],
diff --git a/src/starboard/android/shared/player_create.cc b/src/starboard/android/shared/player_create.cc index 7edd8d6..142eaab 100644 --- a/src/starboard/android/shared/player_create.cc +++ b/src/starboard/android/shared/player_create.cc
@@ -30,27 +30,38 @@ UpdateActiveSessionPlatformPlaybackState; SbPlayer SbPlayerCreate(SbWindow window, - SbMediaVideoCodec video_codec, - SbMediaAudioCodec audio_codec, -#if SB_API_VERSION < 10 - SbMediaTime duration_pts, -#endif // SB_API_VERSION < 10 - SbDrmSystem drm_system, - const SbMediaAudioSampleInfo* audio_sample_info, - const char* max_video_capabilities, + const SbPlayerCreationParam* creation_param, SbPlayerDeallocateSampleFunc sample_deallocate_func, SbPlayerDecoderStatusFunc decoder_status_func, SbPlayerStatusFunc player_status_func, SbPlayerErrorFunc player_error_func, void* context, - SbPlayerOutputMode output_mode, SbDecodeTargetGraphicsContextProvider* provider) { SB_UNREFERENCED_PARAMETER(window); - SB_UNREFERENCED_PARAMETER(max_video_capabilities); - SB_UNREFERENCED_PARAMETER(provider); -#if SB_API_VERSION < 10 - SB_UNREFERENCED_PARAMETER(duration_pts); -#endif // SB_API_VERSION < 10 + + if (!creation_param) { + SB_LOG(ERROR) << "CreationParam cannot be null."; + return kSbPlayerInvalid; + } + + if (!creation_param->audio_mime) { + SB_LOG(ERROR) << "creation_param->audio_mime cannot be null."; + return kSbPlayerInvalid; + } + if (!creation_param->video_mime) { + SB_LOG(ERROR) << "creation_param->video_mime cannot be null."; + return kSbPlayerInvalid; + } + if (!creation_param->max_video_capabilities) { + SB_LOG(ERROR) << "creation_param->max_video_capabilities cannot be null."; + return kSbPlayerInvalid; + } + + SB_LOG(INFO) << "SbPlayerCreate() called with audio mime \"" + << creation_param->audio_mime << "\", video mime \"" + << creation_param->video_mime + << "\", and max video capabilities \"" + << creation_param->max_video_capabilities << "\"."; if (!sample_deallocate_func || !decoder_status_func || !player_status_func #if SB_HAS(PLAYER_ERROR_MESSAGE) @@ -60,6 +71,9 @@ return kSbPlayerInvalid; } + auto audio_codec = creation_param->audio_sample_info.codec; + auto video_codec = creation_param->video_sample_info.codec; + if (audio_codec != kSbMediaAudioCodecNone && audio_codec != kSbMediaAudioCodecAac && audio_codec != kSbMediaAudioCodecOpus) { @@ -67,13 +81,6 @@ return kSbPlayerInvalid; } - if (audio_codec == kSbMediaAudioCodecAac && !audio_sample_info) { - SB_LOG(ERROR) - << "SbPlayerCreate() requires a non-NULL SbMediaAudioSampleInfo " - << "when |audio_codec| is not kSbMediaAudioCodecNone"; - return kSbPlayerInvalid; - } - if (video_codec != kSbMediaVideoCodecNone && video_codec != kSbMediaVideoCodecH264 && video_codec != kSbMediaVideoCodecVp9 && @@ -89,7 +96,8 @@ return kSbPlayerInvalid; } - if (!SbPlayerOutputModeSupported(output_mode, video_codec, drm_system)) { + auto output_mode = creation_param->output_mode; + if (SbPlayerGetPreferredOutputMode(creation_param) != output_mode) { SB_LOG(ERROR) << "Unsupported player output mode " << output_mode; return kSbPlayerInvalid; } @@ -103,13 +111,13 @@ UpdateActiveSessionPlatformPlaybackState(kPlaying); starboard::scoped_ptr<PlayerWorker::Handler> handler( - new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system, - audio_sample_info, output_mode, - provider)); + new FilterBasedPlayerWorkerHandler( + video_codec, audio_codec, creation_param->drm_system, + &creation_param->audio_sample_info, output_mode, provider)); SbPlayer player = SbPlayerPrivate::CreateInstance( - audio_codec, video_codec, audio_sample_info, sample_deallocate_func, - decoder_status_func, player_status_func, player_error_func, context, - handler.Pass()); + audio_codec, video_codec, &creation_param->audio_sample_info, + sample_deallocate_func, decoder_status_func, player_status_func, + player_error_func, context, handler.Pass()); // TODO: accomplish this through more direct means. // Set the bounds to initialize the VideoSurfaceView. The initial values don't
diff --git a/src/starboard/android/shared/sdk_utils.py b/src/starboard/android/shared/sdk_utils.py index bc0c11c..2618b8b 100644 --- a/src/starboard/android/shared/sdk_utils.py +++ b/src/starboard/android/shared/sdk_utils.py
@@ -13,36 +13,36 @@ # limitations under the License. """Utilities to use the toolchain from the Android NDK.""" -import ConfigParser +import errno import fcntl -import hashlib import logging import os import re import shutil -import StringIO import subprocess import sys import time -import urllib import zipfile +import requests from starboard.tools import build +# Which version of the Android NDK to install and build with. +_NDK_VERSION = '20.1.5948944' + # Packages to install in the Android SDK. # We download ndk-bundle separately, so it's not in this list. # Get available packages from "sdkmanager --list --verbose" _ANDROID_SDK_PACKAGES = [ - 'build-tools;28.0.3', - 'cmake;3.6.4111459', + 'build-tools;29.0.2', + 'cmake;3.10.2.4988404', 'emulator', 'extras;android;m2repository', 'extras;google;m2repository', - 'lldb;3.1', + 'ndk;' + _NDK_VERSION, 'patcher;v4', - 'platforms;android-28', + 'platforms;android-29', 'platform-tools', - 'tools', ] # Seconds to sleep before writing "y" for android sdk update license prompt. @@ -50,11 +50,11 @@ # Location from which to download the SDK command-line tools # see https://developer.android.com/studio/index.html#command-tools -_SDK_URL = 'https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip' +_SDK_URL = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip' # Location from which to download the Android NDK. # see https://developer.android.com/ndk/downloads (perhaps in "NDK archives") -_NDK_ZIP_REVISION = 'android-ndk-r19c' +_NDK_ZIP_REVISION = 'android-ndk-r20b' _NDK_ZIP_FILE = _NDK_ZIP_REVISION + '-linux-x86_64.zip' _NDK_URL = 'https://dl.google.com/android/repository/' + _NDK_ZIP_FILE @@ -70,44 +70,18 @@ else: _SDK_PATH = _STARBOARD_TOOLCHAINS_SDK_DIR -_ANDROID_NDK_HOME = os.environ.get('ANDROID_NDK_HOME') -if _ANDROID_NDK_HOME: - _NDK_PATH = _ANDROID_NDK_HOME -else: - _NDK_PATH = os.path.join(_SDK_PATH, 'ndk-bundle') +_NDK_PATH = os.path.join(_SDK_PATH, 'ndk', _NDK_VERSION) -_SDKMANAGER_TOOL = os.path.join(_SDK_PATH, 'tools', 'bin', 'sdkmanager') - -# Maps the Android ABI to the architecture name of the toolchain. -_TOOLS_ABI_ARCH_MAP = { - 'x86': 'x86', - 'armeabi': 'arm', - 'armeabi-v7a': 'arm', - 'arm64-v8a': 'arm64', -} - -_SCRIPT_HASH_PROPERTY = 'SdkUtils.Hash' - -with open(__file__, 'rb') as script: - _SCRIPT_HASH = hashlib.md5(script.read()).hexdigest() +_SDKMANAGER_TOOL = os.path.join(_SDK_PATH, 'cmdline-tools', '1.0', 'bin', + 'sdkmanager') -def _CheckStamp(dir_path): - """Checks that the specified directory is up-to-date with the NDK.""" - stamp_path = os.path.join(dir_path, 'ndk.stamp') - return (os.path.exists(stamp_path) and - _ReadNdkRevision(stamp_path) == _GetInstalledNdkRevision() and - _ReadProperty(stamp_path, _SCRIPT_HASH_PROPERTY) == _SCRIPT_HASH) - - -def _UpdateStamp(dir_path): - """Updates the stamp file in the specified directory to the NDK revision.""" - path = GetNdkPath() - properties_path = os.path.join(path, 'source.properties') - stamp_path = os.path.join(dir_path, 'ndk.stamp') - shutil.copyfile(properties_path, stamp_path) - with open(stamp_path, 'a') as stamp: - stamp.write('{} = {}\n'.format(_SCRIPT_HASH_PROPERTY, _SCRIPT_HASH)) +def _MakeDirs(destination_path): + try: + os.makedirs(destination_path) + except OSError as e: + if e.errno != errno.EEXIST: + raise def GetNdkPath(): @@ -118,34 +92,12 @@ return _SDK_PATH -def _ReadNdkRevision(properties_path): - return _ReadProperty(properties_path, 'pkg.revision') - - -def _ReadProperty(properties_path, property_key): - with open(properties_path, 'r') as f: - ini_str = '[properties]\n' + f.read() - config = ConfigParser.RawConfigParser() - config.readfp(StringIO.StringIO(ini_str)) - try: - return config.get('properties', property_key) - except ConfigParser.NoOptionError: - return None - - -def _GetInstalledNdkRevision(): - """Returns the installed NDK's revision.""" - path = GetNdkPath() - properties_path = os.path.join(path, 'source.properties') - try: - return _ReadNdkRevision(properties_path) - except IOError: - logging.error("Error: Can't read NDK properties in %s", properties_path) - sys.exit(1) - - def _DownloadAndUnzipFile(url, destination_path): - dl_file, dummy_headers = urllib.urlretrieve(url) + shutil.rmtree(destination_path, ignore_errors=True) + _MakeDirs(destination_path) + dl_file = os.path.join(destination_path, 'tmp.zip') + request = requests.get(url, allow_redirects=True) + open(dl_file, 'wb').write(request.content) _UnzipFile(dl_file, destination_path) @@ -165,49 +117,34 @@ toolchains_dir_fd = os.open(_STARBOARD_TOOLCHAINS_DIR, os.O_RDONLY) fcntl.flock(toolchains_dir_fd, fcntl.LOCK_EX) + if os.environ.get('ANDROID_NDK_HOME'): + logging.warning('Warning: ANDROID_NDK_HOME is deprecated and ignored.') + if _ANDROID_HOME: if not os.access(_SDKMANAGER_TOOL, os.X_OK): - logging.error('Error: ANDROID_HOME is set but SDK is not present!') + logging.error('Error: ANDROID_HOME is set but SDK is not present.') sys.exit(1) logging.warning('Warning: Using Android SDK in ANDROID_HOME,' ' which is not automatically updated.\n' ' The following package versions are installed:') installed_packages = _GetInstalledSdkPackages() for package in _ANDROID_SDK_PACKAGES: - version = installed_packages.get(package, '< MISSING! >') + version = installed_packages.get(package, '< NOT INSTALLED >') msg = ' {:30} : {}'.format(package, version) logging.warning(msg) - else: - logging.warning('Checking Android SDK.') - _DownloadInstallOrUpdateSdk() - ndk_path = GetNdkPath() - if _ANDROID_NDK_HOME: - logging.warning('Warning: ANDROID_NDK_HOME references NDK %s in %s,' - ' which is not automatically updated.', - _GetInstalledNdkRevision(), ndk_path) + if not os.path.exists(GetNdkPath()): + logging.error('Error: ANDROID_HOME is is missing NDK %s.', + _NDK_VERSION) + sys.exit(1) - if _ANDROID_HOME or _ANDROID_NDK_HOME: reply = raw_input( 'Do you want to continue using your custom Android tools? [y/N]') if reply.upper() != 'Y': sys.exit(1) - elif not _CheckStamp(ndk_path): - logging.warning('Downloading NDK from %s to %s', _NDK_URL, ndk_path) - if os.path.exists(ndk_path): - shutil.rmtree(ndk_path) - # Download the NDK into _STARBOARD_TOOLCHAINS_DIR and move the top - # _NDK_ZIP_REVISION directory that is in the zip to 'ndk-bundle'. - ndk_unzip_path = os.path.join(_STARBOARD_TOOLCHAINS_DIR, - _NDK_ZIP_REVISION) - if os.path.exists(ndk_unzip_path): - shutil.rmtree(ndk_unzip_path) - _DownloadAndUnzipFile(_NDK_URL, _STARBOARD_TOOLCHAINS_DIR) - # Move NDK into its proper final place. - os.rename(ndk_unzip_path, ndk_path) - _UpdateStamp(ndk_path) - - logging.warning('Using Android NDK version %s', _GetInstalledNdkRevision()) + else: + logging.warning('Checking Android SDK.') + _DownloadInstallOrUpdateSdk() finally: fcntl.flock(toolchains_dir_fd, fcntl.LOCK_UN) os.close(toolchains_dir_fd) @@ -235,14 +172,15 @@ section_re = re.compile(r'^[A-Z][^:]*:$') version_re = re.compile(r'^\s+Version:\s+(\S+)') - p = subprocess.Popen( - [_SDKMANAGER_TOOL, '--list', '--verbose'], stdout=subprocess.PIPE) + p = subprocess.Popen([_SDKMANAGER_TOOL, '--list', '--verbose'], + stdout=subprocess.PIPE) installed_package_versions = {} new_style = False old_style = False for line in iter(p.stdout.readline, ''): - + # Throw away loading progress indicators up to the last CR. + line = line.split('\r')[-1] if section_re.match(line): if new_style or old_style: # We left the new/old style installed packages section @@ -292,6 +230,14 @@ if os.path.exists(_STARBOARD_TOOLCHAINS_SDK_DIR): shutil.rmtree(_STARBOARD_TOOLCHAINS_SDK_DIR) _DownloadAndUnzipFile(_SDK_URL, _STARBOARD_TOOLCHAINS_SDK_DIR) + # TODO: Remove this workaround for sdkmanager incorrectly picking up the + # "tools" directory from the ZIP as the name of its component. + if not os.access(_SDKMANAGER_TOOL, os.X_OK): + old_tools_dir = os.path.join(_STARBOARD_TOOLCHAINS_SDK_DIR, 'tools') + new_tools_dir = os.path.join(_STARBOARD_TOOLCHAINS_SDK_DIR, + 'cmdline-tools', '1.0') + os.mkdir(os.path.dirname(new_tools_dir)) + os.rename(old_tools_dir, new_tools_dir) if not os.access(_SDKMANAGER_TOOL, os.X_OK): logging.error('SDK download failed.') sys.exit(1)
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi index 2401618..365ced9 100644 --- a/src/starboard/android/shared/starboard_platform.gypi +++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -404,9 +404,9 @@ '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc', '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc', '<(DEPTH)/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc', '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc', '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc', '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
diff --git a/src/starboard/android/shared/window_on_screen_keyboard_suggestions_supported.cc b/src/starboard/android/shared/window_on_screen_keyboard_suggestions_supported.cc index 992e15e..ffd2d59 100644 --- a/src/starboard/android/shared/window_on_screen_keyboard_suggestions_supported.cc +++ b/src/starboard/android/shared/window_on_screen_keyboard_suggestions_supported.cc
@@ -15,9 +15,7 @@ #include "starboard/window.h" #if SB_HAS(ON_SCREEN_KEYBOARD) -#if SB_API_VERSION >= 11 bool SbWindowOnScreenKeyboardSuggestionsSupported(SbWindow window) { return true; } -#endif // SB_API_VERSION >= 11 #endif // SB_HAS(ON_SCREEN_KEYBOARD)
diff --git a/src/starboard/android/shared/window_update_on_screen_keyboard_suggestions.cc b/src/starboard/android/shared/window_update_on_screen_keyboard_suggestions.cc index 24f0925..f3e75c2 100644 --- a/src/starboard/android/shared/window_update_on_screen_keyboard_suggestions.cc +++ b/src/starboard/android/shared/window_update_on_screen_keyboard_suggestions.cc
@@ -17,7 +17,6 @@ #include "starboard/android/shared/application_android.h" #if SB_HAS(ON_SCREEN_KEYBOARD) -#if SB_API_VERSION >= 11 void SbWindowUpdateOnScreenKeyboardSuggestions(SbWindow window, const char* suggestions[], int num_suggestions, @@ -31,5 +30,4 @@ ticket); return; } -#endif // SB_API_VERSION >= 11 #endif // SB_HAS(ON_SCREEN_KEYBOARD)
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h index e06b52e..a49c22b 100644 --- a/src/starboard/configuration.h +++ b/src/starboard/configuration.h
@@ -644,6 +644,15 @@ #define SB_HAS_AC3_AUDIO 1 #endif // defined(SB_HAS_AC3_AUDIO) #endif // SB_API_VERSION >= 11 + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) +#if SB_API_VERSION < 11 +#error \ + "SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) requires " \ + "SB_API_VERSION 11 or later." +#endif // SB_API_VERSION < 11 +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + // --- Derived Configuration ------------------------------------------------- // Whether the current platform is little endian.
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn index 8fa8add..bd55f1e 100644 --- a/src/starboard/linux/shared/BUILD.gn +++ b/src/starboard/linux/shared/BUILD.gn
@@ -533,9 +533,9 @@ "//starboard/shared/starboard/player/player_destroy.cc", "//starboard/shared/starboard/player/player_get_current_frame.cc", "//starboard/shared/starboard/player/player_get_info.cc", + "//starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc", "//starboard/shared/starboard/player/player_internal.cc", "//starboard/shared/starboard/player/player_internal.h", - "//starboard/shared/starboard/player/player_output_mode_supported.cc", "//starboard/shared/starboard/player/player_seek.cc", "//starboard/shared/starboard/player/player_set_bounds.cc", "//starboard/shared/starboard/player/player_set_playback_rate.cc",
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi index 1297986..ea9844d 100644 --- a/src/starboard/linux/shared/starboard_platform.gypi +++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -196,6 +196,7 @@ '<(DEPTH)/starboard/shared/pthread/mutex_release.cc', '<(DEPTH)/starboard/shared/pthread/once.cc', '<(DEPTH)/starboard/shared/pthread/thread_context_get_pointer.cc', + '<(DEPTH)/starboard/shared/pthread/thread_context_internal.cc', '<(DEPTH)/starboard/shared/pthread/thread_context_internal.h', '<(DEPTH)/starboard/shared/pthread/thread_create.cc', '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc', @@ -290,6 +291,7 @@ '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc', '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc', '<(DEPTH)/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
diff --git a/src/starboard/nplb/drm_create_system_test.cc b/src/starboard/nplb/drm_create_system_test.cc index 0b68275..9cd4b7a 100644 --- a/src/starboard/nplb/drm_create_system_test.cc +++ b/src/starboard/nplb/drm_create_system_test.cc
@@ -44,7 +44,6 @@ TEST(SbDrmTest, NullCallbacks) { for (int i = 0; i < SB_ARRAY_SIZE_INT(kKeySystems); ++i) { const char* key_system = kKeySystems[i]; -#if SB_API_VERSION >= 10 { SbDrmSystem drm_system = SbDrmCreateSystem( key_system, NULL /* context */, @@ -86,69 +85,9 @@ EXPECT_FALSE(SbDrmSystemIsValid(drm_system)); SbDrmDestroySystem(drm_system); } -#elif SB_HAS(DRM_SESSION_CLOSED) - { - SbDrmSystem drm_system = SbDrmCreateSystem( - key_system, NULL /* context */, - NULL /* session_update_request_func */, DummySessionUpdatedFunc, - DummySessionKeyStatusesChangedFunc, DummySessionClosedFunc); - EXPECT_FALSE(SbDrmSystemIsValid(drm_system)); - SbDrmDestroySystem(drm_system); - } - { - SbDrmSystem drm_system = SbDrmCreateSystem( - key_system, NULL /* context */, DummySessionUpdateRequestFunc, - NULL /*session_updated_func */, DummySessionKeyStatusesChangedFunc, - DummySessionClosedFunc); - EXPECT_FALSE(SbDrmSystemIsValid(drm_system)); - SbDrmDestroySystem(drm_system); - } - { - SbDrmSystem drm_system = SbDrmCreateSystem( - key_system, NULL /* context */, DummySessionUpdateRequestFunc, - DummySessionUpdatedFunc, NULL /* session_key_statuses_changed_func */, - DummySessionClosedFunc); - EXPECT_FALSE(SbDrmSystemIsValid(drm_system)); - SbDrmDestroySystem(drm_system); - } - { - SbDrmSystem drm_system = SbDrmCreateSystem( - key_system, NULL /* context */, DummySessionUpdateRequestFunc, - DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc, - NULL /* session_closed_func */); - EXPECT_FALSE(SbDrmSystemIsValid(drm_system)); - SbDrmDestroySystem(drm_system); - } -#else // SB_HAS(DRM_SESSION_CLOSED) - { - SbDrmSystem drm_system = SbDrmCreateSystem( - key_system, NULL /* context */, - NULL /* session_update_request_func */, DummySessionUpdatedFunc, - DummySessionKeyStatusesChangedFunc); - EXPECT_FALSE(SbDrmSystemIsValid(drm_system)); - SbDrmDestroySystem(drm_system); - } - { - SbDrmSystem drm_system = SbDrmCreateSystem( - key_system, NULL /* context */, DummySessionUpdateRequestFunc, - NULL /* session_updated_func */, DummySessionKeyStatusesChangedFunc); - EXPECT_FALSE(SbDrmSystemIsValid(drm_system)); - SbDrmDestroySystem(drm_system); - } - { - SbDrmSystem drm_system = SbDrmCreateSystem( - key_system, NULL /* context */, DummySessionUpdateRequestFunc, - DummySessionUpdatedFunc, - NULL /* session_key_statuses_changed_func */); - EXPECT_FALSE(SbDrmSystemIsValid(drm_system)); - SbDrmDestroySystem(drm_system); - } -#endif // SB_HAS(DRM_SESSION_CLOSED) } } -#endif // SB_API_VERSION >= 10 -#if SB_API_VERSION >= 10 TEST(SbDrmTest, MultiDrm) { const int kMaxPlayersPerKeySystem = 16; std::vector<SbDrmSystem> created_drm_systems;
diff --git a/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc b/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc new file mode 100644 index 0000000..2e38c86 --- /dev/null +++ b/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
@@ -0,0 +1,102 @@ +// Copyright 2020 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/media.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace shared { +namespace starboard { +namespace media { +namespace { + +TEST(SbMediaCanPlayMimeAndKeySystem, SunnyDay) { + // Vp9 + SbMediaCanPlayMimeAndKeySystem( + "video/webm; codecs=\"vp9\"; width=3840; height=2160; framerate=30; " + "bitrate=21823784; eotf=bt709", + ""); + // Avc + SbMediaSupportType result = SbMediaCanPlayMimeAndKeySystem( + "video/mp4; codecs=\"avc1.4d4015\"; width=640; " + "height=360; framerate=30;", + ""); + ASSERT_EQ(result, kSbMediaSupportTypeProbably); + // Hdr bt709 + SbMediaCanPlayMimeAndKeySystem( + "video/webm; codecs=\"vp09.02.10.10\";eotf=bt709;width=640;height=360", + ""); + // Aac + result = SbMediaCanPlayMimeAndKeySystem( + "audio/mp4; codecs=\"mp4a.40.2\"; channels=2", ""); + ASSERT_EQ(result, kSbMediaSupportTypeProbably); + // Opus + SbMediaCanPlayMimeAndKeySystem("audio/webm; codecs=\"opus\"; channels=2", ""); +} + +TEST(SbMediaCanPlayMimeAndKeySystem, Invalid) { + // Invalid codec + SbMediaSupportType result = + SbMediaCanPlayMimeAndKeySystem("video/webm; codecs=\"abc\";", ""); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); + + result = SbMediaCanPlayMimeAndKeySystem( + "video/webm; codecs=\"vp09.00.01.00.22\";", ""); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); + + // Invalid container + result = + SbMediaCanPlayMimeAndKeySystem("video/abc; codecs=\"avc1.4d4015\";", ""); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); + + // Invalid size + result = SbMediaCanPlayMimeAndKeySystem( + "video/mp4; codecs=\"avc1.4d4015\"; width=99999; height=1080;", ""); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); + + result = SbMediaCanPlayMimeAndKeySystem( + "video/mp4; codecs=\"avc1.4d4015\"; width=1920; height=99999;", ""); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); + + // Invalid bitrate + result = SbMediaCanPlayMimeAndKeySystem( + "video/mp4; codecs=\"avc1.4d4015\"; width=1920; height=1080; " + "bitrate=999999999;", + ""); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); + + // Invalid eotf + result = SbMediaCanPlayMimeAndKeySystem( + "video/webm; codecs=\"vp09.02.10.10\"; width=1920; height=1080; " + "eotf=abc", + ""); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); + + // Invalid channels + result = SbMediaCanPlayMimeAndKeySystem( + "audio/mp4; codecs=\"mp4a.40.2\"; channels=99", ""); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); + + // Invalid keysystem + result = SbMediaCanPlayMimeAndKeySystem( + "video/mp4; codecs=\"avc1.4d4015\"; width=1920; height=1080;", "abc"); + ASSERT_EQ(result, kSbMediaSupportTypeNotSupported); +} + +} // namespace +} // namespace media +} // namespace starboard +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/nplb/media_set_audio_write_duration_test.cc b/src/starboard/nplb/media_set_audio_write_duration_test.cc index 0ca9a37..39fecb0 100644 --- a/src/starboard/nplb/media_set_audio_write_duration_test.cc +++ b/src/starboard/nplb/media_set_audio_write_duration_test.cc
@@ -16,6 +16,7 @@ #include "starboard/common/optional.h" #include "starboard/common/spin_lock.h" +#include "starboard/nplb/player_creation_param_helpers.h" #include "starboard/player.h" #include "starboard/shared/starboard/media/media_support_internal.h" #include "starboard/shared/starboard/player/video_dmp_reader.h" @@ -98,10 +99,8 @@ sample_info.buffer_size = player_sample_info.buffer_size; sample_info.timestamp = player_sample_info.timestamp; sample_info.drm_info = NULL; -#if SB_API_VERSION >= 11 sample_info.type = kSbMediaTypeAudio; sample_info.audio_sample_info = dmp_reader_.audio_sample_info(); -#endif // SB_API_VERSION >= 11 SbPlayer player = pending_decoder_status_->player; SbMediaType type = pending_decoder_status_->type; @@ -136,25 +135,40 @@ SbMediaAudioCodec kAudioCodec = dmp_reader_.audio_codec(); SbDrmSystem kDrmSystem = kSbDrmSystemInvalid; + last_input_timestamp_ = + dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, 0).timestamp; + first_input_timestamp_ = last_input_timestamp_; + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + SbPlayerCreationParam creation_param = CreatePlayerCreationParam( + audio_sample_info.codec, kSbMediaVideoCodecNone); + creation_param.audio_sample_info = audio_sample_info; + creation_param.output_mode = + SbPlayerGetPreferredOutputMode(&creation_param); + EXPECT_NE(creation_param.output_mode, kSbPlayerOutputModeInvalid); + + SbPlayer player = SbPlayerCreate( + fake_graphics_context_provider_.window(), &creation_param, + DummyDeallocateSampleFunc, DecoderStatusFunc, PlayerStatusFunc, + DummyErrorFunc, this /* context */, + fake_graphics_context_provider_.decoder_target_provider()); +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) SbPlayerOutputMode output_mode = kSbPlayerOutputModeDecodeToTexture; + if (!SbPlayerOutputModeSupported(output_mode, kSbMediaVideoCodecNone, kSbDrmSystemInvalid)) { output_mode = kSbPlayerOutputModePunchOut; } - last_input_timestamp_ = - dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, 0).timestamp; - first_input_timestamp_ = last_input_timestamp_; - SbPlayer player = SbPlayerCreate( fake_graphics_context_provider_.window(), kSbMediaVideoCodecNone, kAudioCodec, kSbDrmSystemInvalid, &audio_sample_info, -#if SB_API_VERSION >= 11 NULL /* max_video_capabilities */, -#endif // SB_API_VERSION >= 11 DummyDeallocateSampleFunc, DecoderStatusFunc, PlayerStatusFunc, DummyErrorFunc, this /* context */, output_mode, fake_graphics_context_provider_.decoder_target_provider()); +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + EXPECT_TRUE(SbPlayerIsValid(player)); return player; }
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp index 9043651..7dae228 100644 --- a/src/starboard/nplb/nplb.gyp +++ b/src/starboard/nplb/nplb.gyp
@@ -151,7 +151,7 @@ # TODO: Separate functions tested by media buffer test into multiple # files. 'media_buffer_test.cc', - 'media_set_audio_write_duration_test.cc', + 'media_can_play_mime_and_key_system_test.cc', 'memory_align_to_page_size_test.cc', 'memory_allocate_aligned_test.cc', 'memory_allocate_test.cc', @@ -181,6 +181,9 @@ 'once_test.cc', 'optional_test.cc', 'player_create_test.cc', + 'player_creation_param_helpers.cc', + 'player_creation_param_helpers.h', + 'player_get_preferred_output_mode_test.cc', 'player_output_mode_supported_test.cc', 'random_helpers.cc', 'recursive_mutex_test.cc',
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc index 0aa6b0e..a3f4efc 100644 --- a/src/starboard/nplb/player_create_test.cc +++ b/src/starboard/nplb/player_create_test.cc
@@ -16,6 +16,7 @@ #include "starboard/blitter.h" #include "starboard/decode_target.h" +#include "starboard/nplb/player_creation_param_helpers.h" #include "starboard/player.h" #include "starboard/testing/fake_graphics_context_provider.h" #include "testing/gtest/include/gtest/gtest.h" @@ -53,56 +54,92 @@ const char* message) {} #endif // SB_HAS(PLAYER_ERROR_MESSAGE) -SbMediaAudioSampleInfo GetDefaultAudioSampleInfo() { - SbMediaAudioSampleInfo audio_sample_info; +SbPlayer CallSbPlayerCreate( + SbWindow window, + SbMediaVideoCodec video_codec, + SbMediaAudioCodec audio_codec, + SbDrmSystem drm_system, + const SbMediaAudioSampleInfo* audio_sample_info, + const char* max_video_capabilities, + SbPlayerDeallocateSampleFunc sample_deallocate_func, + SbPlayerDecoderStatusFunc decoder_status_func, + SbPlayerStatusFunc player_status_func, + void* context, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* context_provider) { +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + if (audio_sample_info) { + SB_CHECK(audio_sample_info->codec == audio_codec); + } else { + SB_CHECK(audio_codec == kSbMediaAudioCodecNone); + } + + SbPlayerCreationParam creation_param = + CreatePlayerCreationParam(audio_codec, video_codec); + if (audio_sample_info) { + creation_param.audio_sample_info = *audio_sample_info; + } + creation_param.drm_system = drm_system; + creation_param.output_mode = output_mode; + creation_param.max_video_capabilities = max_video_capabilities; + + return SbPlayerCreate(window, &creation_param, sample_deallocate_func, + decoder_status_func, player_status_func, DummyErrorFunc, + context, context_provider); + +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + + return SbPlayerCreate(window, video_codec, audio_codec, +#if SB_API_VERSION < 10 + SB_PLAYER_NO_DURATION, +#endif // SB_API_VERSION < 10 + kSbDrmSystemInvalid, audio_sample_info, #if SB_API_VERSION >= 11 - audio_sample_info.codec = kSbMediaAudioCodecAac; + max_video_capabilities, #endif // SB_API_VERSION >= 11 - audio_sample_info.format_tag = 0xff; - audio_sample_info.number_of_channels = 2; - audio_sample_info.samples_per_second = 22050; - audio_sample_info.block_alignment = 4; - audio_sample_info.bits_per_sample = 32; - audio_sample_info.audio_specific_config_size = 0; - audio_sample_info.average_bytes_per_second = - audio_sample_info.samples_per_second * - audio_sample_info.number_of_channels * audio_sample_info.bits_per_sample / - 8; + sample_deallocate_func, decoder_status_func, + player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + DummyErrorFunc, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + context, output_mode, context_provider); - return audio_sample_info; +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) +} + +bool IsOutputModeSupported(SbPlayerOutputMode output_mode, + SbMediaVideoCodec codec) { +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + SbPlayerCreationParam creation_param = + CreatePlayerCreationParam(kSbMediaAudioCodecNone, codec); + creation_param.output_mode = output_mode; + return SbPlayerGetPreferredOutputMode(&creation_param) == output_mode; +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + return SbPlayerOutputModeSupported(output_mode, codec, kSbDrmSystemInvalid); +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) } TEST_F(SbPlayerTest, SunnyDay) { - SbMediaAudioSampleInfo audio_sample_info = GetDefaultAudioSampleInfo(); + SbMediaAudioSampleInfo audio_sample_info = + CreateAudioSampleInfo(kSbMediaAudioCodecAac); SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264; - SbDrmSystem kDrmSystem = kSbDrmSystemInvalid; SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture, kSbPlayerOutputModePunchOut}; for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) { SbPlayerOutputMode output_mode = output_modes[i]; - if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) { + + if (!IsOutputModeSupported(output_mode, kVideoCodec)) { continue; } - - SbPlayer player = SbPlayerCreate( + SbPlayer player = CallSbPlayerCreate( fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264, - kSbMediaAudioCodecAac, -#if SB_API_VERSION < 10 - SB_PLAYER_NO_DURATION, -#endif // SB_API_VERSION < 10 - kSbDrmSystemInvalid, &audio_sample_info, -#if SB_API_VERSION >= 11 - NULL /* max_video_capabilities */, -#endif // SB_API_VERSION >= 11 - DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyStatusFunc, -#if SB_HAS(PLAYER_ERROR_MESSAGE) - DummyErrorFunc, -#endif // SB_HAS(PLAYER_ERROR_MESSAGE) - NULL /* context */, output_mode, - fake_graphics_context_provider_.decoder_target_provider()); + kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info, + "" /* max_video_capabilities */, DummyDeallocateSampleFunc, + DummyDecoderStatusFunc, DummyStatusFunc, NULL /* context */, + output_mode, fake_graphics_context_provider_.decoder_target_provider()); EXPECT_TRUE(SbPlayerIsValid(player)); if (output_mode == kSbPlayerOutputModeDecodeToTexture) { @@ -115,36 +152,26 @@ #if SB_API_VERSION >= 10 TEST_F(SbPlayerTest, NullCallbacks) { - SbMediaAudioSampleInfo audio_sample_info = GetDefaultAudioSampleInfo(); + SbMediaAudioSampleInfo audio_sample_info = + CreateAudioSampleInfo(kSbMediaAudioCodecAac); SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264; - SbDrmSystem kDrmSystem = kSbDrmSystemInvalid; SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture, kSbPlayerOutputModePunchOut}; for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) { SbPlayerOutputMode output_mode = output_modes[i]; - if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) { + if (!IsOutputModeSupported(output_mode, kVideoCodec)) { continue; } { - SbPlayer player = SbPlayerCreate( + SbPlayer player = CallSbPlayerCreate( fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264, - kSbMediaAudioCodecAac, -#if SB_API_VERSION < 10 - SB_PLAYER_NO_DURATION, -#endif // SB_API_VERSION < 10 - kSbDrmSystemInvalid, &audio_sample_info, -#if SB_API_VERSION >= 11 - NULL /* max_video_capabilities */, -#endif // SB_API_VERSION >= 11 - NULL /* deallocate_sample_func */, DummyDecoderStatusFunc, - DummyStatusFunc, -#if SB_HAS(PLAYER_ERROR_MESSAGE) - DummyErrorFunc, -#endif // SB_HAS(PLAYER_ERROR_MESSAGE) - NULL /* context */, output_mode, + kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info, + "" /* max_video_capabilities */, NULL /* deallocate_sample_func */, + DummyDecoderStatusFunc, DummyStatusFunc, NULL /* context */, + output_mode, fake_graphics_context_provider_.decoder_target_provider()); EXPECT_FALSE(SbPlayerIsValid(player)); @@ -152,22 +179,12 @@ } { - SbPlayer player = SbPlayerCreate( + SbPlayer player = CallSbPlayerCreate( fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264, - kSbMediaAudioCodecAac, -#if SB_API_VERSION < 10 - SB_PLAYER_NO_DURATION, -#endif // SB_API_VERSION < 10 - kSbDrmSystemInvalid, &audio_sample_info, -#if SB_API_VERSION >= 11 - NULL /* max_video_capabilities */, -#endif // SB_API_VERSION >= 11 - DummyDeallocateSampleFunc, NULL /* decoder_status_func */, - DummyStatusFunc, -#if SB_HAS(PLAYER_ERROR_MESSAGE) - DummyErrorFunc, -#endif // SB_HAS(PLAYER_ERROR_MESSAGE) - NULL /* context */, output_mode, + kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info, + "" /* max_video_capabilities */, DummyDeallocateSampleFunc, + NULL /* decoder_status_func */, DummyStatusFunc, NULL /* context */, + output_mode, fake_graphics_context_provider_.decoder_target_provider()); EXPECT_FALSE(SbPlayerIsValid(player)); @@ -175,22 +192,12 @@ } { - SbPlayer player = SbPlayerCreate( + SbPlayer player = CallSbPlayerCreate( fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264, - kSbMediaAudioCodecAac, -#if SB_API_VERSION < 10 - SB_PLAYER_NO_DURATION, -#endif // SB_API_VERSION < 10 - kSbDrmSystemInvalid, &audio_sample_info, -#if SB_API_VERSION >= 11 - NULL /* max_video_capabilities */, -#endif // SB_API_VERSION >= 11 - DummyDeallocateSampleFunc, DummyDecoderStatusFunc, - NULL /*status_func */, -#if SB_HAS(PLAYER_ERROR_MESSAGE) - DummyErrorFunc, -#endif // SB_HAS(PLAYER_ERROR_MESSAGE) - NULL /* context */, output_mode, + kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info, + "" /* max_video_capabilities */, DummyDeallocateSampleFunc, + DummyDecoderStatusFunc, NULL /*status_func */, NULL /* context */, + output_mode, fake_graphics_context_provider_.decoder_target_provider()); EXPECT_FALSE(SbPlayerIsValid(player)); @@ -198,13 +205,29 @@ } #if SB_HAS(PLAYER_ERROR_MESSAGE) + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + + { + SbPlayerCreationParam creation_param = CreatePlayerCreationParam( + kSbMediaAudioCodecAac, kSbMediaVideoCodecH264); + + SbPlayer player = SbPlayerCreate( + fake_graphics_context_provider_.window(), &creation_param, + DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyStatusFunc, + NULL /* error_func */, NULL /* context */, + fake_graphics_context_provider_.decoder_target_provider()); + EXPECT_FALSE(SbPlayerIsValid(player)); + + SbPlayerDestroy(player); + } + +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + { SbPlayer player = SbPlayerCreate( fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264, kSbMediaAudioCodecAac, -#if SB_API_VERSION < 10 - SB_PLAYER_NO_DURATION, -#endif // SB_API_VERSION < 10 kSbDrmSystemInvalid, &audio_sample_info, #if SB_API_VERSION >= 11 NULL /* max_video_capabilities */, @@ -216,6 +239,9 @@ SbPlayerDestroy(player); } + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + #endif // SB_HAS(PLAYER_ERROR_MESSAGE) } } @@ -224,31 +250,21 @@ #if SB_HAS(AUDIOLESS_VIDEO) TEST_F(SbPlayerTest, Audioless) { SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264; - SbDrmSystem kDrmSystem = kSbDrmSystemInvalid; SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture, kSbPlayerOutputModePunchOut}; for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) { SbPlayerOutputMode output_mode = output_modes[i]; - if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) { + if (!IsOutputModeSupported(output_mode, kVideoCodec)) { continue; } - SbPlayer player = SbPlayerCreate( + SbPlayer player = CallSbPlayerCreate( fake_graphics_context_provider_.window(), kVideoCodec, - kSbMediaAudioCodecNone, -#if SB_API_VERSION < 10 - SB_PLAYER_NO_DURATION, -#endif // SB_API_VERSION < 10 - kSbDrmSystemInvalid, NULL /* audio_sample_info */, -#if SB_API_VERSION >= 11 - NULL /* max_video_capabilities */, -#endif // SB_API_VERSION >= 11 + kSbMediaAudioCodecNone, kSbDrmSystemInvalid, + NULL /* audio_sample_info */, "" /* max_video_capabilities */, DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyStatusFunc, -#if SB_HAS(PLAYER_ERROR_MESSAGE) - DummyErrorFunc, -#endif // SB_HAS(PLAYER_ERROR_MESSAGE) NULL /* context */, output_mode, fake_graphics_context_provider_.decoder_target_provider()); EXPECT_TRUE(SbPlayerIsValid(player)); @@ -264,36 +280,26 @@ #if SB_API_VERSION >= 10 TEST_F(SbPlayerTest, AudioOnly) { - SbMediaAudioSampleInfo audio_sample_info = GetDefaultAudioSampleInfo(); + SbMediaAudioSampleInfo audio_sample_info = + CreateAudioSampleInfo(kSbMediaAudioCodecAac); SbMediaAudioCodec kAudioCodec = kSbMediaAudioCodecAac; SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264; - SbDrmSystem kDrmSystem = kSbDrmSystemInvalid; SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture, kSbPlayerOutputModePunchOut}; for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) { SbPlayerOutputMode output_mode = output_modes[i]; - if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) { + if (!IsOutputModeSupported(output_mode, kVideoCodec)) { continue; } - SbPlayer player = SbPlayerCreate( + SbPlayer player = CallSbPlayerCreate( fake_graphics_context_provider_.window(), kSbMediaVideoCodecNone, - kAudioCodec, -#if SB_API_VERSION < 10 - SB_PLAYER_NO_DURATION, -#endif // SB_API_VERSION < 10 - kSbDrmSystemInvalid, &audio_sample_info, -#if SB_API_VERSION >= 11 - NULL /* max_video_capabilities */, -#endif // SB_API_VERSION >= 11 - DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyStatusFunc, -#if SB_HAS(PLAYER_ERROR_MESSAGE) - DummyErrorFunc, -#endif // SB_HAS(PLAYER_ERROR_MESSAGE) - NULL /* context */, output_mode, - fake_graphics_context_provider_.decoder_target_provider()); + kAudioCodec, kSbDrmSystemInvalid, &audio_sample_info, + "" /* max_video_capabilities */, DummyDeallocateSampleFunc, + DummyDecoderStatusFunc, DummyStatusFunc, NULL /* context */, + output_mode, fake_graphics_context_provider_.decoder_target_provider()); EXPECT_TRUE(SbPlayerIsValid(player)); if (output_mode == kSbPlayerOutputModeDecodeToTexture) { @@ -303,11 +309,10 @@ SbPlayerDestroy(player); } } -#endif // SB_API_VERSION >= 10 -#if SB_API_VERSION >= 10 TEST_F(SbPlayerTest, MultiPlayer) { - SbMediaAudioSampleInfo audio_sample_info = GetDefaultAudioSampleInfo(); + SbMediaAudioSampleInfo audio_sample_info = + CreateAudioSampleInfo(kSbMediaAudioCodecAac); SbDrmSystem kDrmSystem = kSbDrmSystemInvalid; constexpr SbPlayerOutputMode kOutputModes[] = { @@ -387,14 +392,11 @@ #if SB_API_VERSION >= 11 audio_sample_info.codec = kAudioCodecs[k]; #endif // SB_API_VERSION >= 11 - created_players.push_back(SbPlayerCreate( + created_players.push_back(CallSbPlayerCreate( fake_graphics_context_provider_.window(), kVideoCodecs[l], kAudioCodecs[k], kSbDrmSystemInvalid, &audio_sample_info, -#if SB_API_VERSION >= 11 - NULL /* max_video_capabilities */, -#endif // SB_API_VERSION >= 11 - DummyDeallocateSampleFunc, DummyDecoderStatusFunc, - DummyStatusFunc, DummyErrorFunc, NULL /* context */, + "" /* max_video_capabilities */, DummyDeallocateSampleFunc, + DummyDecoderStatusFunc, DummyStatusFunc, NULL /* context */, kOutputModes[j], fake_graphics_context_provider_.decoder_target_provider())); if (!SbPlayerIsValid(created_players.back())) {
diff --git a/src/starboard/nplb/player_creation_param_helpers.cc b/src/starboard/nplb/player_creation_param_helpers.cc new file mode 100644 index 0000000..62009ff --- /dev/null +++ b/src/starboard/nplb/player_creation_param_helpers.cc
@@ -0,0 +1,149 @@ +// Copyright 2020 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/nplb/player_creation_param_helpers.h" + +#include "starboard/common/log.h" + +namespace starboard { +namespace nplb { + +SbMediaAudioSampleInfo CreateAudioSampleInfo(SbMediaAudioCodec codec) { + SbMediaAudioSampleInfo audio_sample_info = {}; + +#if SB_API_VERSION >= 11 + audio_sample_info.codec = codec; +#endif // SB_API_VERSION >= 11 + + switch (codec) { + case kSbMediaAudioCodecNone: + break; + case kSbMediaAudioCodecAac: { + static const uint8_t kAacAudioSpecificConfig[16] = {18, 16}; + + audio_sample_info.format_tag = 0xff; + audio_sample_info.number_of_channels = 2; + audio_sample_info.samples_per_second = 44100; + audio_sample_info.block_alignment = 4; + audio_sample_info.bits_per_sample = 16; + audio_sample_info.audio_specific_config = kAacAudioSpecificConfig; + audio_sample_info.audio_specific_config_size = + sizeof(kAacAudioSpecificConfig); + audio_sample_info.average_bytes_per_second = + audio_sample_info.samples_per_second * + audio_sample_info.number_of_channels * + audio_sample_info.bits_per_sample / 8; + break; + } + case kSbMediaAudioCodecAc3: + case kSbMediaAudioCodecEac3: { + audio_sample_info.format_tag = 0xff; + audio_sample_info.number_of_channels = 6; + audio_sample_info.samples_per_second = 48000; + audio_sample_info.block_alignment = 4; + audio_sample_info.bits_per_sample = 16; + audio_sample_info.audio_specific_config = nullptr; + audio_sample_info.audio_specific_config_size = 0; + audio_sample_info.average_bytes_per_second = + audio_sample_info.samples_per_second * + audio_sample_info.number_of_channels * + audio_sample_info.bits_per_sample / 8; + break; + } + case kSbMediaAudioCodecOpus: { + static const uint8_t kOpusAudioSpecificConfig[19] = { + 79, 112, 117, 115, 72, 101, 97, 100, 1, 2, 56, 1, 128, 187}; + + audio_sample_info.format_tag = 0xff; + audio_sample_info.number_of_channels = 2; + audio_sample_info.samples_per_second = 48000; + audio_sample_info.block_alignment = 4; + audio_sample_info.bits_per_sample = 32; + audio_sample_info.audio_specific_config = kOpusAudioSpecificConfig; + audio_sample_info.audio_specific_config_size = + sizeof(kOpusAudioSpecificConfig); + audio_sample_info.average_bytes_per_second = + audio_sample_info.samples_per_second * + audio_sample_info.number_of_channels * + audio_sample_info.bits_per_sample / 8; + break; + } + case kSbMediaAudioCodecVorbis: { + // Note that unlike the configuration of the other formats, the following + // configuration is made up, instead of taking from a real input. + audio_sample_info.format_tag = 0xff; + audio_sample_info.number_of_channels = 2; + audio_sample_info.samples_per_second = 48000; + audio_sample_info.block_alignment = 4; + audio_sample_info.bits_per_sample = 16; + audio_sample_info.audio_specific_config = nullptr; + audio_sample_info.audio_specific_config_size = 0; + audio_sample_info.average_bytes_per_second = + audio_sample_info.samples_per_second * + audio_sample_info.number_of_channels * + audio_sample_info.bits_per_sample / 8; + break; + } + } + return audio_sample_info; +} + +SbMediaVideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec) { + SbMediaVideoSampleInfo video_sample_info = {}; + +#if SB_API_VERSION >= 11 + video_sample_info.codec = codec; +#endif // SB_API_VERSION >= 11 + +#if SB_API_VERSION >= 11 + video_sample_info.color_metadata.primaries = kSbMediaPrimaryIdBt709; + video_sample_info.color_metadata.transfer = kSbMediaTransferIdBt709; + video_sample_info.color_metadata.matrix = kSbMediaMatrixIdBt709; + video_sample_info.color_metadata.range = kSbMediaRangeIdLimited; +#else // SB_API_VERSION >= 11 + static SbMediaColorMetadata color_metadata; + color_metadata.primaries = kSbMediaPrimaryIdBt709; + color_metadata.transfer = kSbMediaTransferIdBt709; + color_metadata.matrix = kSbMediaMatrixIdBt709; + color_metadata.range = kSbMediaRangeIdLimited; + video_sample_info.color_metadata = &color_metadata; +#endif // SB_API_VERSION >= 11 + + video_sample_info.frame_width = 1920; + video_sample_info.frame_height = 1080; + + return video_sample_info; +} + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +SbPlayerCreationParam CreatePlayerCreationParam(SbMediaAudioCodec audio_codec, + SbMediaVideoCodec video_codec) { + SbPlayerCreationParam creation_param = {}; + + creation_param.audio_mime = ""; + creation_param.video_mime = ""; + creation_param.drm_system = kSbDrmSystemInvalid; + creation_param.audio_sample_info = CreateAudioSampleInfo(audio_codec); + creation_param.video_sample_info = CreateVideoSampleInfo(video_codec); + creation_param.output_mode = kSbPlayerOutputModeInvalid; + creation_param.max_video_capabilities = ""; + + return creation_param; +} + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +} // namespace nplb +} // namespace starboard
diff --git a/src/starboard/nplb/player_creation_param_helpers.h b/src/starboard/nplb/player_creation_param_helpers.h new file mode 100644 index 0000000..8cb327d --- /dev/null +++ b/src/starboard/nplb/player_creation_param_helpers.h
@@ -0,0 +1,37 @@ +// Copyright 2020 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_NPLB_PLAYER_CREATION_PARAM_HELPERS_H_ +#define STARBOARD_NPLB_PLAYER_CREATION_PARAM_HELPERS_H_ + +#include "starboard/configuration.h" + +#include "starboard/media.h" +#include "starboard/player.h" + +namespace starboard { +namespace nplb { + +SbMediaAudioSampleInfo CreateAudioSampleInfo(SbMediaAudioCodec codec); +SbMediaVideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec); + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) +SbPlayerCreationParam CreatePlayerCreationParam(SbMediaAudioCodec audio_codec, + SbMediaVideoCodec video_codec); +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +} // namespace nplb +} // namespace starboard + +#endif // STARBOARD_NPLB_PLAYER_CREATION_PARAM_HELPERS_H_
diff --git a/src/starboard/nplb/player_get_preferred_output_mode_test.cc b/src/starboard/nplb/player_get_preferred_output_mode_test.cc new file mode 100644 index 0000000..4167dd9 --- /dev/null +++ b/src/starboard/nplb/player_get_preferred_output_mode_test.cc
@@ -0,0 +1,52 @@ +// Copyright 2019 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/player.h" + +#include "starboard/configuration.h" +#include "starboard/nplb/player_creation_param_helpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +namespace starboard { +namespace nplb { +namespace { + +TEST(SbPlayerGetPreferredOutputModeTest, SunnyDay) { + auto creation_param = + CreatePlayerCreationParam(kSbMediaAudioCodecAac, kSbMediaVideoCodecH264); + + creation_param.output_mode = kSbPlayerOutputModeInvalid; + auto output_mode = SbPlayerGetPreferredOutputMode(&creation_param); + ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid); + + creation_param.output_mode = output_mode; + output_mode = SbPlayerGetPreferredOutputMode(&creation_param); + ASSERT_EQ(output_mode, creation_param.output_mode); + + creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture; + output_mode = SbPlayerGetPreferredOutputMode(&creation_param); + ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid); + + creation_param.output_mode = kSbPlayerOutputModePunchOut; + output_mode = SbPlayerGetPreferredOutputMode(&creation_param); + ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid); +} + +} // namespace +} // namespace nplb +} // namespace starboard + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/nplb/player_output_mode_supported_test.cc b/src/starboard/nplb/player_output_mode_supported_test.cc index 65b35b3..d4e77f2 100644 --- a/src/starboard/nplb/player_output_mode_supported_test.cc +++ b/src/starboard/nplb/player_output_mode_supported_test.cc
@@ -21,6 +21,8 @@ namespace nplb { namespace { +#if !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + TEST(SbPlayerOutputModeSupportedTest, SunnyDay) { SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264; SbDrmSystem kDrmSystem = kSbDrmSystemInvalid; @@ -46,6 +48,8 @@ EXPECT_FALSE(result); } +#endif // !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + } // namespace } // namespace nplb } // namespace starboard
diff --git a/src/starboard/nplb/socket_get_interface_address_test.cc b/src/starboard/nplb/socket_get_interface_address_test.cc index 36ed9c3..3c1a317 100644 --- a/src/starboard/nplb/socket_get_interface_address_test.cc +++ b/src/starboard/nplb/socket_get_interface_address_test.cc
@@ -64,7 +64,7 @@ EXPECT_TRUE(SbSocketGetInterfaceAddress(NULL, &source, &netmask)); // A netmask that starts with 0 is likely incorrect. - EXPECT_TRUE(netmask.address[0] & 0x8); + EXPECT_TRUE(netmask.address[0] & 0x80); EXPECT_TRUE(source.type == kSbSocketAddressTypeIpv4 || source.type == kSbSocketAddressTypeIpv6); EXPECT_TRUE(netmask.type == kSbSocketAddressTypeIpv4 || @@ -89,7 +89,7 @@ EXPECT_FALSE(IsLocalhost(&source)); // A netmask that starts with 0 is likely incorrect. - EXPECT_TRUE(netmask.address[0] & 0x8); + EXPECT_TRUE(netmask.address[0] & 0x80); EXPECT_EQ(GetAddressType(), source.type); EXPECT_EQ(GetAddressType(), netmask.type); EXPECT_EQ(0, source.port); @@ -128,7 +128,7 @@ EXPECT_EQ(GetAddressType(), source.type); EXPECT_NE(0, source.port); // A netmask that starts with 0 is likely incorrect. - EXPECT_TRUE(netmask.address[0] & 0x8); + EXPECT_TRUE(netmask.address[0] & 0x80); EXPECT_EQ(GetAddressType(), netmask.type); EXPECT_NE(0, SbMemoryCompare(source.address, invalid_address.address, SB_ARRAY_SIZE(source.address)));
diff --git a/src/starboard/player.h b/src/starboard/player.h index 08e16d1..b579672 100644 --- a/src/starboard/player.h +++ b/src/starboard/player.h
@@ -117,6 +117,51 @@ kSbPlayerOutputModeInvalid, } SbPlayerOutputMode; +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +// The playback related parameters to pass into SbPlayerCreate() and +// SbPlayerGetPreferredOutputMode(). +typedef struct SbPlayerCreationParam { + // The audio mime of the stream if available. Otherwise it will point to an + // empty string. It will never be NULL. + const char* audio_mime; + // The video mime of the stream if available. Otherwise it will point to an + // empty string. It will never be NULL. + const char* video_mime; + // Provides an appropriate DRM system if the media stream has encrypted + // portions. It will be |kSbDrmSystemInvalid| if the stream does not have + // encrypted portions. + SbDrmSystem drm_system; + // Contains a populated SbMediaAudioSampleInfo if |audio_sample_info.codec| + // isn't |kSbMediaAudioCodecNone|. When |audio_sample_info.codec| is + // |kSbMediaAudioCodecNone|, the video doesn't have an audio track. + SbMediaAudioSampleInfo audio_sample_info; + // Contains a populated SbMediaVideoSampleInfo if |video_sample_info.codec| + // isn't |kSbMediaVideoCodecNone|. When |video_sample_info.codec| is + // |kSbMediaVideoCodecNone|, the video is audio only. + SbMediaVideoSampleInfo video_sample_info; + // Selects how the decoded video frames will be output. For example, + // |kSbPlayerOutputModePunchOut| indicates that the decoded video frames will + // be output to a background video layer by the platform, and + // |kSbPlayerOutputDecodeToTexture| indicates that the decoded video frames + // should be made available for the application to pull via calls to + // SbPlayerGetCurrentFrame(). + SbPlayerOutputMode output_mode; + // Indicates the max video capabilities required. The web app will not provide + // a video stream exceeding the maximums described by this parameter. Allows + // the platform to optimize playback pipeline for low quality video streams if + // it knows that it will never adapt to higher quality streams. The string + // uses the same format as the string passed in to + // SbMediaCanPlayMimeAndKeySystem(), for example, when it is set to + // "width=1920; height=1080; framerate=15;", the video will never adapt to + // resolution higher than 1920x1080 or frame per second higher than 15 fps. + // When the maximums are unknown, this will be set to an empty string. It + // will never be set to NULL. + const char* max_video_capabilities; +} SbPlayerCreationParam; + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + #if SB_API_VERSION >= 11 // Identify the type of side data accompanied with |SbPlayerSampleInfo|, as side @@ -411,15 +456,16 @@ // is no longer valid after this function returns. The implementation has to // make a copy of the content if it is needed after the function returns. #if SB_API_VERSION >= 11 -// |max_video_capabilities|: This string communicates the video maximums to the -// platform. The web app will not provide a video stream exceeding the -// maximums described by this parameter. Allows the platform to optimize -// playback pipeline for low quality video streams if it knows that it will -// never adapt to higher quality streams. The string uses the same format as -// the string passed in to SbMediaCanPlayMimeAndKeySystem(), for example, when -// it is set to "width=1920; height=1080; framerate=15;", the video will never -// adapt to resolution higher than 1920x1080 or frame per second higher than -// 15 fps. When the maximums are unknown, this will be set to NULL. +// |max_video_capabilities|: This string communicates the max video capabilities +// required to the platform. The web app will not provide a video stream +// exceeding the maximums described by this parameter. Allows the platform to +// optimize playback pipeline for low quality video streams if it knows that +// it will never adapt to higher quality streams. The string uses the same +// format as the string passed in to SbMediaCanPlayMimeAndKeySystem(), for +// example, when it is set to "width=1920; height=1080; framerate=15;", the +// video will never adapt to resolution higher than 1920x1080 or frame per +// second higher than 15 fps. When the maximums are unknown, this will be set +// to NULL. #endif // SB_API_VERSION >= 11 #if SB_HAS(AUDIOLESS_VIDEO) // When |audio_codec| is |kSbMediaAudioCodecNone|, this must be set to NULL. @@ -467,6 +513,21 @@ // |decoder_status_func|, |player_status_func|, or |player_error_func| if it // applies), then |kSbPlayerInvalid| must be returned. #endif // SB_API_VERSION >= 10 + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +SB_EXPORT SbPlayer +SbPlayerCreate(SbWindow window, + const SbPlayerCreationParam* creation_param, + SbPlayerDeallocateSampleFunc sample_deallocate_func, + SbPlayerDecoderStatusFunc decoder_status_func, + SbPlayerStatusFunc player_status_func, + SbPlayerErrorFunc player_error_func, + void* context, + SbDecodeTargetGraphicsContextProvider* context_provider); + +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + SB_EXPORT SbPlayer SbPlayerCreate(SbWindow window, SbMediaVideoCodec video_codec, @@ -492,12 +553,38 @@ void* context, SbPlayerOutputMode output_mode, SbDecodeTargetGraphicsContextProvider* context_provider); + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) +// Returns the preferred output mode of the implementation when a video +// described by |creation_param| is played. It is assumed that it is okay to +// call SbPlayerCreate() with the same video described by |creation_param|, +// with its |output_mode| member replaced by the returned output mode. +// When the caller has no preference on the output mode, it will set +// |creation_param->output_mode| to |kSbPlayerOutputModeInvalid|, and the +// implementation can return its preferred output mode based on the information +// contained in |creation_param|. The caller can also set +// |creation_param->output_mode| to its preferred output mode, and the +// implementation should return the same output mode if it is supported, +// otherwise the implementation should return an output mode that it is +// supported, as if |creation_param->output_mode| is set to +// |kSbPlayerOutputModeInvalid| prior to the call. +// Note that it is not the responsibility of this function to verify whether the +// video described by |creation_param| can be played on the platform, and the +// implementation should try its best effort to return a valid output mode. +// |creation_param| will never be NULL. +SB_EXPORT SbPlayerOutputMode +SbPlayerGetPreferredOutputMode(const SbPlayerCreationParam* creation_param); + +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) // Returns true if the given player output mode is supported by the platform. // If this function returns true, it is okay to call SbPlayerCreate() with // the given |output_mode|. SB_EXPORT bool SbPlayerOutputModeSupported(SbPlayerOutputMode output_mode, SbMediaVideoCodec codec, SbDrmSystem drm_system); +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) // Destroys |player|, freeing all associated resources. Each callback must // receive one more callback to say that the player was destroyed. Callbacks
diff --git a/src/starboard/raspi/shared/launcher.py b/src/starboard/raspi/shared/launcher.py index 75e57c6..2ea99b7 100644 --- a/src/starboard/raspi/shared/launcher.py +++ b/src/starboard/raspi/shared/launcher.py
@@ -143,7 +143,7 @@ retry_count = 0 expected_prompts = [ r'.*Are\syou\ssure.*', # Fingerprint verification - r'\S+ password:', # Password prompt + r'.* password:', # Password prompt '.*[a-zA-Z]+.*', # Any other text input ] while True:
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi index 3c0ebb4..f71d0c8 100644 --- a/src/starboard/raspi/shared/starboard_platform.gypi +++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -234,6 +234,7 @@ '<(DEPTH)/starboard/shared/pthread/mutex_release.cc', '<(DEPTH)/starboard/shared/pthread/once.cc', '<(DEPTH)/starboard/shared/pthread/thread_context_get_pointer.cc', + '<(DEPTH)/starboard/shared/pthread/thread_context_internal.cc', '<(DEPTH)/starboard/shared/pthread/thread_context_internal.h', '<(DEPTH)/starboard/shared/pthread/thread_create.cc', '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc', @@ -326,6 +327,7 @@ '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc', '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc', '<(DEPTH)/starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
diff --git a/src/starboard/shared/pthread/thread_context_get_pointer.cc b/src/starboard/shared/pthread/thread_context_get_pointer.cc index d7e2e7d..4625ebf 100644 --- a/src/starboard/shared/pthread/thread_context_get_pointer.cc +++ b/src/starboard/shared/pthread/thread_context_get_pointer.cc
@@ -21,88 +21,24 @@ #include "starboard/common/log.h" #include "starboard/shared/pthread/thread_context_internal.h" -// TODO: Implement for e.g. Mac OSX, BSD flavors, QNX - maybe in another file. -#if !defined(__gnu_linux__) // Note: __linux__ is undef'd for Starboard builds. -#error "SbThreadContext is only implemented for Linux" -#endif - -// UCLIBC pretends to be GLIBC. -#if (defined(__GLIBC__) || defined(__GNU_LIBRARY__)) && !defined(__UCLIBC__) -#define SB_IS_GLIBC 1 -#else -#define SB_IS_GLIBC 0 -#endif - -// clang-format off -#if SB_IS_ARCH_X86 -# if SB_IS_32_BIT -# error("TODO: Validate UCONTEXT macros on 32-bit X86") -# define UCONTEXT_IP(ucontext) ((ucontext)->uc_mcontext.gregs[REG_EIP]) -# define UCONTEXT_SP(ucontext) ((ucontext)->uc_mcontext.gregs[REG_ESP]) -# define UCONTEXT_FP(ucontext) ((ucontext)->uc_mcontext.gregs[REG_EBP]) -# elif SB_IS_64_BIT -# define UCONTEXT_IP(ucontext) ((ucontext)->uc_mcontext.gregs[REG_RIP]) -# define UCONTEXT_SP(ucontext) ((ucontext)->uc_mcontext.gregs[REG_RSP]) -# define UCONTEXT_FP(ucontext) ((ucontext)->uc_mcontext.gregs[REG_RBP]) -# endif -#elif SB_IS_ARCH_ARM -# if SB_IS_32_BIT -# if SB_IS_GLIBC && ((__GLIBC__ * 100 + __GLIBC_MINOR__) < 204) -# error("TODO: Validate UCONTEXT macros on 32-bit ARM w/ old GLIBC") -# define UCONTEXT_IP(ucontext) ((ucontext)->uc_mcontext.gregs[R15]) -# define UCONTEXT_SP(ucontext) ((ucontext)->uc_mcontext.gregs[R13]) -# define UCONTEXT_FP(ucontext) ((ucontext)->uc_mcontext.gregs[R11]) -# else // SB_IS_GLIBC < 2.04 -# define UCONTEXT_IP(ucontext) ((ucontext)->uc_mcontext.arm_pc) -# define UCONTEXT_SP(ucontext) ((ucontext)->uc_mcontext.arm_sp) -# define UCONTEXT_FP(ucontext) ((ucontext)->uc_mcontext.arm_fp) -# endif // SB_IS_GLIBC < 2.04 -# elif SB_IS_64_BIT -# error("TODO: Validate UCONTEXT macros on 64-bit ARM") -# define UCONTEXT_IP(ucontext) ((ucontext)->uc_mcontext.pc) -# define UCONTEXT_SP(ucontext) ((ucontext)->uc_mcontext.sp) -# define UCONTEXT_FP(ucontext) ((ucontext)->uc_mcontext.regs[29]) -# endif -#elif SB_IS_ARCH_MIPS -# error("TODO: Validate UCONTEXT macros on MIPS") -# define UCONTEXT_IP(ucontext) ((ucontext)->uc_mcontext.pc) -# define UCONTEXT_SP(ucontext) ((ucontext)->uc_mcontext.gregs[29]) -# define UCONTEXT_FP(ucontext) ((ucontext)->uc_mcontext.gregs[30]) -#elif SB_IS_ARCH_PPC -# if SB_IS_GLIBC -# error("TODO: Validate UCONTEXT macros on PPC w/ GLIBC") -# define UCONTEXT_IP(ucontext) ((ucontext)->uc_mcontext.regs->nip) -# define UCONTEXT_SP(ucontext) ((ucontext)->uc_mcontext.regs->gpr[PT_R1]) -# define UCONTEXT_FP(ucontext) ((ucontext)->uc_mcontext.regs->gpr[PT_R31]) -# else // SB_IS_GLIBC -# error("TODO: Validate UCONTEXT macros on PPC") -# define UCONTEXT_IP(ucontext) ((ucontext)->uc_mcontext.gp_regs[32]) -# define UCONTEXT_SP(ucontext) ((ucontext)->uc_mcontext.gp_regs[1]) -# define UCONTEXT_FP(ucontext) ((ucontext)->uc_mcontext.gp_regs[31]) -# endif // SB_IS_GLIBC -#else // SB_IS_ARCH_XXX -# error "SbThreadContext isn't implemented for this CPU architecture" -#endif // SB_IS_ARCH_XXX -// clang-format on - bool SbThreadContextGetPointer(SbThreadContext context, SbThreadContextProperty property, void** out_value) { - if (context == kSbThreadContextInvalid || context->ucontext == nullptr) { + if (context == kSbThreadContextInvalid || context->ip_ == nullptr) { return false; } switch (property) { case kSbThreadContextInstructionPointer: - *out_value = reinterpret_cast<void*>(UCONTEXT_IP(context->ucontext)); + *out_value = context->ip_; return true; case kSbThreadContextStackPointer: - *out_value = reinterpret_cast<void*>(UCONTEXT_SP(context->ucontext)); + *out_value = context->sp_; return true; case kSbThreadContextFramePointer: - *out_value = reinterpret_cast<void*>(UCONTEXT_FP(context->ucontext)); + *out_value = context->fp_; return true; default:
diff --git a/src/starboard/shared/pthread/thread_context_internal.cc b/src/starboard/shared/pthread/thread_context_internal.cc new file mode 100644 index 0000000..f5e7353 --- /dev/null +++ b/src/starboard/shared/pthread/thread_context_internal.cc
@@ -0,0 +1,73 @@ +// Copyright 2020 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/pthread/thread_context_internal.h" + +#if SB_API_VERSION >= 11 + +#if !defined(__gnu_linux__) // Note: __linux__ is undef'd for Starboard builds. +#error "SbThreadContext is only implemented for Linux" +#endif + +#if !(SB_IS_32_BIT || SB_IS_64_BIT) +#error "CPU architecture must be either 32-bit or 64-bit" +#endif + +// UCLIBC pretends to be GLIBC. +#if (defined(__GLIBC__) || defined(__GNU_LIBRARY__)) && !defined(__UCLIBC__) +#define SB_IS_GLIBC 1 +#else +#define SB_IS_GLIBC 0 +#endif + +SbThreadContextPrivate::SbThreadContextPrivate(ucontext_t* ucontext) { + mcontext_t& mcontext = ucontext->uc_mcontext; + +// TODO: Remove redundant #if checks when +// SB_MINIMUM_API_VERSION >= SB_SABI_FILE_VERSION. +#if SB_IS_ARCH_X86 || SB_IS_ARCH_X64 +#if SB_IS_64_BIT + // 64-bit X86 (aka X64) + ip_ = reinterpret_cast<void*>(mcontext.gregs[REG_RIP]); + sp_ = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]); + fp_ = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]); +#else + // 32-bit X86 + ip_ = reinterpret_cast<void*>(mcontext.gregs[REG_EIP]); + sp_ = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]); + fp_ = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]); +#endif +#elif SB_IS_ARCH_ARM || SB_IS_ARCH_ARM64 +#if SB_IS_64_BIT + // 64-bit ARM + ip_ = reinterpret_cast<void*>(mcontext.pc); + sp_ = reinterpret_cast<void*>(mcontext.sp); + fp_ = reinterpret_cast<void*>(mcontext.regs[29]); +#elif SB_IS_GLIBC && ((__GLIBC__ * 100 + __GLIBC_MINOR__) < 204) + // 32-bit ARM w/ old GLIBC that used gregs[] + ip_ = reinterpret_cast<void*>(mcontext.gregs[R15]); + sp_ = reinterpret_cast<void*>(mcontext.gregs[R13]); + fp_ = reinterpret_cast<void*>(mcontext.gregs[R11]); +#else + // 32-bit ARM + ip_ = reinterpret_cast<void*>(mcontext.arm_pc); + sp_ = reinterpret_cast<void*>(mcontext.arm_sp); + fp_ = reinterpret_cast<void*>(mcontext.arm_fp); +#endif +#else // SB_IS_ARCH_XXX +#error "SbThreadContext isn't implemented for this CPU architecture" +#endif // SB_IS_ARCH_XXX +} + +#endif // SB_API_VERSION >= 11
diff --git a/src/starboard/shared/pthread/thread_context_internal.h b/src/starboard/shared/pthread/thread_context_internal.h index 3effcd6..14ef86d 100644 --- a/src/starboard/shared/pthread/thread_context_internal.h +++ b/src/starboard/shared/pthread/thread_context_internal.h
@@ -15,11 +15,23 @@ #ifndef STARBOARD_SHARED_PTHREAD_THREAD_CONTEXT_INTERNAL_H_ #define STARBOARD_SHARED_PTHREAD_THREAD_CONTEXT_INTERNAL_H_ +#include <signal.h> + #include "starboard/thread.h" #if SB_API_VERSION >= 11 struct SbThreadContextPrivate { - ucontext_t* ucontext = nullptr; + explicit SbThreadContextPrivate(ucontext_t* ucontext); + + SbThreadContextPrivate() = default; + SbThreadContextPrivate(const SbThreadContextPrivate&) = default; + SbThreadContextPrivate(SbThreadContextPrivate&&) = default; + SbThreadContextPrivate& operator=(const SbThreadContextPrivate&) = default; + SbThreadContextPrivate& operator=(SbThreadContextPrivate&&) = default; + + void* ip_ = nullptr; + void* sp_ = nullptr; + void* fp_ = nullptr; }; #endif // SB_API_VERSION >= 11
diff --git a/src/starboard/shared/pthread/thread_sampler_internal.cc b/src/starboard/shared/pthread/thread_sampler_internal.cc index bca4da4..a56f8ec 100644 --- a/src/starboard/shared/pthread/thread_sampler_internal.cc +++ b/src/starboard/shared/pthread/thread_sampler_internal.cc
@@ -109,7 +109,7 @@ starboard::ScopedLock lock(GetMutex()); SB_DCHECK(frozen_sampler_ == sampler) << "SbThreadSampler didn't freeze."; if (frozen_sampler_ != sampler) return false; - sb_context_.ucontext = nullptr; + sb_context_ = SbThreadContextPrivate(); sem_post(&thaw_semaphore_); frozen_sampler_ = kSbThreadSamplerInvalid; return true; @@ -119,7 +119,7 @@ void* context) { SB_UNREFERENCED_PARAMETER(info); if (signal != SIGPROF) return; - sb_context_.ucontext = reinterpret_cast<ucontext_t*>(context); + sb_context_ = SbThreadContextPrivate(reinterpret_cast<ucontext_t*>(context)); // |Freeze| can return the context now. sem_post(&freeze_semaphore_); // Keep this thread frozen until |Thaw| is called.
diff --git a/src/starboard/shared/starboard/player/filter/video_frame_cadence_pattern_generator.cc b/src/starboard/shared/starboard/player/filter/video_frame_cadence_pattern_generator.cc index 83ccb11..e291f6f 100644 --- a/src/starboard/shared/starboard/player/filter/video_frame_cadence_pattern_generator.cc +++ b/src/starboard/shared/starboard/player/filter/video_frame_cadence_pattern_generator.cc
@@ -75,9 +75,9 @@ } void VideoFrameCadencePatternGenerator::AdvanceToNextFrame() { - SB_DCHECK(refresh_rate_ != kInvalidRefreshRate); - SB_DCHECK(frame_rate_ != kInvalidFrameRate); - + // It is possible that AdvanceToNextFrame() is called before refresh rate and + // frame rate are set. This can happen when the platform release the frame on + // rendering. ++frame_index_; }
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc index fd6f379..c8dfbdd 100644 --- a/src/starboard/shared/starboard/player/player_create.cc +++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -33,6 +33,49 @@ FilterBasedPlayerWorkerHandler; using starboard::shared::starboard::player::PlayerWorker; +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +SbPlayer SbPlayerCreate(SbWindow window, + const SbPlayerCreationParam* creation_param, + SbPlayerDeallocateSampleFunc sample_deallocate_func, + SbPlayerDecoderStatusFunc decoder_status_func, + SbPlayerStatusFunc player_status_func, + SbPlayerErrorFunc player_error_func, + void* context, + SbDecodeTargetGraphicsContextProvider* provider) { + if (!creation_param) { + SB_LOG(ERROR) << "CreationParam cannot be null."; + return kSbPlayerInvalid; + } + if (!creation_param->audio_mime) { + SB_LOG(ERROR) << "creation_param->audio_mime cannot be null."; + return kSbPlayerInvalid; + } + if (!creation_param->video_mime) { + SB_LOG(ERROR) << "creation_param->video_mime cannot be null."; + return kSbPlayerInvalid; + } + if (!creation_param->max_video_capabilities) { + SB_LOG(ERROR) << "creation_param->max_video_capabilities cannot be null."; + return kSbPlayerInvalid; + } + + SB_LOG(INFO) << "SbPlayerCreate() called with audio mime \"" + << creation_param->audio_mime << "\", video mime \"" + << creation_param->video_mime + << "\", and max video capabilities \"" + << creation_param->max_video_capabilities << "\"."; + + SbMediaAudioCodec audio_codec = creation_param->audio_sample_info.codec; + SbMediaVideoCodec video_codec = creation_param->video_sample_info.codec; + SbDrmSystem drm_system = creation_param->drm_system; + const SbMediaAudioSampleInfo* audio_sample_info = + &creation_param->audio_sample_info; + const char* max_video_capabilities = creation_param->max_video_capabilities; + const auto output_mode = creation_param->output_mode; + +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + SbPlayer SbPlayerCreate(SbWindow window, SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, @@ -53,6 +96,8 @@ void* context, SbPlayerOutputMode output_mode, SbDecodeTargetGraphicsContextProvider* provider) { +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + SB_UNREFERENCED_PARAMETER(window); #if SB_API_VERSION >= 11 SB_UNREFERENCED_PARAMETER(max_video_capabilities); @@ -120,10 +165,17 @@ return kSbPlayerInvalid; } +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + if (SbPlayerGetPreferredOutputMode(creation_param) != output_mode) { + SB_LOG(ERROR) << "Unsupported player output mode " << output_mode; + return kSbPlayerInvalid; + } +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) if (!SbPlayerOutputModeSupported(output_mode, video_codec, drm_system)) { SB_LOG(ERROR) << "Unsupported player output mode " << output_mode; return kSbPlayerInvalid; } +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) UpdateActiveSessionPlatformPlaybackState(kPlaying);
diff --git a/src/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc b/src/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc new file mode 100644 index 0000000..f69b6d7 --- /dev/null +++ b/src/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc
@@ -0,0 +1,74 @@ +// Copyright 2019 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/player.h" + +#include <algorithm> + +#include "starboard/configuration.h" +#include "starboard/shared/starboard/player/filter/video_decoder_internal.h" + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +SbPlayerOutputMode SbPlayerGetPreferredOutputMode( + const SbPlayerCreationParam* creation_param) { + using starboard::shared::starboard::player::filter::VideoDecoder; + + if (!creation_param) { + SB_LOG(ERROR) << "creation_param cannot be NULL"; + return kSbPlayerOutputModeInvalid; + } + + if (!creation_param->audio_mime) { + SB_LOG(ERROR) << "creation_param->audio_mime cannot be NULL"; + return kSbPlayerOutputModeInvalid; + } + + if (!creation_param->video_mime) { + SB_LOG(ERROR) << "creation_param->video_mime cannot be NULL"; + return kSbPlayerOutputModeInvalid; + } + + if (!creation_param->max_video_capabilities) { + SB_LOG(ERROR) << "creation_param->max_video_capabilities cannot be NULL"; + return kSbPlayerOutputModeInvalid; + } + + auto codec = creation_param->video_sample_info.codec; + auto drm_system = creation_param->drm_system; + + SbPlayerOutputMode output_modes_to_check[] = { + kSbPlayerOutputModePunchOut, kSbPlayerOutputModeDecodeToTexture, + }; + + // Check |kSbPlayerOutputModeDecodeToTexture| first if the caller prefers it. + if (creation_param->output_mode == kSbPlayerOutputModeDecodeToTexture) { + std::swap(output_modes_to_check[0], output_modes_to_check[1]); + } + + if (VideoDecoder::OutputModeSupported(output_modes_to_check[0], codec, + drm_system)) { + return output_modes_to_check[0]; + } + + if (VideoDecoder::OutputModeSupported(output_modes_to_check[1], codec, + drm_system)) { + return output_modes_to_check[1]; + } + + SB_NOTREACHED(); + return kSbPlayerOutputModeInvalid; +} + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/shared/starboard/player/player_output_mode_supported.cc b/src/starboard/shared/starboard/player/player_output_mode_supported.cc index e3af869..beadfb7 100644 --- a/src/starboard/shared/starboard/player/player_output_mode_supported.cc +++ b/src/starboard/shared/starboard/player/player_output_mode_supported.cc
@@ -18,6 +18,8 @@ #include "starboard/configuration.h" #include "starboard/shared/starboard/player/filter/video_decoder_internal.h" +#if !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + bool SbPlayerOutputModeSupported(SbPlayerOutputMode output_mode, SbMediaVideoCodec codec, SbDrmSystem drm_system) { @@ -25,3 +27,4 @@ OutputModeSupported(output_mode, codec, drm_system); } +#endif // !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc index d7d2243..ff11851 100644 --- a/src/starboard/shared/stub/player_create.cc +++ b/src/starboard/shared/stub/player_create.cc
@@ -14,6 +14,24 @@ #include "starboard/player.h" +#include "starboard/configuration.h" + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +SbPlayer SbPlayerCreate( + SbWindow /*window*/, + const SbPlayerCreationParam* /*creation_param*/, + SbPlayerDeallocateSampleFunc /*sample_deallocate_func*/, + SbPlayerDecoderStatusFunc /*decoder_status_func*/, + SbPlayerStatusFunc /*player_status_func*/, + SbPlayerErrorFunc /*player_error_func*/, + void* /*context*/, + SbDecodeTargetGraphicsContextProvider* /*context_provider*/) { + return kSbPlayerInvalid; +} + +#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + SbPlayer SbPlayerCreate(SbWindow /*window*/, SbMediaVideoCodec /*video_codec*/, SbMediaAudioCodec /*audio_codec*/, @@ -34,3 +52,5 @@ SbDecodeTargetGraphicsContextProvider* /*provider*/) { return kSbPlayerInvalid; } + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/shared/stub/player_get_preferred_output_mode.cc b/src/starboard/shared/stub/player_get_preferred_output_mode.cc new file mode 100644 index 0000000..f52fcc3 --- /dev/null +++ b/src/starboard/shared/stub/player_get_preferred_output_mode.cc
@@ -0,0 +1,26 @@ +// Copyright 2019 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/player.h" + +#include "starboard/configuration.h" + +#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + +SbPlayerOutputMode SbPlayerGetPreferredOutputMode( + const SbPlayerCreationParam* /*creation_param*/) { + return kSbPlayerOutputModeInvalid; +} + +#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/shared/stub/player_output_mode_supported.cc b/src/starboard/shared/stub/player_output_mode_supported.cc index 3b3f7dc..2b470fa 100644 --- a/src/starboard/shared/stub/player_output_mode_supported.cc +++ b/src/starboard/shared/stub/player_output_mode_supported.cc
@@ -14,8 +14,12 @@ #include "starboard/player.h" +#if !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) + bool SbPlayerOutputModeSupported(SbPlayerOutputMode /*output_mode*/, SbMediaVideoCodec /*codec*/, SbDrmSystem /*drm_system*/) { return false; } + +#endif // !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/stub/BUILD.gn b/src/starboard/stub/BUILD.gn index cfeb1ba..be648ee 100644 --- a/src/starboard/stub/BUILD.gn +++ b/src/starboard/stub/BUILD.gn
@@ -279,6 +279,7 @@ "//starboard/shared/stub/player_destroy.cc", "//starboard/shared/stub/player_get_current_frame.cc", "//starboard/shared/stub/player_get_info.cc", + "//starboard/shared/stub/player_get_preferred_output_mode.cc", "//starboard/shared/stub/player_output_mode_supported.cc", "//starboard/shared/stub/player_seek.cc", "//starboard/shared/stub/player_set_bounds.cc",
diff --git a/src/starboard/stub/stub_sources.gypi b/src/starboard/stub/stub_sources.gypi index 572bc18..ac44bed 100644 --- a/src/starboard/stub/stub_sources.gypi +++ b/src/starboard/stub/stub_sources.gypi
@@ -150,6 +150,7 @@ '<(DEPTH)/starboard/shared/stub/player_get_info.cc', '<(DEPTH)/starboard/shared/stub/player_get_info2.cc', '<(DEPTH)/starboard/shared/stub/player_get_maximum_number_of_samples_per_write.cc', + '<(DEPTH)/starboard/shared/stub/player_get_preferred_output_mode.cc', '<(DEPTH)/starboard/shared/stub/player_output_mode_supported.cc', '<(DEPTH)/starboard/shared/stub/player_seek.cc', '<(DEPTH)/starboard/shared/stub/player_seek2.cc',
diff --git a/src/starboard/tools/app_launcher_packager.py b/src/starboard/tools/app_launcher_packager.py index 9be2843..5114a5b 100644 --- a/src/starboard/tools/app_launcher_packager.py +++ b/src/starboard/tools/app_launcher_packager.py
@@ -142,7 +142,9 @@ for p in starboard.tools.platform.GetAll(): platform_path = os.path.relpath( starboard.tools.platform.Get(p).path, repo_root) - platforms_map[p] = platform_path + # Store posix paths even on Windows so MH Linux hosts can use them. + # The template has code to re-normalize them when used on Windows hosts. + platforms_map[p] = platform_path.replace('\\', '/') template = jinja2.Template( open(os.path.join(current_dir, 'platform.py.template')).read()) with open(os.path.join(dest_dir, 'platform.py'), 'w+') as f:
diff --git a/src/starboard/tools/platform.py.template b/src/starboard/tools/platform.py.template index 7a211bc..17d55b5 100644 --- a/src/starboard/tools/platform.py.template +++ b/src/starboard/tools/platform.py.template
@@ -21,6 +21,7 @@ # The name->platform path mapping. _PATH_MAP = {{platforms_map}} +_PATH_MAP = {k:os.path.normpath(v) for k,v in _PATH_MAP.iteritems()} # Cache of the name->PlatformInfo mapping.
diff --git a/src/third_party/proxy_py/proxy.py b/src/third_party/proxy_py/proxy.py index 7b4a8ec..6b614a6 100755 --- a/src/third_party/proxy_py/proxy.py +++ b/src/third_party/proxy_py/proxy.py
@@ -630,10 +630,11 @@ Subclass MUST implement `handle` method. It accepts an instance of accepted `Client` connection. """ - def __init__(self, hostname='127.0.0.1', port=8899, backlog=100): + def __init__(self, hostname='127.0.0.1', port=8899, backlog=100, client_ips=None): self.hostname = hostname self.port = port self.backlog = backlog + self.client_ips = client_ips self.socket = None def handle(self, client): @@ -648,6 +649,12 @@ self.socket.listen(self.backlog) while True: conn, addr = self.socket.accept() + if self.client_ips and addr[0] not in self.client_ips: + logger.warning('Closing socket on rejected client IP %s' % addr[0]) + conn.shutdown(socket.SHUT_RDWR) + conn.close() + continue + logger.info('Handling socket on accepted client IP %s' % addr[0]) client = Client(conn, addr) self.handle(client) except Exception as e: @@ -665,8 +672,8 @@ def __init__(self, hostname='127.0.0.1', port=8899, backlog=100, auth_code=None, server_recvbuf_size=8192, client_recvbuf_size=8192, - host_resolver=None): - super(HTTP, self).__init__(hostname, port, backlog) + host_resolver=None, client_ips=None): + super(HTTP, self).__init__(hostname, port, backlog, client_ips) self.auth_code = auth_code self.client_recvbuf_size = client_recvbuf_size self.server_recvbuf_size = server_recvbuf_size @@ -719,7 +726,9 @@ 'that proxy.py can open concurrently.') parser.add_argument('--log-level', default='INFO', help='DEBUG, INFO (default), WARNING, ERROR, CRITICAL') parser.add_argument('--host_resolver', default=None, help='Default: No host resolution. ' - 'JSON hosts file used for hostname IP resolution.') + 'JSON hosts file used for hostname IP resolution.') + parser.add_argument('--client_ips', default=None, nargs='*', help='Default: No client IP restriction. ' + 'The only client IPs that the proxy will accept.') args = parser.parse_args() logging.basicConfig(level=getattr(logging, args.log_level), @@ -743,7 +752,8 @@ auth_code=auth_code, server_recvbuf_size=int(args.server_recvbuf_size), client_recvbuf_size=int(args.client_recvbuf_size), - host_resolver=host_resolver) + host_resolver=host_resolver, + client_ips=args.client_ips) proxy.run() except KeyboardInterrupt: pass