diff --git a/.github/actions/build/action.yaml b/.github/actions/build/action.yaml index 28616c4afed4..927ec0bec326 100644 --- a/.github/actions/build/action.yaml +++ b/.github/actions/build/action.yaml @@ -17,14 +17,14 @@ runs: run: | set -x env - if [ -z ${COBALT_BOOTLOADER+x} ]; then + if [ -z ${COBALT_EVERGREEN_LOADER+x} ]; then BUILD_PLATFORM=${{ matrix.target_platform }} BUILD_TARGET=all if [[ "${{matrix.config}}" =~ ^(qa|gold)$ ]]; then BUILD_TARGET=default fi else - BUILD_PLATFORM=${COBALT_BOOTLOADER} + BUILD_PLATFORM=${COBALT_EVERGREEN_LOADER} BUILD_TARGET='loader_app_install elf_loader_sandbox_install native_target/crashpad_handler' fi # GitHub Runners have home set to /github/home. diff --git a/.github/actions/gn/action.yaml b/.github/actions/gn/action.yaml index 7afac0350b1e..ba027017a1cd 100644 --- a/.github/actions/gn/action.yaml +++ b/.github/actions/gn/action.yaml @@ -22,13 +22,13 @@ runs: run: | set -x extra_arguments="${{matrix.extra_gn_arguments}}" - if [ -z ${COBALT_BOOTLOADER+x} ]; then + if [ -z ${COBALT_EVERGREEN_LOADER+x} ]; then BUILD_PLATFORM=${{ matrix.target_platform }} else - BUILD_PLATFORM=${COBALT_BOOTLOADER} - if [ ! -z "${{matrix.bootloader_extra_gn_arguments}}" ] + BUILD_PLATFORM=${COBALT_EVERGREEN_LOADER} + if [ ! -z "${{matrix.evergreen_loader_extra_gn_arguments}}" ] then - extra_arguments="${{matrix.bootloader_extra_gn_arguments}}" + extra_arguments="${{matrix.evergreen_loader_extra_gn_arguments}}" fi fi gn gen $GITHUB_WORKSPACE/out/${BUILD_PLATFORM}_${{matrix.config}} --args="target_platform=\"${BUILD_PLATFORM}\" ${{matrix.sb_api_version}} ${{matrix.target_os}} ${{matrix.target_cpu}} ${extra_arguments} is_internal_build=false build_type=\"${{matrix.config}}\"" diff --git a/.github/actions/on_device_tests/action.yaml b/.github/actions/on_device_tests/action.yaml index 7b9ef1e98983..c981288dafe7 100644 --- a/.github/actions/on_device_tests/action.yaml +++ b/.github/actions/on_device_tests/action.yaml @@ -23,9 +23,9 @@ runs: echo "WORKFLOW=${WORKFLOW}" >> $GITHUB_ENV # Boot loader env - if [ "${COBALT_BOOTLOADER}" != "null" ]; then + if [ "${COBALT_EVERGREEN_LOADER}" != "null" ]; then echo "LOADER_CONFIG=${{ matrix.config }}" >> $GITHUB_ENV - echo "LOADER_PLATFORM=${COBALT_BOOTLOADER}" >> $GITHUB_ENV + echo "LOADER_PLATFORM=${COBALT_EVERGREEN_LOADER}" >> $GITHUB_ENV fi # Dimension env diff --git a/.github/actions/on_host_test/action.yaml b/.github/actions/on_host_test/action.yaml index a10a8bf2af5d..c751de0de0f9 100644 --- a/.github/actions/on_host_test/action.yaml +++ b/.github/actions/on_host_test/action.yaml @@ -39,21 +39,21 @@ runs: fi python3 ${GITHUB_WORKSPACE}/tools/create_archive.py -x -s ${GITHUB_WORKSPACE}/out/tmp/${{matrix.platform}}_${{matrix.config}}.${ARCHIVE_EXTENSION} -d ${GITHUB_WORKSPACE}/out ${parallel} rm -rf ${GITHUB_WORKSPACE}/out/tmp - - name: Download Bootloader Archive - if: ${{ env.COBALT_BOOTLOADER != null && env.COBALT_BOOTLOADER != 'null' }} + - name: Download Evergreen loader Archive + if: ${{ env.COBALT_EVERGREEN_LOADER != null && env.COBALT_EVERGREEN_LOADER != 'null' }} shell: bash env: WORKFLOW: ${{ github.workflow }} run: | set -x PROJECT_NAME=$(gcloud config get-value project) - gsutil cp gs://${PROJECT_NAME}-test-artifacts/${WORKFLOW}/${GITHUB_RUN_NUMBER}/${{matrix.platform}}_${{matrix.config}}/${COBALT_BOOTLOADER}_${{matrix.config}}.${ARCHIVE_EXTENSION} ${GITHUB_WORKSPACE}/out/tmp/${COBALT_BOOTLOADER}_${{matrix.config}}.${ARCHIVE_EXTENSION} - - name: Extract Bootloader Archive - if: ${{ env.COBALT_BOOTLOADER != null && env.COBALT_BOOTLOADER != 'null' }} + gsutil cp gs://${PROJECT_NAME}-test-artifacts/${WORKFLOW}/${GITHUB_RUN_NUMBER}/${{matrix.platform}}_${{matrix.config}}/${COBALT_EVERGREEN_LOADER}_${{matrix.config}}.${ARCHIVE_EXTENSION} ${GITHUB_WORKSPACE}/out/tmp/${COBALT_EVERGREEN_LOADER}_${{matrix.config}}.${ARCHIVE_EXTENSION} + - name: Extract Evergreen loader Archive + if: ${{ env.COBALT_EVERGREEN_LOADER != null && env.COBALT_EVERGREEN_LOADER != 'null' }} shell: bash run: | set -x - python3 ${GITHUB_WORKSPACE}/tools/create_archive.py -x -s ${GITHUB_WORKSPACE}/out/tmp/${COBALT_BOOTLOADER}_${{matrix.config}}.${ARCHIVE_EXTENSION} -d ${GITHUB_WORKSPACE}/out --parallel + python3 ${GITHUB_WORKSPACE}/tools/create_archive.py -x -s ${GITHUB_WORKSPACE}/out/tmp/${COBALT_EVERGREEN_LOADER}_${{matrix.config}}.${ARCHIVE_EXTENSION} -d ${GITHUB_WORKSPACE}/out --parallel rm -rf ${GITHUB_WORKSPACE}/out/tmp - name: Set Env Variables shell: bash @@ -66,8 +66,8 @@ runs: run: | set -x loader_args='' - if [ "${COBALT_BOOTLOADER}" != "null" ]; then - loader_args="--loader_platform ${COBALT_BOOTLOADER} --loader_config ${{matrix.config}}" + if [ "${COBALT_EVERGREEN_LOADER}" != "null" ]; then + loader_args="--loader_platform ${COBALT_EVERGREEN_LOADER} --loader_config ${{matrix.config}}" fi if [[ "${{matrix.shard}}" == 'integration' ]]; then xvfb-run -a --server-args="-screen 0 1920x1080x24i +render +extension GLX -noreset" python3 $GITHUB_WORKSPACE/cobalt/black_box_tests/black_box_tests.py --platform ${{matrix.target_platform}} --config ${{matrix.config}} ${loader_args} diff --git a/.github/actions/upload_test_artifacts/action.yaml b/.github/actions/upload_test_artifacts/action.yaml index d2923eb553a7..33eaa063fc0f 100644 --- a/.github/actions/upload_test_artifacts/action.yaml +++ b/.github/actions/upload_test_artifacts/action.yaml @@ -18,13 +18,13 @@ runs: run: | set -x project_name=$(gcloud config get-value project) - if [ -z ${COBALT_BOOTLOADER+x} ] + if [ -z ${COBALT_EVERGREEN_LOADER+x} ] then PLATFORM=${{matrix.platform}} echo "TARGET_PLATFORM=${{matrix.target_platform}}" >> $GITHUB_ENV else - PLATFORM=${COBALT_BOOTLOADER} - echo "TARGET_PLATFORM=${COBALT_BOOTLOADER}" >> $GITHUB_ENV + PLATFORM=${COBALT_EVERGREEN_LOADER} + echo "TARGET_PLATFORM=${COBALT_EVERGREEN_LOADER}" >> $GITHUB_ENV fi if [ "${{ inputs.type }}" == 'ondevice' ] @@ -56,9 +56,9 @@ runs: if [ "${{ inputs.type }}" == 'ondevice' ] then outdir="$GITHUB_WORKSPACE/out/${{matrix.target_platform}}_${{matrix.config}}" - if [ -n "${COBALT_BOOTLOADER}" ] + if [ -n "${COBALT_EVERGREEN_LOADER}" ] then - outdir="${outdir} $GITHUB_WORKSPACE/out/${COBALT_BOOTLOADER}_${{matrix.config}}" + outdir="${outdir} $GITHUB_WORKSPACE/out/${COBALT_EVERGREEN_LOADER}_${{matrix.config}}" fi python3 $GITHUB_WORKSPACE/tools/create_archive.py --test_infra -d ${{env.ARCHIVE_FILE}} -s ${outdir} elif [ "${{ inputs.type }}" == 'onhost' ] diff --git a/.github/config/evergreen-arm-hardfp.json b/.github/config/evergreen-arm-hardfp.json index 8067c723440a..13b3cd366bb8 100644 --- a/.github/config/evergreen-arm-hardfp.json +++ b/.github/config/evergreen-arm-hardfp.json @@ -1,6 +1,6 @@ { "docker_service": "build-raspi", - "bootloader": "raspi-2", + "evergreen_loader": "raspi-2", "on_device_test": { "enabled": true, "tests": [ @@ -25,7 +25,7 @@ "target_platform":"evergreen-arm-hardfp", "target_cpu":"target_cpu=\\\"arm\\\"", "extra_gn_arguments":"use_asan=false", - "bootloader_extra_gn_arguments": "use_asan=false is_clang=false", + "evergreen_loader_extra_gn_arguments": "use_asan=false is_clang=false", "dimension": "release_version=regex:10.*" }, { @@ -34,7 +34,7 @@ "target_platform":"evergreen-arm-hardfp", "target_cpu":"target_cpu=\\\"arm\\\"", "extra_gn_arguments":"use_asan=false", - "bootloader_extra_gn_arguments":"use_asan=false is_clang=false", + "evergreen_loader_extra_gn_arguments":"use_asan=false is_clang=false", "sb_api_version": "sb_api_version=15", "dimension": "release_version=regex:10.*" }, @@ -44,7 +44,7 @@ "target_platform":"evergreen-arm-hardfp", "target_cpu":"target_cpu=\\\"arm\\\"", "extra_gn_arguments":"use_asan=false", - "bootloader_extra_gn_arguments":"use_asan=false is_clang=false", + "evergreen_loader_extra_gn_arguments":"use_asan=false is_clang=false", "sb_api_version": "sb_api_version=14", "dimension": "release_version=regex:10.*" }, @@ -54,7 +54,7 @@ "target_platform":"evergreen-arm-hardfp", "target_cpu":"target_cpu=\\\"arm\\\"", "extra_gn_arguments":"use_asan=false", - "bootloader_extra_gn_arguments":"use_asan=false is_clang=false", + "evergreen_loader_extra_gn_arguments":"use_asan=false is_clang=false", "sb_api_version": "sb_api_version=13", "dimension": "release_version=regex:10.*" } diff --git a/.github/config/evergreen-x64.json b/.github/config/evergreen-x64.json index b95a3183741a..0f18b571e021 100644 --- a/.github/config/evergreen-x64.json +++ b/.github/config/evergreen-x64.json @@ -1,7 +1,7 @@ { "docker_service": "build-linux-evergreen", "on_host_test": true, - "bootloader": "linux-x64x11", + "evergreen_loader": "linux-x64x11", "on_host_test_shards": ["0", "1", "2", "3", "blackbox", "wpt"], "platforms": [ "evergreen-x64", diff --git a/.github/config/linux.json b/.github/config/linux.json index 79ceb0bbd36a..e1efe4b1c102 100644 --- a/.github/config/linux.json +++ b/.github/config/linux.json @@ -1,5 +1,6 @@ { "docker_service": "build-linux", + "evergreen_loader": "linux-x64x11", "on_host_test": true, "on_host_test_shards": ["0", "1", "2", "3", "blackbox", "wpt"], "platforms": [ diff --git a/.github/workflows/android_24.lts.1+.yaml b/.github/workflows/android_24.lts.1+.yaml index cec9f87e4c68..18e7546ca54b 100644 --- a/.github/workflows/android_24.lts.1+.yaml +++ b/.github/workflows/android_24.lts.1+.yaml @@ -1,6 +1,8 @@ name: android_24.lts.1+ on: + release: + types: [prereleased, released, published] pull_request: types: [ready_for_review, opened, reopened, synchronize, labeled] branches: @@ -28,6 +30,7 @@ jobs: with: platform: android-arm64 nightly: ${{ github.event.inputs.nightly }} + keep_artifacts: cobalt.apk android-x86: uses: ./.github/workflows/main.yaml permissions: @@ -36,6 +39,7 @@ jobs: with: platform: android-x86 nightly: ${{ github.event.inputs.nightly }} + keep_artifacts: cobalt.apk android-arm: uses: ./.github/workflows/main.yaml permissions: @@ -44,3 +48,49 @@ jobs: with: platform: android-arm nightly: ${{ github.event.inputs.nightly }} + keep_artifacts: cobalt.apk + + upload-release-artifacts: + runs-on: ubuntu-latest + needs: [ android-arm, android-arm64, android-x86 ] + permissions: + actions: write + steps: + - name: Download arm-gold apk + uses: actions/download-artifact@v4 + with: + name: android-arm-gold + path: arm-gold + - name: Download arm-qa apk + uses: actions/download-artifact@v4 + with: + name: android-arm-qa + path: arm-qa + - name: Download arm64-gold apk + uses: actions/download-artifact@v4 + with: + name: android-arm64-gold + path: arm64-gold + - name: Download arm64-qa apk + uses: actions/download-artifact@v4 + with: + name: android-arm64-gold + path: arm64-qa + - name: Download x86-gold apk + uses: actions/download-artifact@v4 + with: + name: android-x86-gold + path: x86-gold + - name: Download x86-qa apk + uses: actions/download-artifact@v4 + with: + name: android-x86-qa + path: x86-qa + - name: 'Upload Android APKs' + uses: actions/upload-artifact@v4 + with: + name: Android APKs + path: ./* + retention-days: 90 + compression-level: 0 # We expect kept artifacts to be already compressed + if-no-files-found: error diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 9ccebb8620df..4717d45d8a5e 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -29,6 +29,11 @@ on: required: false type: boolean default: false + keep_artifacts: + description: 'Which artifacts to keep for releases' + required: false + type: string + default: '' # Global env vars. env: @@ -100,8 +105,8 @@ jobs: run: echo "on_host_test=$(cat ${GITHUB_WORKSPACE}/.github/config/${{ inputs.platform }}.json | jq -rc '.on_host_test')" >> $GITHUB_ENV - id: set-on-host-test-shards run: echo "on_host_test_shards=$(cat ${GITHUB_WORKSPACE}/.github/config/${{ inputs.platform }}.json | jq -c '.on_host_test_shards')" >> $GITHUB_ENV - - id: set-on-host-test-bootloader - run: echo "bootloader=$(cat ${GITHUB_WORKSPACE}/.github/config/${{ inputs.platform }}.json | jq -rc '.bootloader')" >> $GITHUB_ENV + - id: set-on-host-test-evergreen-loader + run: echo "evergreen_loader=$(cat ${GITHUB_WORKSPACE}/.github/config/${{ inputs.platform }}.json | jq -rc '.evergreen_loader')" >> $GITHUB_ENV - id: set-docker-service run: | echo "docker_service=$(cat ${GITHUB_WORKSPACE}/.github/config/${{ inputs.platform }}.json | jq -r '.docker_service')" >> $GITHUB_ENV @@ -113,7 +118,7 @@ jobs: on_device_test_attempts: ${{ env.on_device_test_attempts }} on_host_test: ${{ env.on_host_test }} on_host_test_shards: ${{ env.on_host_test_shards }} - bootloader: ${{ env.bootloader }} + evergreen_loader: ${{ env.evergreen_loader }} docker_service: ${{ env.docker_service }} # Builds, tags, and pushes Cobalt docker build images to ghr. @@ -210,34 +215,43 @@ jobs: uses: ./.github/actions/gn - name: Build Cobalt uses: ./.github/actions/build + - name: 'Upload Artifact' + uses: actions/upload-artifact@v4 + if: inputs.keep_artifacts + with: + name: ${{ matrix.platform }}-${{ matrix.config }} + path: out/${{ matrix.platform }}_${{ matrix.config }}/${{ inputs.keep_artifacts }} + retention-days: 7 + compression-level: 0 # We expect kept artifacts to be already compressed + if-no-files-found: error - name: Run API Leak Detector uses: ./.github/actions/api_leak_detector if: inputs.run_api_leak_detector with: relative_manifest_path: ${{ inputs.leak_manifest_filename }} - - name: Upload Nightly Artifacts - if: ${{ ( inputs.nightly == 'true' || github.event_name == 'schedule' ) && matrix.config != 'debug' }} - uses: ./.github/actions/upload_nightly_artifacts - name: Upload On Host Test Artifacts if: ${{ matrix.config == 'devel' && needs.initialize.outputs.on_host_test == 'true' }} uses: ./.github/actions/upload_test_artifacts with: type: onhost os: linux - # For some reason passing needs.initialize.outputs.bootloader as parameter to build + # For some reason passing needs.initialize.outputs.evergreen_loader as parameter to build # action didn't work, so instead we set an env var. - - name: Set bootloader config - if: ${{ needs.initialize.outputs.bootloader != 'null' }} - run: echo "COBALT_BOOTLOADER=${{needs.initialize.outputs.bootloader}}" >> $GITHUB_ENV - # Build bootloader for on-host tests if necessary. - - name: Bootloader GN - if: ${{ needs.initialize.outputs.bootloader != 'null' && matrix.config == 'devel' }} + - name: Set Evergreen loader config + if: ${{ needs.initialize.outputs.evergreen_loader != 'null' }} + run: echo "COBALT_EVERGREEN_LOADER=${{needs.initialize.outputs.evergreen_loader}}" >> $GITHUB_ENV + # Build Evergreen loader for on-host tests if necessary. + - name: Evergreen loader GN + if: ${{ needs.initialize.outputs.evergreen_loader != 'null' && ( matrix.config == 'devel' || matrix.config == 'qa' ) }} uses: ./.github/actions/gn - - name: Build Bootloader - if: ${{ needs.initialize.outputs.bootloader != 'null' && matrix.config == 'devel' }} + - name: Build Evergreen loader + if: ${{ needs.initialize.outputs.evergreen_loader != 'null' && ( matrix.config == 'devel' || matrix.config == 'qa' ) }} uses: ./.github/actions/build - - name: Upload Bootloader On Host Test Artifacts - if: ${{ needs.initialize.outputs.bootloader != 'null' && matrix.config == 'devel' && needs.initialize.outputs.on_host_test == 'true'}} + - name: Upload Nightly Artifacts + if: ${{ ( inputs.nightly == 'true' || github.event_name == 'schedule' ) && matrix.config != 'debug' }} + uses: ./.github/actions/upload_nightly_artifacts + - name: Upload Evergreen loader On Host Test Artifacts + if: ${{ needs.initialize.outputs.evergreen_loader != 'null' && matrix.config == 'devel' && needs.initialize.outputs.on_host_test == 'true'}} uses: ./.github/actions/upload_test_artifacts with: type: onhost @@ -279,7 +293,7 @@ jobs: shard: ${{ fromJson(needs.initialize.outputs.on_device_test).tests }} include: ${{ fromJson(needs.initialize.outputs.includes) }} env: - COBALT_BOOTLOADER: ${{ needs.initialize.outputs.bootloader }} + COBALT_EVERGREEN_LOADER: ${{ needs.initialize.outputs.evergreen_loader }} ON_DEVICE_TEST_ATTEMPTS: ${{ needs.initialize.outputs.on_device_test_attempts }} MODULAR_BUILD: ${{ inputs.modular && 1 || 0 }} steps: @@ -311,7 +325,7 @@ jobs: # For some reason tests complaining about HOME set to /github/home # with permission denied error. HOME: /root - COBALT_BOOTLOADER: ${{needs.initialize.outputs.bootloader}} + COBALT_EVERGREEN_LOADER: ${{needs.initialize.outputs.evergreen_loader}} MODULAR_BUILD: ${{ inputs.modular && 1 || 0 }} steps: - name: Checkout diff --git a/.gitignore b/.gitignore index 9a5a5caa9c01..31136b856f33 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ /venv/* _certs/ .coverage +compile_commands.json diff --git a/cobalt/base/tokens.h b/cobalt/base/tokens.h index 0b82b64e8116..908ce2d0a17e 100644 --- a/cobalt/base/tokens.h +++ b/cobalt/base/tokens.h @@ -116,6 +116,7 @@ namespace base { MacroOpWithNameOnly(resourcetimingbufferfull) \ MacroOpWithNameOnly(result) \ MacroOpWithNameOnly(resume) \ + MacroOpWithNameOnly(samplebufferfull) \ MacroOpWithNameOnly(scroll) \ MacroOpWithNameOnly(securitypolicyviolation) \ MacroOpWithNameOnly(seeked) \ diff --git a/cobalt/black_box_tests/black_box_tests.py b/cobalt/black_box_tests/black_box_tests.py index 7dc6fd7ab359..98430bda0f22 100755 --- a/cobalt/black_box_tests/black_box_tests.py +++ b/cobalt/black_box_tests/black_box_tests.py @@ -78,6 +78,7 @@ 'disable_eval_with_csp', 'h5vcc_storage_write_verify_test', 'http_cache', + 'javascript_profiler', 'persistent_cookie', 'scroll', 'service_worker_add_to_cache_test', diff --git a/cobalt/black_box_tests/testdata/javascript_profiler.html b/cobalt/black_box_tests/testdata/javascript_profiler.html new file mode 100644 index 000000000000..f268510db49a --- /dev/null +++ b/cobalt/black_box_tests/testdata/javascript_profiler.html @@ -0,0 +1,88 @@ + + + + JavaScript Profiler Test + + + + + + diff --git a/cobalt/black_box_tests/tests/javascript_profiler.py b/cobalt/black_box_tests/tests/javascript_profiler.py new file mode 100644 index 000000000000..33dabc7a0821 --- /dev/null +++ b/cobalt/black_box_tests/tests/javascript_profiler.py @@ -0,0 +1,40 @@ +# Copyright 2024 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. +"""Tests if Cobalt client page can use window.Profiler.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from cobalt.black_box_tests import black_box_tests +from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer + + +class JavaScriptProfilerTest(black_box_tests.BlackBoxTestCase): + """Ensure that the client can declare a `window.Profiler` object.""" + + def test_javascript_profiler_prime_profiler(self): + with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server: + modes = [ + 'testPrimeProfiler', + 'testSampleBufferFullProfiler', + 'testAbruptGarbageCollection', + ] + for mode in modes: + url = server.GetURL( + file_name=f'testdata/javascript_profiler.html?mode={mode}') + with self.CreateCobaltRunner(url=url) as runner: + runner.WaitForJSTestsSetup() + self.assertTrue(runner.JSTestsSucceeded(), + f'JavaScript profiler failed at case mode="{mode}".') diff --git a/cobalt/dom/html_media_element.cc b/cobalt/dom/html_media_element.cc index 45a49ca18f6c..ade06946cc33 100644 --- a/cobalt/dom/html_media_element.cc +++ b/cobalt/dom/html_media_element.cc @@ -644,21 +644,16 @@ void HTMLMediaElement::ScheduleEvent(const scoped_refptr& event) { event_queue_.Enqueue(event); } -std::string HTMLMediaElement::h5vcc_audio_connectors( - script::ExceptionState* exception_state) const { +std::string HTMLMediaElement::h5vcc_audio_connectors() const { #if SB_API_VERSION >= 15 if (!player_) { - web::DOMException::Raise(web::DOMException::kInvalidStateErr, - exception_state); - return std::string(); + return ""; } std::vector configs = player_->GetAudioConnectors(); return base::JoinString(configs, ";"); #else // SB_API_VERSION >= 15 - web::DOMException::Raise(web::DOMException::kNotSupportedErr, - exception_state); - return std::string(); + return ""; #endif // SB_API_VERSION >= 15 } diff --git a/cobalt/dom/html_media_element.h b/cobalt/dom/html_media_element.h index 559c72a0ab60..f4eefebf3a1e 100644 --- a/cobalt/dom/html_media_element.h +++ b/cobalt/dom/html_media_element.h @@ -149,8 +149,7 @@ class HTMLMediaElement : public HTMLElement, // Returns semicolon separated names of audio connectors, like // "hdmi;bluetooth". // TODO(b/267678497): The current interface is tentative, to be refined. - std::string h5vcc_audio_connectors( - script::ExceptionState* exception_state) const; + std::string h5vcc_audio_connectors() const; // Set max video capabilities. void SetMaxVideoCapabilities(const std::string& max_video_capabilities, diff --git a/cobalt/dom/html_media_element.idl b/cobalt/dom/html_media_element.idl index 401060a6f7b7..f3b79173e6c6 100644 --- a/cobalt/dom/html_media_element.idl +++ b/cobalt/dom/html_media_element.idl @@ -63,8 +63,7 @@ interface HTMLMediaElement : HTMLElement { attribute boolean muted; // non standard, semicolon separated names of audio connectors, like - // "hdmi;bluetooth". It raises `NotSupportedError` on apps doesn't support - // this feature, or `InvalidStateError` if there isn't an active playback. + // "hdmi;bluetooth". // TODO(b/267678497): The current interface is tentative, to be refined. - [RaisesException] readonly attribute DOMString h5vccAudioConnectors; + readonly attribute DOMString h5vccAudioConnectors; }; diff --git a/cobalt/js_profiler/BUILD.gn b/cobalt/js_profiler/BUILD.gn index 2a025d16f9e3..026c22065972 100644 --- a/cobalt/js_profiler/BUILD.gn +++ b/cobalt/js_profiler/BUILD.gn @@ -16,6 +16,8 @@ static_library("js_profiler") { sources = [ "profiler.cc", "profiler.h", + "profiler_group.cc", + "profiler_group.h", "profiler_trace_builder.cc", "profiler_trace_builder.h", "profiler_trace_wrapper.h", diff --git a/cobalt/js_profiler/js_profiler_test.cc b/cobalt/js_profiler/js_profiler_test.cc index 98bb584a206b..c2c52fa07a92 100644 --- a/cobalt/js_profiler/js_profiler_test.cc +++ b/cobalt/js_profiler/js_profiler_test.cc @@ -35,6 +35,10 @@ class ProfilerTest : public dom::testing::TestWithJavaScript { public: ProfilerTest() {} + void CollectGarbage() { + window_.web_context()->javascript_engine()->CollectGarbage(); + } + protected: dom::testing::StubWindow window_; StrictMock exception_state_; @@ -111,5 +115,42 @@ TEST_F(ProfilerTest, ProfilerJSCode) { EXPECT_TRUE(EvaluateScript("Profiler", &result)); EXPECT_EQ(result, "function Profiler() { [native code] }"); } + +TEST_F(ProfilerTest, ProfilerGroupDisposesOfCpuProfiler) { + v8::HandleScope scope(web::get_isolate(window_.environment_settings())); + ProfilerInitOptions init_options; + init_options.set_sample_interval(10); + init_options.set_max_buffer_size(1); + + auto profiler_group = ProfilerGroup::From(window_.environment_settings()); + EXPECT_FALSE(profiler_group->active()); + EXPECT_EQ(profiler_group->num_active_profilers(), 0); + + scoped_refptr profiler_(new Profiler( + window_.environment_settings(), init_options, &exception_state_)); + EXPECT_EQ(profiler_group->num_active_profilers(), 1); + EXPECT_TRUE(profiler_group->active()); + + auto promise = profiler_->Stop(window_.environment_settings()); + EXPECT_EQ(profiler_->stopped(), true); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(profiler_group->num_active_profilers(), 0); + EXPECT_FALSE(profiler_group->active()); +} + +TEST_F(ProfilerTest, ProfilerCanBeCancelled) { + v8::HandleScope scope(web::get_isolate(window_.environment_settings())); + ProfilerInitOptions init_options; + init_options.set_sample_interval(10); + init_options.set_max_buffer_size(1000); + + scoped_refptr profiler_(new Profiler( + window_.environment_settings(), init_options, &exception_state_)); + + profiler_->Cancel(); + EXPECT_EQ(profiler_->stopped(), true); +} + } // namespace js_profiler } // namespace cobalt diff --git a/cobalt/js_profiler/profiler.cc b/cobalt/js_profiler/profiler.cc index c380aed7c53f..21e4cdddb184 100644 --- a/cobalt/js_profiler/profiler.cc +++ b/cobalt/js_profiler/profiler.cc @@ -14,45 +14,27 @@ #include "cobalt/js_profiler/profiler.h" -#include #include #include #include #include -#include "cobalt/base/polymorphic_downcast.h" -#include "cobalt/js_profiler/profiler_trace_builder.h" +#include "base/logging.h" #include "cobalt/js_profiler/profiler_trace_wrapper.h" #include "cobalt/web/cache_utils.h" #include "cobalt/web/context.h" #include "cobalt/web/dom_exception.h" -#include "cobalt/web/environment_settings.h" #include "cobalt/web/environment_settings_helper.h" -namespace { -v8::Local toV8String(v8::Isolate* isolate, - const std::string& string) { - if (string.empty()) return v8::String::Empty(isolate); - return v8::String::NewFromUtf8(isolate, string.c_str(), - v8::NewStringType::kNormal, string.length()) - .ToLocalChecked(); -} -} // namespace - namespace cobalt { namespace js_profiler { -volatile uint32_t s_lastProfileId = 0; - -static constexpr int kBaseSampleIntervalMs = 10; - Profiler::Profiler(script::EnvironmentSettings* settings, ProfilerInitOptions options, script::ExceptionState* exception_state) - : cobalt::web::EventTarget(settings), - stopped_(false), - time_origin_{base::TimeTicks::Now()} { - profiler_id_ = nextProfileId(); + : stopped_(false), time_origin_{base::TimeTicks::Now()} { + profiler_group_ = ProfilerGroup::From(settings); + profiler_id_ = profiler_group_->NextProfilerId(); const base::TimeDelta sample_interval = base::Milliseconds(options.sample_interval()); @@ -66,21 +48,19 @@ Profiler::Profiler(script::EnvironmentSettings* settings, int effective_sample_interval_ms = static_cast(sample_interval.InMilliseconds()); - if (effective_sample_interval_ms % kBaseSampleIntervalMs != 0 || + if (effective_sample_interval_ms % Profiler::kBaseSampleIntervalMs != 0 || effective_sample_interval_ms == 0) { effective_sample_interval_ms += - (kBaseSampleIntervalMs - - effective_sample_interval_ms % kBaseSampleIntervalMs); + (Profiler::kBaseSampleIntervalMs - + effective_sample_interval_ms % Profiler::kBaseSampleIntervalMs); } sample_interval_ = effective_sample_interval_ms; - auto isolate = web::get_isolate(settings); - - auto status = ImplProfilingStart( - profiler_id_, + SB_LOG(INFO) << "[PROFILER] START " + profiler_id_; + auto status = profiler_group_->ProfilerStart( + this, settings, v8::CpuProfilingOptions(v8::kLeafNodeLineNumbers, - options.max_buffer_size(), sample_interval_us), - settings); + options.max_buffer_size(), sample_interval_us)); if (status == v8::CpuProfilingStatus::kAlreadyStarted) { web::DOMException::Raise(web::DOMException::kInvalidStateErr, @@ -91,46 +71,31 @@ Profiler::Profiler(script::EnvironmentSettings* settings, } } -Profiler::~Profiler() { - if (cpu_profiler_) { - cpu_profiler_->Dispose(); - cpu_profiler_ = nullptr; +void Profiler::AddEventListener( + script::EnvironmentSettings* environment_settings, const std::string& name, + const Profiler::SampleBufferFullCallbackHolder& holder) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (name != base::Tokens::samplebufferfull()) { + return; } + auto* global_wrappable = web::get_global_wrappable(environment_settings); + SampleBufferFullCallbackReference* token_callback = + new SampleBufferFullCallbackReference(global_wrappable, holder); + listeners_.push_back( + std::unique_ptr(token_callback)); } -v8::CpuProfilingStatus Profiler::ImplProfilingStart( - std::string profiler_id, v8::CpuProfilingOptions options, - script::EnvironmentSettings* settings) { - auto isolate = web::get_isolate(settings); - cpu_profiler_ = v8::CpuProfiler::New(isolate); - cpu_profiler_->SetSamplingInterval(kBaseSampleIntervalMs * - base::Time::kMicrosecondsPerMillisecond); - return cpu_profiler_->StartProfiling( - toV8String(isolate, profiler_id), options, - std::make_unique(this)); -} - -std::string Profiler::nextProfileId() { - s_lastProfileId++; - return "cobalt::profiler[" + std::to_string(s_lastProfileId) + "]"; -} - -void Profiler::PerformStop( - script::EnvironmentSettings* environment_settings, - std::unique_ptr promise_reference, - base::TimeTicks time_origin, std::string profiler_id) { - auto isolate = web::get_isolate(environment_settings); - auto profile = - cpu_profiler_->StopProfiling(toV8String(isolate, profiler_id_)); - auto trace = ProfilerTraceBuilder::FromProfile(profile, time_origin_); - scoped_refptr result(new ProfilerTraceWrapper(trace)); - cpu_profiler_->Dispose(); - cpu_profiler_ = nullptr; - promise_reference->value().Resolve(result); +void Profiler::DispatchSampleBufferFullEvent() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + for (auto it = listeners_.begin(); it != listeners_.end(); ++it) { + (*it)->value().Run(); + } + listeners_.clear(); } Profiler::ProfilerTracePromise Profiler::Stop( script::EnvironmentSettings* environment_settings) { + SB_LOG(INFO) << "[PROFILER] STOPPING " + profiler_id_; script::HandlePromiseWrappable promise = web::get_script_value_factory(environment_settings) ->CreateInterfacePromise>(); @@ -145,7 +110,7 @@ Profiler::ProfilerTracePromise Profiler::Stop( context->message_loop()->task_runner()->PostTask( FROM_HERE, base::BindOnce(&Profiler::PerformStop, base::Unretained(this), - environment_settings, std::move(promise_reference), + profiler_group_, std::move(promise_reference), std::move(time_origin_), std::move(profiler_id_))); } else { promise->Reject(new web::DOMException(web::DOMException::kInvalidStateErr, @@ -154,5 +119,23 @@ Profiler::ProfilerTracePromise Profiler::Stop( return promise; } +void Profiler::PerformStop( + ProfilerGroup* profiler_group, + std::unique_ptr promise_reference, + base::TimeTicks time_origin, std::string profiler_id) { + SB_LOG(INFO) << "[PROFILER] STOPPED " + profiler_id_; + auto trace = profiler_group->ProfilerStop(this); + scoped_refptr result(new ProfilerTraceWrapper(trace)); + promise_reference->value().Resolve(result); +} + +void Profiler::Cancel() { + if (!stopped_) { + stopped_ = true; + profiler_group_->ProfilerStop(this); + } + profiler_group_ = nullptr; +} + } // namespace js_profiler } // namespace cobalt diff --git a/cobalt/js_profiler/profiler.h b/cobalt/js_profiler/profiler.h index a6da1e77e63d..675f60b0a756 100644 --- a/cobalt/js_profiler/profiler.h +++ b/cobalt/js_profiler/profiler.h @@ -17,67 +17,78 @@ #include #include +#include +#include "base/threading/thread_checker.h" #include "cobalt/dom/performance_high_resolution_time.h" +#include "cobalt/js_profiler/profiler_group.h" #include "cobalt/js_profiler/profiler_init_options.h" #include "cobalt/js_profiler/profiler_trace.h" +#include "cobalt/script/callback_function.h" #include "cobalt/script/promise.h" +#include "cobalt/script/script_value.h" #include "cobalt/script/value_handle.h" #include "cobalt/script/wrappable.h" #include "cobalt/web/event_target.h" -#include "third_party/v8/include/cppgc/member.h" #include "third_party/v8/include/v8-profiler.h" namespace cobalt { namespace js_profiler { -class Profiler : public cobalt::web::EventTarget { +// Forward declaration of ProfilerGroup +class ProfilerGroup; + +// TODO(b/326337485): Profiler should be a subclass of EventTarget. +class Profiler : public script::Wrappable { public: - using ProfilerTracePromise = script::HandlePromiseWrappable; + static const int kBaseSampleIntervalMs = 10; + typedef script::HandlePromiseWrappable ProfilerTracePromise; + typedef script::CallbackFunction SampleBufferFullCallback; + typedef script::ScriptValue + SampleBufferFullCallbackHolder; + typedef SampleBufferFullCallbackHolder::Reference + SampleBufferFullCallbackReference; Profiler(script::EnvironmentSettings* settings, ProfilerInitOptions options, script::ExceptionState* exception_state); - ~Profiler(); + ~Profiler() override = default; + + void AddEventListener(script::EnvironmentSettings* environment_settings, + const std::string& name, + const SampleBufferFullCallbackHolder& listener); + + void DispatchSampleBufferFullEvent(); ProfilerTracePromise Stop(script::EnvironmentSettings* environment_settings); + void Cancel(); bool stopped() const { return stopped_; } dom::DOMHighResTimeStamp sample_interval() const { return sample_interval_; } + std::string ProfilerId() const { return profiler_id_; } + base::TimeTicks time_origin() const { return time_origin_; } DEFINE_WRAPPABLE_TYPE(Profiler); - virtual v8::CpuProfilingStatus ImplProfilingStart( - std::string profiler_id, v8::CpuProfilingOptions options, - script::EnvironmentSettings* settings); - private: - void PerformStop(script::EnvironmentSettings* environment_settings, + void PerformStop(ProfilerGroup* profiler_group, std::unique_ptr promise_reference, base::TimeTicks time_origin, std::string profiler_id); - std::string nextProfileId(); - bool stopped_; dom::DOMHighResTimeStamp sample_interval_; - v8::CpuProfiler* cpu_profiler_ = nullptr; base::TimeTicks time_origin_; std::string profiler_id_; -}; + ProfilerGroup* profiler_group_; + // All samplebufferfull listeners. Prevents GC on callbacks owned by this + // object, by binding to global-wrappable. + std::vector> listeners_; -class ProfilerMaxSamplesDelegate : public v8::DiscardedSamplesDelegate { - public: - explicit ProfilerMaxSamplesDelegate(Profiler* profiler) - : profiler_(profiler) {} - void Notify() override { - if (profiler_.Get()) { - profiler_->DispatchEvent(new web::Event("samplebufferfull")); - } - } + // Thread checker for the thread that creates this instance. + THREAD_CHECKER(thread_checker_); - private: - cppgc::WeakMember profiler_; + DISALLOW_COPY_AND_ASSIGN(Profiler); }; } // namespace js_profiler diff --git a/cobalt/js_profiler/profiler.idl b/cobalt/js_profiler/profiler.idl index 6f3793374219..0492b0039aff 100644 --- a/cobalt/js_profiler/profiler.idl +++ b/cobalt/js_profiler/profiler.idl @@ -20,9 +20,16 @@ ConstructorCallWith=EnvironmentSettings, RaisesException = Constructor, ] -interface Profiler : EventTarget { +interface Profiler { readonly attribute DOMHighResTimeStamp sampleInterval; readonly attribute boolean stopped; + // TODO(b/326337485): This function mocks but does not fully emulate the EventTarget interface. It can + // take and call many callbacks as listeners. However, note that this class does not remove listeners + // or dispatch events. Use with caution. + [CallWith=EnvironmentSettings] void addEventListener(DOMString token, SampleBufferFullCallback listener); + [CallWith=EnvironmentSettings] Promise stop(); }; + +callback SampleBufferFullCallback = void(); diff --git a/cobalt/js_profiler/profiler_group.cc b/cobalt/js_profiler/profiler_group.cc new file mode 100644 index 000000000000..e52512dc561c --- /dev/null +++ b/cobalt/js_profiler/profiler_group.cc @@ -0,0 +1,138 @@ +// Copyright 2024 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 "cobalt/js_profiler/profiler_group.h" + +#include "cobalt/js_profiler/profiler_trace_builder.h" +#include "cobalt/web/context.h" +#include "cobalt/web/environment_settings_helper.h" + +namespace { +v8::Local toV8String(v8::Isolate* isolate, + const std::string& string) { + if (string.empty()) return v8::String::Empty(isolate); + return v8::String::NewFromUtf8(isolate, string.c_str(), + v8::NewStringType::kNormal, string.length()) + .ToLocalChecked(); +} +} // namespace + +namespace cobalt { +namespace js_profiler { + +ProfilerGroup* ProfilerGroup::From( + script::EnvironmentSettings* environment_settings) { + web::Context* context = web::get_context(environment_settings); + if (!context->profiler_group()) { + script::GlobalEnvironment* global_env = + web::get_global_environment(environment_settings); + context->set_profiler_group( + std::make_unique(global_env->isolate())); + } + return context->profiler_group(); +} + +v8::CpuProfilingStatus ProfilerGroup::ProfilerStart( + const scoped_refptr& profiler, + script::EnvironmentSettings* settings, v8::CpuProfilingOptions options) { + if (!cpu_profiler_) { + cpu_profiler_ = v8::CpuProfiler::New(isolate_); + cpu_profiler_->SetSamplingInterval( + cobalt::js_profiler::Profiler::kBaseSampleIntervalMs * + base::Time::kMicrosecondsPerMillisecond); + } + profilers_.push_back(profiler); + num_active_profilers_++; + return cpu_profiler_->StartProfiling( + toV8String(isolate_, profiler->ProfilerId()), options, + std::make_unique(this, + profiler->ProfilerId())); +} + +ProfilerTrace ProfilerGroup::ProfilerStop(Profiler* profiler) { + auto profile = cpu_profiler_->StopProfiling( + toV8String(isolate_, profiler->ProfilerId())); + this->PopProfiler(profiler->ProfilerId()); + auto trace = + ProfilerTraceBuilder::FromProfile(profile, profiler->time_origin()); + if (profile) { + profile->Delete(); + } + if (cpu_profiler_ && num_active_profilers_ == 0) { + cpu_profiler_->Dispose(); + cpu_profiler_ = nullptr; + } + return trace; +} + +std::string ProfilerGroup::NextProfilerId() { + auto id = "cobalt::profiler[" + std::to_string(next_profiler_id_) + "]"; + next_profiler_id_++; + return id; +} + +void ProfilerGroup::DispatchSampleBufferFullEvent(std::string profiler_id) { + auto profiler = GetProfiler(profiler_id); + + if (profiler) { + profiler->DispatchSampleBufferFullEvent(); + } +} + +Profiler* ProfilerGroup::GetProfiler(std::string profiler_id) { + auto profiler = + std::find_if(profilers_.begin(), profilers_.end(), + [&profiler_id](const scoped_refptr& profiler) { + return profiler->ProfilerId() == profiler_id; + }); + if (profiler == profilers_.end()) { + return nullptr; + } + return profiler->get(); +} + +void ProfilerGroup::PopProfiler(std::string profiler_id) { + auto profiler = std::find_if(profilers_.begin(), profilers_.end(), + [&profiler_id](const Profiler* profiler) { + return profiler->ProfilerId() == profiler_id; + }); + if (profiler != profilers_.end()) { + profilers_.erase(profiler); + } + num_active_profilers_--; +} + +void ProfilerGroup::WillDestroyCurrentMessageLoop() { + while (!profilers_.empty()) { + Profiler* profiler = profilers_[0]; + DCHECK(profiler); + profiler->Cancel(); + DCHECK(profiler->stopped()); + } + + DCHECK_EQ(num_active_profilers_, 0); + if (cpu_profiler_) { + cpu_profiler_->Dispose(); + cpu_profiler_ = nullptr; + } +} + +void ProfilerMaxSamplesDelegate::Notify() { + if (profiler_group_.Get()) { + profiler_group_->DispatchSampleBufferFullEvent(profiler_id_); + } +} + +} // namespace js_profiler +} // namespace cobalt diff --git a/cobalt/js_profiler/profiler_group.h b/cobalt/js_profiler/profiler_group.h new file mode 100644 index 000000000000..a36a616c5d54 --- /dev/null +++ b/cobalt/js_profiler/profiler_group.h @@ -0,0 +1,103 @@ +// Copyright 2024 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_JS_PROFILER_PROFILER_GROUP_H_ +#define COBALT_JS_PROFILER_PROFILER_GROUP_H_ + +#include +#include +#include +#include + +#include "base/message_loop/message_loop.h" +#include "cobalt/js_profiler/profiler.h" +#include "cobalt/js_profiler/profiler_trace.h" +#include "cobalt/script/global_environment.h" +#include "third_party/v8/include/v8-profiler.h" +#include "v8/include/cppgc/member.h" +#include "v8/include/libplatform/libplatform.h" +#include "v8/include/v8-platform.h" +#include "v8/include/v8.h" + +namespace cobalt { +namespace js_profiler { + +// Forward Declaration of Profiler Class. +class Profiler; + +// A ProfilerGroup represents a set of window.Profiler JS objects sharing an +// underlying v8::CpuProfiler attached to a common isolate. +class ProfilerGroup : public base::MessageLoop::DestructionObserver { + public: + explicit ProfilerGroup(v8::Isolate* isolate) : isolate_(isolate) {} + + ~ProfilerGroup() = default; + + v8::CpuProfilingStatus ProfilerStart(const scoped_refptr& profiler, + script::EnvironmentSettings* settings, + v8::CpuProfilingOptions options); + + ProfilerTrace ProfilerStop(Profiler* profiler); + + void ProfilerCancel(Profiler* profiler); + + void DispatchSampleBufferFullEvent(std::string profiler_id); + + static ProfilerGroup* From(script::EnvironmentSettings* environment_settings); + + // Generates an unused string identifier to use for a new profiling session. + std::string NextProfilerId(); + + // From base::MessageLoop::DestructionObserver. + // All active profiling threads must be stopped before discarding this object. + void WillDestroyCurrentMessageLoop() override; + + int num_active_profilers() const { return num_active_profilers_; } + + bool active() const { return !!cpu_profiler_; } + + private: + Profiler* GetProfiler(std::string profiler_id); + void PopProfiler(std::string profiler_id); + + v8::Isolate* isolate_; + v8::CpuProfiler* cpu_profiler_ = nullptr; + + int num_active_profilers_ = 0; + int next_profiler_id_ = 0; + + // All active profilers, retained from GC. + std::vector> profilers_; +}; + +// ProfilerMaxSamplesDelegate has a notify function that is called when the +// number of collected samples exceeds the allocated buffer. It is primarily +// responsible for the "samplebufferfull" event listener on the Profiler class. +class ProfilerMaxSamplesDelegate : public v8::DiscardedSamplesDelegate { + public: + explicit ProfilerMaxSamplesDelegate(ProfilerGroup* profiler_group, + std::string profiler_id) + : profiler_group_(profiler_group), profiler_id_(profiler_id) {} + + void Notify() override; + + private: + cppgc::WeakMember profiler_group_; + std::string profiler_id_; +}; + +} // namespace js_profiler +} // namespace cobalt + +#endif // COBALT_JS_PROFILER_PROFILER_GROUP_H_ diff --git a/cobalt/media/BUILD.gn b/cobalt/media/BUILD.gn index 99795513a638..0b6a1028312c 100644 --- a/cobalt/media/BUILD.gn +++ b/cobalt/media/BUILD.gn @@ -120,6 +120,7 @@ target(gtest_target_type, "media_test") { sources = [ "base/cval_stats_test.cc", "base/decoder_buffer_cache_test.cc", + "base/metrics_provider_test.cc", "file_data_source_test.cc", "progressive/demuxer_extension_wrapper_test.cc", "progressive/mock_data_source_reader.h", diff --git a/cobalt/media/base/metrics_provider.cc b/cobalt/media/base/metrics_provider.cc index 510d5a867feb..beccc2454401 100644 --- a/cobalt/media/base/metrics_provider.cc +++ b/cobalt/media/base/metrics_provider.cc @@ -16,6 +16,8 @@ #include "base/logging.h" #include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" +#include "base/time/time.h" namespace cobalt { namespace media { @@ -122,5 +124,44 @@ bool MediaMetricsProvider::IsInitialized() const { return media_info_.has_value(); } +void MediaMetricsProvider::StartTrackingAction(WebMediaPlayerAction action) { + DCHECK(IsInitialized()); + DCHECK(!IsActionCurrentlyTracked(action)); + ScopedLock scoped_lock(mutex_); + + tracked_actions_start_times_[action] = clock_->NowTicks(); +} + +void MediaMetricsProvider::EndTrackingAction(WebMediaPlayerAction action) { + DCHECK(IsInitialized()); + DCHECK(IsActionCurrentlyTracked(action)); + ScopedLock scoped_lock(mutex_); + + auto duration = clock_->NowTicks() - tracked_actions_start_times_[action]; + ReportActionLatencyUMA(action, duration); + tracked_actions_start_times_.erase(action); +} + +bool MediaMetricsProvider::IsActionCurrentlyTracked( + WebMediaPlayerAction action) { + DCHECK(IsInitialized()); + ScopedLock scoped_lock(mutex_); + return tracked_actions_start_times_.find(action) != + tracked_actions_start_times_.end(); +} + +void MediaMetricsProvider::ReportActionLatencyUMA( + WebMediaPlayerAction action, const base::TimeDelta& action_duration) { + switch (action) { + case WebMediaPlayerAction::SEEK: + UMA_HISTOGRAM_TIMES("Cobalt.Media.WebMediaPlayer.Seek.Timing", + action_duration); + break; + case WebMediaPlayerAction::UNKNOWN_ACTION: + default: + break; + } +} + } // namespace media } // namespace cobalt diff --git a/cobalt/media/base/metrics_provider.h b/cobalt/media/base/metrics_provider.h index dff1df7f3ae4..07675d4d77b5 100644 --- a/cobalt/media/base/metrics_provider.h +++ b/cobalt/media/base/metrics_provider.h @@ -15,9 +15,15 @@ #ifndef COBALT_MEDIA_BASE_METRICS_PROVIDER_H_ #define COBALT_MEDIA_BASE_METRICS_PROVIDER_H_ +#include #include +#include "base/containers/small_map.h" +#include "base/time/default_tick_clock.h" +#include "base/time/tick_clock.h" +#include "base/time/time.h" #include "starboard/common/mutex.h" +#include "starboard/types.h" #include "third_party/chromium/media/base/audio_codecs.h" #include "third_party/chromium/media/base/container_names.h" #include "third_party/chromium/media/base/pipeline_status.h" @@ -32,9 +38,16 @@ using VideoCodec = ::media::VideoCodec; using PipelineStatus = ::media::PipelineStatus; using VideoDecoderType = ::media::VideoDecoderType; +enum class WebMediaPlayerAction : uint8_t { + UNKNOWN_ACTION, + SEEK, +}; + class MediaMetricsProvider { public: - MediaMetricsProvider() = default; + MediaMetricsProvider( + const base::TickClock* clock = base::DefaultTickClock::GetInstance()) + : clock_(clock) {} ~MediaMetricsProvider(); private: @@ -71,11 +84,24 @@ class MediaMetricsProvider { void ReportPipelineUMA(); + // Used to record the latency of an action in the WebMediaPlayer. + void StartTrackingAction(WebMediaPlayerAction action); + void EndTrackingAction(WebMediaPlayerAction action); + bool IsActionCurrentlyTracked(WebMediaPlayerAction action); + private: std::string GetUMANameForAVStream(const PipelineInfo& player_info) const; bool IsInitialized() const; + void ReportActionLatencyUMA(WebMediaPlayerAction action, + const base::TimeDelta& action_duration); + private: + // Media player action latency data. + const base::TickClock* clock_; + base::small_map, 2> + tracked_actions_start_times_; + // UMA pipeline packaged data PipelineInfo uma_info_; diff --git a/cobalt/media/base/metrics_provider_test.cc b/cobalt/media/base/metrics_provider_test.cc new file mode 100644 index 000000000000..78a1fe4bfa1c --- /dev/null +++ b/cobalt/media/base/metrics_provider_test.cc @@ -0,0 +1,70 @@ +// Copyright 2024 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 "cobalt/media/base/metrics_provider.h" + +#include +#include + +#include "base/test/metrics/histogram_tester.h" +#include "base/test/simple_test_tick_clock.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cobalt { +namespace media { +namespace { +constexpr char kUmaPrefix[] = "Cobalt.Media."; + +class MediaMetricsProviderTest : public ::testing::Test { + protected: + MediaMetricsProviderTest() : metrics_(&clock_) {} + + void SetUp() override { clock_.SetNowTicks(base::TimeTicks()); } + + base::HistogramTester histogram_tester_; + base::SimpleTestTickClock clock_; + + MediaMetricsProvider metrics_; +}; + +TEST_F(MediaMetricsProviderTest, ReportsSeekLatency) { + metrics_.Initialize(true); + metrics_.StartTrackingAction(WebMediaPlayerAction::SEEK); + + clock_.Advance(base::TimeDelta::FromMilliseconds(100)); + metrics_.EndTrackingAction(WebMediaPlayerAction::SEEK); + + histogram_tester_.ExpectUniqueSample( + std::string(kUmaPrefix) + "WebMediaPlayer.Seek.Timing", 100, 1); +} + +TEST_F(MediaMetricsProviderTest, SupportsTrackingMultipleActions) { + metrics_.Initialize(true); + metrics_.StartTrackingAction(WebMediaPlayerAction::SEEK); + metrics_.StartTrackingAction(WebMediaPlayerAction::UNKNOWN_ACTION); + + clock_.Advance(base::TimeDelta::FromMilliseconds(100)); + metrics_.EndTrackingAction(WebMediaPlayerAction::UNKNOWN_ACTION); + + clock_.Advance(base::TimeDelta::FromMilliseconds(1000)); + metrics_.EndTrackingAction(WebMediaPlayerAction::SEEK); + + histogram_tester_.ExpectUniqueSample( + std::string(kUmaPrefix) + "WebMediaPlayer.Seek.Timing", 1100, 1); +} + +} // namespace +} // namespace media +} // namespace cobalt diff --git a/cobalt/media/player/web_media_player_impl.cc b/cobalt/media/player/web_media_player_impl.cc index e773474d419b..6f34375e5f3c 100644 --- a/cobalt/media/player/web_media_player_impl.cc +++ b/cobalt/media/player/web_media_player_impl.cc @@ -22,6 +22,7 @@ #include "base/trace_event/trace_event.h" #include "cobalt/base/instance_counter.h" #include "cobalt/media/base/drm_system.h" +#include "cobalt/media/base/metrics_provider.h" #include "cobalt/media/base/sbplayer_pipeline.h" #include "cobalt/media/player/web_media_player_proxy.h" #include "cobalt/media/progressive/data_source_reader.h" @@ -362,6 +363,7 @@ void WebMediaPlayerImpl::Seek(float seconds) { return; } + media_metrics_provider_.StartTrackingAction(WebMediaPlayerAction::SEEK); media_log_->AddEvent<::media::MediaLogEvent::kSeek>(seconds); base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds); @@ -637,6 +639,14 @@ void WebMediaPlayerImpl::OnPipelineSeek(::media::PipelineStatus status, const bool kEosPlayed = false; GetClient()->TimeChanged(kEosPlayed); } + + // WebMediaPlayerImpl::OnPipelineSeek() is called frequently, more than just + // when WebMediaPlayerImpl::Seek() is called. This helps to filter out when + // the pipeline seeks without being initiated by WebMediaPlayerImpl. + if (media_metrics_provider_.IsActionCurrentlyTracked( + WebMediaPlayerAction::SEEK)) { + media_metrics_provider_.EndTrackingAction(WebMediaPlayerAction::SEEK); + } } void WebMediaPlayerImpl::OnPipelineEnded(::media::PipelineStatus status) { diff --git a/cobalt/media_session/media_session_client.cc b/cobalt/media_session/media_session_client.cc index ab740dead22d..d1888a253f19 100644 --- a/cobalt/media_session/media_session_client.cc +++ b/cobalt/media_session/media_session_client.cc @@ -212,6 +212,28 @@ void MediaSessionClient::UpdatePlatformPlaybackState( PostDelayedTaskForMaybeFreezeCallback(); } +void MediaSessionClient::InvokeAction( + const CobaltExtensionMediaSessionAction& action) { + std::unique_ptr details( + new CobaltExtensionMediaSessionActionDetails()); + CobaltExtensionMediaSessionActionDetailsInit(details.get(), action); + DCHECK(media_session_->task_runner_); + media_session_->task_runner_->PostTask( + FROM_HERE, base::BindOnce(&MediaSessionClient::InvokeActionInternal, + AsWeakPtr(), base::Passed(&details))); +} + +void MediaSessionClient::InvokeAction( + const CobaltExtensionMediaSessionActionDetails& details) { + std::unique_ptr details_ptr( + new CobaltExtensionMediaSessionActionDetails(details)); + DCHECK(media_session_->task_runner_); + media_session_->task_runner_->PostTask( + FROM_HERE, base::Bind(&MediaSessionClient::InvokeActionInternal, + AsWeakPtr(), base::Passed(&details_ptr))); +} + + void MediaSessionClient::RunMaybeFreezeCallback(int sequence_number) { if (sequence_number != sequence_number_) return; @@ -233,14 +255,8 @@ void MediaSessionClient::InvokeActionInternal( details->action == kCobaltExtensionMediaSessionActionSeekto); DCHECK(!details->fast_seek || details->action == kCobaltExtensionMediaSessionActionSeekto); - - DCHECK(media_session_->task_runner_); - if (!media_session_->task_runner_->BelongsToCurrentThread()) { - media_session_->task_runner_->PostTask( - FROM_HERE, base::Bind(&MediaSessionClient::InvokeActionInternal, - AsWeakPtr(), base::Passed(&details))); - return; - } + CHECK(media_session_->task_runner_); + CHECK(media_session_->task_runner_->BelongsToCurrentThread()); MediaSession::ActionMap::iterator it = media_session_->action_map_.find( ConvertMediaSessionAction(details->action)); diff --git a/cobalt/media_session/media_session_client.h b/cobalt/media_session/media_session_client.h index 54817e40044f..3223f32221e2 100644 --- a/cobalt/media_session/media_session_client.h +++ b/cobalt/media_session/media_session_client.h @@ -62,19 +62,10 @@ class MediaSessionClient : public base::SupportsWeakPtr { // Invokes a given media session action // https://wicg.github.io/mediasession/#actions-model // Can be invoked from any thread. - void InvokeAction(CobaltExtensionMediaSessionAction action) { - std::unique_ptr details( - new CobaltExtensionMediaSessionActionDetails()); - CobaltExtensionMediaSessionActionDetailsInit(details.get(), action); - InvokeActionInternal(std::move(details)); - } + void InvokeAction(const CobaltExtensionMediaSessionAction& action); // Invokes a given media session action that takes additional details. - void InvokeAction(CobaltExtensionMediaSessionActionDetails details) { - std::unique_ptr details_ptr( - new CobaltExtensionMediaSessionActionDetails(details)); - InvokeActionInternal(std::move(details_ptr)); - } + void InvokeAction(const CobaltExtensionMediaSessionActionDetails& details); // Invoked on the browser thread when any metadata, position state, playback // state, or supported session actions change. diff --git a/cobalt/media_session/media_session_test.cc b/cobalt/media_session/media_session_test.cc index 43f34ed64c02..a50e6a72513e 100644 --- a/cobalt/media_session/media_session_test.cc +++ b/cobalt/media_session/media_session_test.cc @@ -221,6 +221,7 @@ TEST(MediaSessionTest, NullActionClears) { .to_ulong()); session->mock_session_client()->InvokeAction( kCobaltExtensionMediaSessionActionPlay); + base::RunLoop().RunUntilIdle(); session->SetActionHandler(kMediaSessionActionPlay, null_holder); session->mock_session_client()->WaitForSessionStateChange(); @@ -230,6 +231,7 @@ TEST(MediaSessionTest, NullActionClears) { .to_ulong()); session->mock_session_client()->InvokeAction( kCobaltExtensionMediaSessionActionPlay); + base::RunLoop().RunUntilIdle(); EXPECT_GE(session->mock_session_client()->GetMediaSessionChangeCount(), 3); } @@ -359,6 +361,7 @@ TEST(MediaSessionTest, InvokeAction) { &details, kCobaltExtensionMediaSessionActionSeekto); details.seek_time = 1.2; session->mock_session_client()->InvokeAction(details); + base::RunLoop().RunUntilIdle(); } TEST(MediaSessionTest, SeekDetails) { @@ -380,17 +383,20 @@ TEST(MediaSessionTest, SeekDetails) { .WillOnce(Return(CallbackResult())); session->mock_session_client()->InvokeAction( kCobaltExtensionMediaSessionActionSeekforward); + base::RunLoop().RunUntilIdle(); EXPECT_CALL(cf, Run(SeekNoOffset(kMediaSessionActionSeekbackward))) .WillOnce(Return(CallbackResult())); session->mock_session_client()->InvokeAction( kCobaltExtensionMediaSessionActionSeekbackward); + base::RunLoop().RunUntilIdle(); EXPECT_CALL(cf, Run(SeekTime(1.2))).WillOnce(Return(CallbackResult())); CobaltExtensionMediaSessionActionDetailsInit( &details, kCobaltExtensionMediaSessionActionSeekto); details.seek_time = 1.2; session->mock_session_client()->InvokeAction(details); + base::RunLoop().RunUntilIdle(); EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekforward, 3.4))) .WillOnce(Return(CallbackResult())); @@ -398,6 +404,7 @@ TEST(MediaSessionTest, SeekDetails) { &details, kCobaltExtensionMediaSessionActionSeekforward); details.seek_offset = 3.4; session->mock_session_client()->InvokeAction(details); + base::RunLoop().RunUntilIdle(); EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekbackward, 5.6))) .WillOnce(Return(CallbackResult())); @@ -405,6 +412,7 @@ TEST(MediaSessionTest, SeekDetails) { &details, kCobaltExtensionMediaSessionActionSeekbackward); details.seek_offset = 5.6; session->mock_session_client()->InvokeAction(details); + base::RunLoop().RunUntilIdle(); session->mock_session_client()->WaitForSessionStateChange(); EXPECT_GE(session->mock_session_client()->GetMediaSessionChangeCount(), 0); diff --git a/cobalt/network/network_module.cc b/cobalt/network/network_module.cc index 09263af68ae4..888bbe9dc119 100644 --- a/cobalt/network/network_module.cc +++ b/cobalt/network/network_module.cc @@ -249,7 +249,12 @@ void NetworkModule::RestartDialService() { } void NetworkModule::OnRestartDialService(base::WaitableEvent* creation_event) { - dial_service_.reset(new DialService()); + // A new DialService instance cannot be created if any already exists + // since they will use the same address and it will cause some socket errors. + // Destroy existing service first. + dial_service_.reset(); + // Create new dial service + dial_service_ = std::make_unique(); dial_service_proxy_->ReplaceDialService(dial_service_->AsWeakPtr()); creation_event->Signal(); } diff --git a/cobalt/version.h b/cobalt/version.h index 9509d06eedb0..19c6ec64a383 100644 --- a/cobalt/version.h +++ b/cobalt/version.h @@ -35,6 +35,6 @@ // release is cut. //. -#define COBALT_VERSION "24.lts.31" +#define COBALT_VERSION "24.lts.32" #endif // COBALT_VERSION_H_ diff --git a/cobalt/web/agent.cc b/cobalt/web/agent.cc index e9f51e4fa55b..1c0c55102c79 100644 --- a/cobalt/web/agent.cc +++ b/cobalt/web/agent.cc @@ -23,6 +23,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "cobalt/base/startup_timer.h" +#include "cobalt/js_profiler/profiler_group.h" #include "cobalt/loader/fetcher_factory.h" #include "cobalt/loader/script_loader_factory.h" #include "cobalt/script/environment_settings.h" @@ -97,6 +98,9 @@ class Impl : public Context { script::ScriptRunner* script_runner() const final { return script_runner_.get(); } + js_profiler::ProfilerGroup* profiler_group() const final { + return profiler_group_.get(); + } Blob::Registry* blob_registry() const final { return blob_registry_.get(); } web::WebSettings* web_settings() const final { return web_settings_; } network::NetworkModule* network_module() const final { @@ -173,6 +177,10 @@ class Impl : public Context { return active_service_worker_; } + void set_profiler_group( + std::unique_ptr profiler_group) final { + profiler_group_ = std::move(profiler_group); + } private: // Injects a list of attributes into the Web Context's global object. @@ -218,6 +226,9 @@ class Impl : public Context { // Environment Settings object std::unique_ptr environment_settings_; + // A ProfilerGroup contains all window.Profiler objects on the isolate. + std::unique_ptr profiler_group_ = nullptr; + // The service worker registration object map. // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#environment-settings-object-service-worker-registration-object-map std::map global_environment( diff --git a/cobalt/web/context.h b/cobalt/web/context.h index b8fc450c494a..cb9a1ad62ef2 100644 --- a/cobalt/web/context.h +++ b/cobalt/web/context.h @@ -15,6 +15,7 @@ #ifndef COBALT_WEB_CONTEXT_H_ #define COBALT_WEB_CONTEXT_H_ +#include #include #include "cobalt/loader/fetcher_factory.h" @@ -39,6 +40,9 @@ class ServiceWorker; class ServiceWorkerContext; class ServiceWorkerObject; } // namespace worker +namespace js_profiler { +class ProfilerGroup; +} // namespace js_profiler namespace web { class WindowOrWorkerGlobalScope; @@ -66,6 +70,7 @@ class Context { virtual web::WebSettings* web_settings() const = 0; virtual network::NetworkModule* network_module() const = 0; virtual worker::ServiceWorkerContext* service_worker_context() const = 0; + virtual js_profiler::ProfilerGroup* profiler_group() const = 0; virtual const std::string& name() const = 0; virtual void SetupEnvironmentSettings(EnvironmentSettings* settings) = 0; @@ -110,6 +115,10 @@ class Context { active_service_worker() = 0; virtual const scoped_refptr& active_service_worker() const = 0; + + // https://wicg.github.io/js-self-profiling/ + virtual void set_profiler_group( + std::unique_ptr profiler_group) = 0; }; } // namespace web diff --git a/cobalt/web/testing/BUILD.gn b/cobalt/web/testing/BUILD.gn index 8c67e014ed08..601c3a77c43b 100644 --- a/cobalt/web/testing/BUILD.gn +++ b/cobalt/web/testing/BUILD.gn @@ -29,6 +29,7 @@ source_set("web_testing") { "//base/test:test_support", "//cobalt/base", "//cobalt/browser", + "//cobalt/js_profiler", "//cobalt/loader", "//cobalt/network", "//cobalt/script", diff --git a/cobalt/web/testing/stub_web_context.h b/cobalt/web/testing/stub_web_context.h index 487101b5a761..7c341e95679c 100644 --- a/cobalt/web/testing/stub_web_context.h +++ b/cobalt/web/testing/stub_web_context.h @@ -17,9 +17,11 @@ #include #include +#include #include "base/message_loop/message_loop.h" #include "base/test/scoped_task_environment.h" +#include "cobalt/js_profiler/profiler_group.h" #include "cobalt/loader/fetcher_factory.h" #include "cobalt/network/network_module.h" #include "cobalt/script/global_environment.h" @@ -53,6 +55,8 @@ class StubWebContext final : public Context { global_environment_ = javascript_engine_->CreateGlobalEnvironment(); blob_registry_.reset(new Blob::Registry); web_settings_.reset(new WebSettingsImpl()); + profiler_group_.reset( + new js_profiler::ProfilerGroup(global_environment_->isolate())); network_module_.reset(new network::NetworkModule()); fetcher_factory_.reset(new loader::FetcherFactory( network_module_.get(), @@ -109,6 +113,10 @@ class StubWebContext final : public Context { DCHECK(network_module_); return network_module_.get(); } + js_profiler::ProfilerGroup* profiler_group() const final { + DCHECK(profiler_group_); + return profiler_group_.get(); + } worker::ServiceWorkerContext* service_worker_context() const final { NOTREACHED(); @@ -202,6 +210,11 @@ class StubWebContext final : public Context { return service_worker_object_; } + void set_profiler_group( + std::unique_ptr profiler_group) { + profiler_group_ = std::move(profiler_group); + } + // Other private: // Name of the web instance. @@ -217,6 +230,7 @@ class StubWebContext final : public Context { std::unique_ptr web_settings_; std::unique_ptr network_module_; + std::unique_ptr profiler_group_; // Environment Settings object std::unique_ptr environment_settings_; UserAgentPlatformInfo* platform_info_ = nullptr; diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java index 5d6e8f9a4432..5710475dde9b 100644 --- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java @@ -649,6 +649,12 @@ void updateMediaSession( playbackState, actions, positionMs, speed, title, artist, album, artwork, duration); } + @SuppressWarnings("unused") + @UsedByNative + public void deactivateMediaSession() { + cobaltMediaSession.deactivateMediaSession(); + } + /** Returns string for kSbSystemPropertyUserAgentAuxField */ @SuppressWarnings("unused") @UsedByNative diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java index 0b28e088079f..2dc78d99f665 100644 --- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java @@ -261,7 +261,16 @@ private void configureMediaFocus(int playbackState, long positionMs, float speed } if (deactivating) { // Suspending lands here. - Log.i(TAG, "MediaSession release"); + deactivateMediaSession(); + } + } + + public void deactivateMediaSession() { + Log.i(TAG, "MediaSession release"); + if (mediaSession != null) { + if (mediaSession.isActive()) { + mediaSession.setActive(false); + } mediaSession.release(); mediaSession = null; } diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java index 2ae4b2306e15..64b1c545985b 100644 --- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java @@ -818,6 +818,9 @@ public static void createVideoMediaCodecBridge( @UsedByNative public void release() { try { + mMediaCodec.setCallback(null); + mCallback = null; + String codecName = mMediaCodec.getName(); Log.w(TAG, "calling MediaCodec.release() on " + codecName); mMediaCodec.release(); diff --git a/starboard/android/shared/BUILD.gn b/starboard/android/shared/BUILD.gn index 71aad0fbadb2..a34fdd56d6ed 100644 --- a/starboard/android/shared/BUILD.gn +++ b/starboard/android/shared/BUILD.gn @@ -166,6 +166,9 @@ static_library("starboard_platform") { "//starboard/shared/pthread/mutex_destroy.cc", "//starboard/shared/pthread/mutex_release.cc", "//starboard/shared/pthread/once.cc", + "//starboard/shared/pthread/thread_context_get_pointer.cc", + "//starboard/shared/pthread/thread_context_internal.cc", + "//starboard/shared/pthread/thread_context_internal.h", "//starboard/shared/pthread/thread_create_local_key.cc", "//starboard/shared/pthread/thread_create_priority.h", "//starboard/shared/pthread/thread_destroy_local_key.cc", @@ -174,6 +177,13 @@ static_library("starboard_platform") { "//starboard/shared/pthread/thread_get_local_value.cc", "//starboard/shared/pthread/thread_is_equal.cc", "//starboard/shared/pthread/thread_join.cc", + "//starboard/shared/pthread/thread_sampler_create.cc", + "//starboard/shared/pthread/thread_sampler_destroy.cc", + "//starboard/shared/pthread/thread_sampler_freeze.cc", + "//starboard/shared/pthread/thread_sampler_internal.cc", + "//starboard/shared/pthread/thread_sampler_internal.h", + "//starboard/shared/pthread/thread_sampler_is_supported.cc", + "//starboard/shared/pthread/thread_sampler_thaw.cc", "//starboard/shared/pthread/thread_set_local_value.cc", "//starboard/shared/pthread/thread_yield.cc", "//starboard/shared/signal/crash_signals.h", @@ -250,12 +260,6 @@ static_library("starboard_platform") { "//starboard/shared/stub/system_request_focus.cc", "//starboard/shared/stub/system_request_reveal.cc", "//starboard/shared/stub/system_sign_with_certification_secret_key.cc", - "//starboard/shared/stub/thread_context_get_pointer.cc", - "//starboard/shared/stub/thread_sampler_create.cc", - "//starboard/shared/stub/thread_sampler_destroy.cc", - "//starboard/shared/stub/thread_sampler_freeze.cc", - "//starboard/shared/stub/thread_sampler_is_supported.cc", - "//starboard/shared/stub/thread_sampler_thaw.cc", "//starboard/shared/stub/ui_nav_get_interface.cc", "accessibility_get_caption_settings.cc", "accessibility_get_display_settings.cc", diff --git a/starboard/android/shared/android_media_session_client.cc b/starboard/android/shared/android_media_session_client.cc index 22ade7d6e521..e031ad1178f0 100644 --- a/starboard/android/shared/android_media_session_client.cc +++ b/starboard/android/shared/android_media_session_client.cc @@ -172,11 +172,6 @@ void OnMediaSessionStateChanged( jint playback_state = CobaltExtensionPlaybackStateToPlaybackState( session_state.actual_playback_state); - SbOnce(&once_flag, OnceInit); - SbMutexAcquire(&mutex); - - SbMutexRelease(&mutex); - jlong playback_state_actions = MediaSessionActionsToPlaybackStateActions( session_state.available_actions); @@ -271,6 +266,9 @@ void DestroyMediaSessionClientCallback() { g_update_platform_playback_state_callback = NULL; SbMutexRelease(&mutex); + + JniEnvExt* env = JniEnvExt::Get(); + env->CallStarboardVoidMethodOrAbort("deactivateMediaSession", "()V"); } } // namespace diff --git a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc index 1086f3f4f435..6d25689180aa 100644 --- a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc +++ b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc @@ -249,6 +249,11 @@ AudioDecoderImpl::Read(int* samples_per_second) { void AudioDecoderImpl::Reset() { SB_DCHECK(BelongsToCurrentThread()); + TeardownCodec(); + if ((ffmpeg_->specialization_version()) == FFMPEG) { + InitializeCodec(); + } + stream_ended_ = false; while (!decoded_audios_.empty()) { decoded_audios_.pop(); diff --git a/starboard/shared/opus/opus_audio_decoder.cc b/starboard/shared/opus/opus_audio_decoder.cc index 483396471331..85ba47e6eb1f 100644 --- a/starboard/shared/opus/opus_audio_decoder.cc +++ b/starboard/shared/opus/opus_audio_decoder.cc @@ -47,31 +47,11 @@ static const VorbisLayout vorbis_mappings[8] = { OpusAudioDecoder::OpusAudioDecoder(const AudioStreamInfo& audio_stream_info) : audio_stream_info_(audio_stream_info) { - int error; - int channels = audio_stream_info_.number_of_channels; - if (channels > 8 || channels < 1) { - SB_LOG(ERROR) << "Can't create decoder with " << channels << " channels"; - return; - } - - decoder_ = opus_multistream_decoder_create( - audio_stream_info_.samples_per_second, channels, - vorbis_mappings[channels - 1].nb_streams, - vorbis_mappings[channels - 1].nb_coupled_streams, - vorbis_mappings[channels - 1].mapping, &error); - if (error != OPUS_OK) { - SB_LOG(ERROR) << "Failed to create decoder with error: " - << opus_strerror(error); - decoder_ = NULL; - return; - } - SB_DCHECK(decoder_ != NULL); + InitializeCodec(); } OpusAudioDecoder::~OpusAudioDecoder() { - if (decoder_) { - opus_multistream_decoder_destroy(decoder_); - } + TeardownCodec(); } void OpusAudioDecoder::Initialize(const OutputCB& output_cb, @@ -215,6 +195,34 @@ void OpusAudioDecoder::WriteEndOfStream() { Schedule(output_cb_); } +void OpusAudioDecoder::InitializeCodec() { + int error; + int channels = audio_stream_info_.number_of_channels; + if (channels > 8 || channels < 1) { + SB_LOG(ERROR) << "Can't create decoder with " << channels << " channels"; + return; + } + + decoder_ = opus_multistream_decoder_create( + audio_stream_info_.samples_per_second, channels, + vorbis_mappings[channels - 1].nb_streams, + vorbis_mappings[channels - 1].nb_coupled_streams, + vorbis_mappings[channels - 1].mapping, &error); + if (error != OPUS_OK) { + SB_LOG(ERROR) << "Failed to create decoder with error: " + << opus_strerror(error); + decoder_ = NULL; + return; + } + SB_DCHECK(decoder_ != NULL); +} + +void OpusAudioDecoder::TeardownCodec() { + if (decoder_) { + opus_multistream_decoder_destroy(decoder_); + } +} + scoped_refptr OpusAudioDecoder::Read( int* samples_per_second) { SB_DCHECK(BelongsToCurrentThread()); @@ -233,6 +241,9 @@ scoped_refptr OpusAudioDecoder::Read( void OpusAudioDecoder::Reset() { SB_DCHECK(BelongsToCurrentThread()); + TeardownCodec(); + InitializeCodec(); + stream_ended_ = false; while (!decoded_audios_.empty()) { decoded_audios_.pop(); diff --git a/starboard/shared/opus/opus_audio_decoder.h b/starboard/shared/opus/opus_audio_decoder.h index ab3e94751829..a8d59dc8c972 100644 --- a/starboard/shared/opus/opus_audio_decoder.h +++ b/starboard/shared/opus/opus_audio_decoder.h @@ -54,6 +54,8 @@ class OpusAudioDecoder private: static constexpr int kMinimumBuffersToDecode = 2; + void InitializeCodec(); + void TeardownCodec(); void DecodePendingBuffers(); bool DecodeInternal(const scoped_refptr& input_buffer); static const int kMaxOpusFramesPerAU = 9600; diff --git a/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc b/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc index 7e748036d5dc..0c341ea3e912 100644 --- a/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc +++ b/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc @@ -146,7 +146,9 @@ void AdaptiveAudioDecoder::Reset() { SB_DCHECK(BelongsToCurrentThread()); if (audio_decoder_) { - TeardownAudioDecoder(); + audio_decoder_->Reset(); + resampler_.reset(); + channel_mixer_.reset(); } CancelPendingJobs(); while (!decoded_audios_.empty()) { diff --git a/third_party/chromium/media/base/demuxer_memory_limit_starboard.cc b/third_party/chromium/media/base/demuxer_memory_limit_starboard.cc index d8305f034f95..188878d8715c 100644 --- a/third_party/chromium/media/base/demuxer_memory_limit_starboard.cc +++ b/third_party/chromium/media/base/demuxer_memory_limit_starboard.cc @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "media/base/demuxer_memory_limit.h" #include "media/base/decoder_buffer.h" diff --git a/third_party/chromium/media/base/demuxer_stream.h b/third_party/chromium/media/base/demuxer_stream.h index 67769b39fd26..f1a975456713 100644 --- a/third_party/chromium/media/base/demuxer_stream.h +++ b/third_party/chromium/media/base/demuxer_stream.h @@ -70,7 +70,7 @@ class MEDIA_EXPORT DemuxerStream { #if defined(STARBOARD) virtual std::string mime_type() const { return ""; } -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) // Request a buffer to returned via the provided callback. // @@ -80,10 +80,10 @@ class MEDIA_EXPORT DemuxerStream { #if defined(STARBOARD) typedef base::OnceCallback>&)> ReadCB; virtual void Read(int max_number_of_buffers_to_read, ReadCB read_cb) = 0; -#else // defined (STARBOARD) +#else // defined(STARBOARD) typedef base::OnceCallback)> ReadCB; virtual void Read(ReadCB read_cb) = 0; -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) // Returns the audio/video decoder configuration. It is an error to call the // audio method on a video stream and vice versa. After |kConfigChanged| is diff --git a/third_party/chromium/media/base/starboard_utils.cc b/third_party/chromium/media/base/starboard_utils.cc index 1518fb9abdf9..786b4b29a54f 100644 --- a/third_party/chromium/media/base/starboard_utils.cc +++ b/third_party/chromium/media/base/starboard_utils.cc @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "media/base/starboard_utils.h" #include diff --git a/third_party/chromium/media/base/starboard_utils.h b/third_party/chromium/media/base/starboard_utils.h index 50920bd698e5..6f908e5e588e 100644 --- a/third_party/chromium/media/base/starboard_utils.h +++ b/third_party/chromium/media/base/starboard_utils.h @@ -15,6 +15,10 @@ #ifndef MEDIA_BASE_STARBOARD_UTILS_H_ #define MEDIA_BASE_STARBOARD_UTILS_H_ +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "starboard/drm.h" #include "starboard/media.h" #include "media/base/audio_codecs.h" diff --git a/third_party/chromium/media/base/starboard_utils_test.cc b/third_party/chromium/media/base/starboard_utils_test.cc index ab5460c1015b..a2feb6d2e101 100644 --- a/third_party/chromium/media/base/starboard_utils_test.cc +++ b/third_party/chromium/media/base/starboard_utils_test.cc @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "media/base/starboard_utils.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/third_party/chromium/media/base/status.h b/third_party/chromium/media/base/status.h index 6101e3f938e7..5ddbda4ac604 100644 --- a/third_party/chromium/media/base/status.h +++ b/third_party/chromium/media/base/status.h @@ -17,7 +17,9 @@ #include "media/base/media_export.h" #include "media/base/media_serializers_base.h" #include "media/base/status_codes.h" +#if defined(STARBOARD) #include "third_party/abseil-cpp/absl/types/optional.h" +#endif // defined(STARBOARD) // Mojo namespaces for serialization friend declarations. namespace mojo { diff --git a/third_party/chromium/media/cobalt/base/bind_post_task.h b/third_party/chromium/media/cobalt/base/bind_post_task.h index b914e8adef5b..634a1d7d3411 100644 --- a/third_party/chromium/media/cobalt/base/bind_post_task.h +++ b/third_party/chromium/media/cobalt/base/bind_post_task.h @@ -5,6 +5,10 @@ #ifndef BASE_BIND_POST_TASK_H_ #define BASE_BIND_POST_TASK_H_ +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "base/location.h" namespace base { diff --git a/third_party/chromium/media/cobalt/base/check.h b/third_party/chromium/media/cobalt/base/check.h index d26320bc304d..17464daa91df 100644 --- a/third_party/chromium/media/cobalt/base/check.h +++ b/third_party/chromium/media/cobalt/base/check.h @@ -15,6 +15,10 @@ #ifndef BASE_CHECK_H_ #define BASE_CHECK_H_ +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "base/logging.h" #endif // BASE_CHECK_H_ diff --git a/third_party/chromium/media/cobalt/base/check_op.h b/third_party/chromium/media/cobalt/base/check_op.h index 296c84f099b8..74e046fcb39f 100644 --- a/third_party/chromium/media/cobalt/base/check_op.h +++ b/third_party/chromium/media/cobalt/base/check_op.h @@ -15,6 +15,10 @@ #ifndef BASE_CHECK_OP_H_ #define BASE_CHECK_OP_H_ +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "base/logging.h" #endif // BASE_CHECK_OP_H_ diff --git a/third_party/chromium/media/cobalt/base/cxx17_backports.h b/third_party/chromium/media/cobalt/base/cxx17_backports.h index a7c1c1305656..2f6e2ac8a508 100644 --- a/third_party/chromium/media/cobalt/base/cxx17_backports.h +++ b/third_party/chromium/media/cobalt/base/cxx17_backports.h @@ -15,6 +15,10 @@ #ifndef BASE_CXX17_BACKPORTS_H_ #define BASE_CXX17_BACKPORTS_H_ +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "base/stl_util.h" #endif // BASE_CXX17_BACKPORTS_H_ diff --git a/third_party/chromium/media/cobalt/base/hash/md5.h b/third_party/chromium/media/cobalt/base/hash/md5.h index 2993131d6091..8f9e6c008f0b 100644 --- a/third_party/chromium/media/cobalt/base/hash/md5.h +++ b/third_party/chromium/media/cobalt/base/hash/md5.h @@ -15,6 +15,10 @@ #ifndef BASE_HASH_MD5_H_ #define BASE_HASH_MD5_H_ +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "base/md5.h" #endif // BASE_HASH_MD5_H_ diff --git a/third_party/chromium/media/cobalt/base/notreached.h b/third_party/chromium/media/cobalt/base/notreached.h index 0afca93445f5..4eb09e6241b4 100644 --- a/third_party/chromium/media/cobalt/base/notreached.h +++ b/third_party/chromium/media/cobalt/base/notreached.h @@ -15,6 +15,10 @@ #ifndef BASE_NOTREACHED_H_ #define BASE_NOTREACHED_H_ +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include "base/logging.h" #endif // BASE_NOTREACHED_H_ diff --git a/third_party/chromium/media/cobalt/base/types/strong_alias.h b/third_party/chromium/media/cobalt/base/types/strong_alias.h index 8c148d46c22f..3fa4447f2387 100644 --- a/third_party/chromium/media/cobalt/base/types/strong_alias.h +++ b/third_party/chromium/media/cobalt/base/types/strong_alias.h @@ -5,6 +5,10 @@ #ifndef BASE_TYPES_STRONG_ALIAS_H_ #define BASE_TYPES_STRONG_ALIAS_H_ +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + #include #include #include diff --git a/third_party/chromium/media/cobalt/net/cookies/site_for_cookies.h b/third_party/chromium/media/cobalt/net/cookies/site_for_cookies.h index 296e3e4dad70..cf6b0406dfb9 100644 --- a/third_party/chromium/media/cobalt/net/cookies/site_for_cookies.h +++ b/third_party/chromium/media/cobalt/net/cookies/site_for_cookies.h @@ -5,6 +5,10 @@ #ifndef NET_SITE_FOR_COOKIES_H #define NET_SITE_FOR_COOKIES_H +#if !defined(STARBOARD) +#error "This file only works with Cobalt/Starboard." +#endif // !defined(STARBOARD) + namespace net { // Reduced version enough to make media code depending on it to be built. diff --git a/third_party/chromium/media/filters/chunk_demuxer.cc b/third_party/chromium/media/filters/chunk_demuxer.cc index e24189d7fb41..da5b8b109f54 100644 --- a/third_party/chromium/media/filters/chunk_demuxer.cc +++ b/third_party/chromium/media/filters/chunk_demuxer.cc @@ -17,7 +17,7 @@ #include "base/strings/string_number_conversions.h" #if defined(STARBOARD) #include "base/strings/string_split.h" -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "media/base/audio_decoder_config.h" @@ -63,7 +63,6 @@ std::string ExpectedCodecs(const std::string& content_type, } #if defined(STARBOARD) - // 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 @@ -95,8 +94,7 @@ bool ParseMimeType(const std::string& mime_type, std::string* type, // underlying parsers. return true; } - -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) } // namespace @@ -107,10 +105,10 @@ ChunkDemuxerStream::ChunkDemuxerStream(const std::string& mime_type, Type type, MediaTrack::Id media_track_id) : mime_type_(mime_type), type_(type), -#else // defined (STARBOARD) +#else // defined(STARBOARD) ChunkDemuxerStream::ChunkDemuxerStream(Type type, MediaTrack::Id media_track_id) : type_(type), -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) liveness_(DemuxerStream::LIVENESS_UNKNOWN), media_track_id_(media_track_id), state_(UNINITIALIZED), @@ -132,9 +130,9 @@ void ChunkDemuxerStream::AbortReads() { if (read_cb_) { #if defined(STARBOARD) std::move(read_cb_).Run(kAborted, {}); -#else // defined (STARBOARD) +#else // defined(STARBOARD) std::move(read_cb_).Run(kAborted, nullptr); -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) } } @@ -156,10 +154,10 @@ void ChunkDemuxerStream::Shutdown() { if (read_cb_) { #if defined(STARBOARD) std::move(read_cb_).Run(DemuxerStream::kOk, {StreamParserBuffer::CreateEOSBuffer()}); -#else // defined (STARBOARD) +#else // defined(STARBOARD) std::move(read_cb_).Run(DemuxerStream::kOk, StreamParserBuffer::CreateEOSBuffer()); -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) } } @@ -182,7 +180,7 @@ void ChunkDemuxerStream::Seek(base::TimeDelta time) { #if defined(STARBOARD) write_head_ = time; -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) stream_->Seek(time); } @@ -229,7 +227,6 @@ bool ChunkDemuxerStream::EvictCodedFrames(base::TimeDelta media_time, } #if defined(STARBOARD) - base::TimeDelta ChunkDemuxerStream::GetWriteHead() const { base::AutoLock auto_lock(lock_); return write_head_; @@ -246,8 +243,6 @@ void ChunkDemuxerStream::SetStreamMemoryLimitOverride(size_t memory_limit) { base::AutoLock auto_lock(lock_); stream_->set_memory_limit_override(memory_limit); } - - #endif // defined(STARBOARD) void ChunkDemuxerStream::OnMemoryPressure( @@ -335,9 +330,9 @@ bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config, DCHECK_EQ(state_, UNINITIALIZED); #if defined(STARBOARD) stream_ = std::make_unique(mime_type_, config, media_log); -#else // defined (STARBOARD) +#else // defined(STARBOARD) stream_ = std::make_unique(config, media_log); -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) return true; } @@ -355,9 +350,9 @@ bool ChunkDemuxerStream::UpdateVideoConfig(const VideoDecoderConfig& config, DCHECK_EQ(state_, UNINITIALIZED); #if defined(STARBOARD) stream_ = std::make_unique(mime_type_, config, media_log); -#else // defined (STARBOARD) +#else // defined(STARBOARD) stream_ = std::make_unique(config, media_log); -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) return true; } @@ -889,7 +884,6 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, } #if defined(STARBOARD) - ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, const std::string& mime_type) { std::string type, codecs; @@ -901,8 +895,7 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, id_to_mime_map_[id] = mime_type; return AddId(id, type, codecs); } - -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) ChunkDemuxer::Status ChunkDemuxer::AddIdInternal( const std::string& id, @@ -1107,7 +1100,6 @@ bool ChunkDemuxer::EvictCodedFrames(const std::string& id, } #if defined(STARBOARD) - base::TimeDelta ChunkDemuxer::GetWriteHead(const std::string& id) const { base::AutoLock auto_lock(lock_); DCHECK(IsValidId(id)); @@ -1138,7 +1130,6 @@ size_t ChunkDemuxer::GetSourceBufferStreamMemoryLimit(const std::string& id) { } return source_state_map_[id]->GetSourceBufferStreamMemoryLimit(); } - #endif // defined(STARBOARD) bool ChunkDemuxer::AppendData(const std::string& id, @@ -1689,7 +1680,7 @@ ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream( #else // defined(STARBOARD) std::unique_ptr stream = std::make_unique(type, media_track_id); -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) DCHECK(track_id_to_demux_stream_map_.find(media_track_id) == track_id_to_demux_stream_map_.end()); diff --git a/third_party/chromium/media/filters/chunk_demuxer.h b/third_party/chromium/media/filters/chunk_demuxer.h index 76a01f414f41..52ebae4f4e94 100644 --- a/third_party/chromium/media/filters/chunk_demuxer.h +++ b/third_party/chromium/media/filters/chunk_demuxer.h @@ -43,9 +43,9 @@ class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream { #if defined(STARBOARD) ChunkDemuxerStream(const std::string& mime_type, Type type, MediaTrack::Id media_track_id); -#else // defined (STARBOARD) +#else // defined(STARBOARD) ChunkDemuxerStream(Type type, MediaTrack::Id media_track_id); -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) ~ChunkDemuxerStream() override; // ChunkDemuxerStream control methods. @@ -134,14 +134,10 @@ class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream { std::string mime_type() const override { return mime_type_; } size_t GetStreamMemoryLimit(); void SetStreamMemoryLimitOverride(size_t memory_limit); -#endif // defined (STARBOARD) - -#if defined(STARBOARD) void Read(int max_number_of_buffers_to_read, ReadCB read_cb) override; -#else // defined (STARBOARD) +#else // defined(STARBOARD) void Read(ReadCB read_cb) override; -#endif // defined (STARBOARD) - +#endif // defined(STARBOARD) Type type() const override; Liveness liveness() const override; AudioDecoderConfig audio_decoder_config() override; @@ -193,7 +189,7 @@ class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream { const std::string mime_type_; int max_number_of_buffers_to_read_{1}; bool pending_config_change_ {false}; -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) // Specifies the type of the stream. const Type type_; @@ -295,7 +291,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // Special version of AddId() that retains the |mime_type| from the web app. Status AddId(const std::string& id, const std::string& mime_type) WARN_UNUSED_RESULT; -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) // Notifies a caller via |tracks_updated_cb| that the set of media tracks // for a given |id| has changed. This callback must be set before any calls to @@ -394,7 +390,6 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { #if defined(STARBOARD) base::TimeDelta GetWriteHead(const std::string& id) const; - void SetSourceBufferStreamMemoryLimit(const std::string& guid, size_t limit); size_t GetSourceBufferStreamMemoryLimit(const std::string& guid); #endif // defined(STARBOARD) @@ -600,7 +595,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { #if defined(STARBOARD) std::map id_to_mime_map_; -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) }; } // namespace media diff --git a/third_party/chromium/media/filters/source_buffer_stream.cc b/third_party/chromium/media/filters/source_buffer_stream.cc index 6d6789f0f3c9..f16184b3fde5 100644 --- a/third_party/chromium/media/filters/source_buffer_stream.cc +++ b/third_party/chromium/media/filters/source_buffer_stream.cc @@ -165,11 +165,11 @@ SourceBufferStream::SourceBufferStream(const std::string& mime_type, MediaLog* media_log) : mime_type_(mime_type), media_log_(media_log), -#else // defined (STARBOARD) +#else // defined(STARBOARD) SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config, MediaLog* media_log) : media_log_(media_log), -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) seek_buffer_timestamp_(kNoTimestamp), coded_frame_group_start_pts_(kNoTimestamp), range_for_next_append_(ranges_.end()), @@ -188,11 +188,11 @@ SourceBufferStream::SourceBufferStream(const std::string& mime_type, MediaLog* media_log) : mime_type_(mime_type), media_log_(media_log), -#else // defined (STARBOARD) +#else // defined(STARBOARD) SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config, MediaLog* media_log) : media_log_(media_log), -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) seek_buffer_timestamp_(kNoTimestamp), coded_frame_group_start_pts_(kNoTimestamp), range_for_next_append_(ranges_.end()), @@ -201,8 +201,12 @@ SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config, base::Milliseconds(kMinimumInterbufferDistanceInMs)), memory_limit_( GetDemuxerStreamVideoMemoryLimit(Demuxer::DemuxerTypes::kChunkDemuxer, +#if !defined(STARBOARD) + &video_config)) { +#else // !defined(STARBOARD) &video_config, mime_type_)) { +#endif // !defined(STARBOARD) DCHECK(video_config.IsValidConfig()); video_configs_.push_back(video_config); DVLOG(2) << __func__ << ": video_buffer_size= " << memory_limit_; @@ -2086,6 +2090,6 @@ base::TimeDelta SourceBufferStream::GetBufferedDurationForGarbageCollection() return duration; } -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) } // namespace media diff --git a/third_party/chromium/media/filters/source_buffer_stream.h b/third_party/chromium/media/filters/source_buffer_stream.h index 248b38ed40e9..c073ad94dbfa 100644 --- a/third_party/chromium/media/filters/source_buffer_stream.h +++ b/third_party/chromium/media/filters/source_buffer_stream.h @@ -68,12 +68,12 @@ class MEDIA_EXPORT SourceBufferStream { SourceBufferStream(const std::string& mime_type, const VideoDecoderConfig& video_config, MediaLog* media_log); -#else // defined (STARBOARD) +#else // defined(STARBOARD) SourceBufferStream(const AudioDecoderConfig& audio_config, MediaLog* media_log); SourceBufferStream(const VideoDecoderConfig& video_config, MediaLog* media_log); -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) SourceBufferStream(const TextTrackConfig& text_config, MediaLog* media_log); @@ -196,7 +196,7 @@ class MEDIA_EXPORT SourceBufferStream { memory_limit_ = memory_limit; memory_override_ = true; } -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) private: friend class SourceBufferStreamTest; @@ -416,7 +416,7 @@ class MEDIA_EXPORT SourceBufferStream { const std::string mime_type_; bool memory_override_ = false; -#endif // defined (STARBOARD) +#endif // defined(STARBOARD) // Used to report log messages that can help the web developer figure out what // is wrong with the content. diff --git a/tools/metrics/histograms/metadata/cobalt/histograms.xml b/tools/metrics/histograms/metadata/cobalt/histograms.xml index d6af96002295..68fa2a65ffdb 100644 --- a/tools/metrics/histograms/metadata/cobalt/histograms.xml +++ b/tools/metrics/histograms/metadata/cobalt/histograms.xml @@ -185,6 +185,17 @@ Always run the pretty print utility on this file after editing: + + + + async@google.com + cobalt-team@google.com + + Timing data for the seek latency of WebMediaPlayer. + + +