tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit e8963b5923b52555159bd08c0a24eb82b9cbb9df
parent 8e58b5d090c09347be8dddf06e7ac411998d467d
Author: Karl Tomlinson <karlt+@karlt.net>
Date:   Mon, 27 Oct 2025 01:06:22 +0000

Bug 1985195 Advance OfflineClockDriver time only when rendering r=pehrsons

Previously the driver would advance its time, but the graph would not, which
sometimes led to a large rendering batch the first iteration that the graph
rendered.

Differential Revision: https://phabricator.services.mozilla.com/D268334

Diffstat:
Mdom/media/GraphDriver.cpp | 5+++--
Mdom/media/GraphDriver.h | 10++++++++++
Mdom/media/MediaTrackGraph.cpp | 37++++++++++++++-----------------------
Mdom/media/gtest/MockGraphInterface.h | 2++
Mdom/media/gtest/TestMediaDataEncoder.cpp | 1+
Adom/media/gtest/TestOfflineClockDriver.cpp | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdom/media/gtest/moz.build | 1+
7 files changed, 86 insertions(+), 25 deletions(-)

diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp @@ -275,8 +275,9 @@ void OfflineClockDriver::RunThread() { } MediaTime OfflineClockDriver::GetIntervalForIteration() { - return MediaTrackGraphImpl::RoundUpToEndOfAudioBlock( - MillisecondsToMediaTime(MEDIA_GRAPH_TARGET_PERIOD_MS)); + return MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(std::clamp<MediaTime>( + mEndTime - mStateComputedTime, 0, + MillisecondsToMediaTime(MEDIA_GRAPH_TARGET_PERIOD_MS))); } /* Helper to proxy the GraphInterface methods used by a running diff --git a/dom/media/GraphDriver.h b/dom/media/GraphDriver.h @@ -520,9 +520,19 @@ class OfflineClockDriver final : public ThreadedDriver { void RunThread() override; + void SetTickCountToRender(uint32_t aTicksToProcess) { + MOZ_ASSERT(InIteration()); + MOZ_ASSERT(mEndTime == 0); + mEndTime = aTicksToProcess; + } + protected: TimeDuration NextIterationWaitDuration() override { return TimeDuration(); } MediaTime GetIntervalForIteration() override; + + private: + // The graph will advance up to this time. Graph thread. + GraphTime mEndTime = 0; }; enum class AudioInputType { Unknown, Voice }; diff --git a/dom/media/MediaTrackGraph.cpp b/dom/media/MediaTrackGraph.cpp @@ -1678,14 +1678,13 @@ auto MediaTrackGraphImpl::OneIterationImpl( NS_ProcessPendingEvents(nullptr); } - GraphTime stateTime = std::min(aStateTime, GraphTime(mEndTime)); - UpdateGraph(stateTime); + UpdateGraph(aStateTime); - mStateComputedTime = stateTime; + mStateComputedTime = aStateTime; GraphTime oldProcessedTime = mProcessedTime; Process(aMixerReceiver); - MOZ_ASSERT(mProcessedTime == stateTime); + MOZ_ASSERT(mProcessedTime == aStateTime); UpdateCurrentTimeForTracks(oldProcessedTime); @@ -4124,25 +4123,17 @@ void MediaTrackGraph::StartNonRealtimeProcessing(uint32_t aTicksToProcess) { MediaTrackGraphImpl* graph = static_cast<MediaTrackGraphImpl*>(this); NS_ASSERTION(!graph->mRealtime, "non-realtime only"); - class Message : public ControlMessage { - public: - explicit Message(MediaTrackGraphImpl* aGraph, uint32_t aTicksToProcess) - : ControlMessage(nullptr), - mGraph(aGraph), - mTicksToProcess(aTicksToProcess) {} - void Run() override { - TRACE("MTG::StartNonRealtimeProcessing ControlMessage"); - MOZ_ASSERT(mGraph->mEndTime == 0, - "StartNonRealtimeProcessing should be called only once"); - mGraph->mEndTime = mGraph->RoundUpToEndOfAudioBlock( - mGraph->mStateComputedTime + mTicksToProcess); - } - // The graph owns this message. - MediaTrackGraphImpl* MOZ_NON_OWNING_REF mGraph; - uint32_t mTicksToProcess; - }; - - graph->AppendMessage(MakeUnique<Message>(graph, aTicksToProcess)); + graph->QueueControlMessageWithNoShutdown([graph = RefPtr{graph}, + aTicksToProcess]() { + TRACE("MTG::StartNonRealtimeProcessing ControlMessage"); + MOZ_ASSERT(graph->mStateComputedTime == 0); + MOZ_ASSERT(graph->mEndTime == 0, + "StartNonRealtimeProcessing should be called only once"); + graph->mEndTime = aTicksToProcess; + OfflineClockDriver* driver = graph->CurrentDriver()->AsOfflineClockDriver(); + MOZ_ASSERT(driver); + driver->SetTickCountToRender(aTicksToProcess); + }); } void MediaTrackGraphImpl::InterruptJS() { diff --git a/dom/media/gtest/MockGraphInterface.h b/dom/media/gtest/MockGraphInterface.h @@ -27,8 +27,10 @@ class MockGraphInterface : public GraphInterface { #endif /* OneIteration cannot be mocked because IterationResult is non-memmovable and * cannot be passed as a parameter, which GMock does internally. */ + MOCK_METHOD(void, MockIteration, (GraphTime aStateComputedTime), ()); IterationResult OneIteration(GraphTime aStateComputedTime, MixerCallbackReceiver* aMixerReceiver) { + MockIteration(aStateComputedTime); GraphDriver* driver = mCurrentDriver; if (aMixerReceiver) { mMixer.StartMixing(); diff --git a/dom/media/gtest/TestMediaDataEncoder.cpp b/dom/media/gtest/TestMediaDataEncoder.cpp @@ -1160,5 +1160,6 @@ TEST_F(MediaDataEncoderTest, VP9EncodeWithScalabilityModeL1T3) { # endif #endif +#undef BLOCK_SIZE #undef GET_OR_RETURN_ON_ERROR #undef RUN_IF_SUPPORTED diff --git a/dom/media/gtest/TestOfflineClockDriver.cpp b/dom/media/gtest/TestOfflineClockDriver.cpp @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MockGraphInterface.h" +#include "gtest/gtest.h" +#include "mozilla/gtest/WaitFor.h" + +using namespace mozilla; +using testing::InSequence; +using testing::Return; + +TEST(TestOfflineClockDriver, TimeAdvance) +MOZ_CAN_RUN_SCRIPT_BOUNDARY { + TrackRate rate = + CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false); + RefPtr graph = new MockGraphInterface(rate); + RefPtr driver = new OfflineClockDriver(graph, rate); + MozPromiseHolder<GenericPromise> doneHolder; + RefPtr<GenericPromise> done = doneHolder.Ensure(__func__); + { + GraphTime length = WEBAUDIO_BLOCK_SIZE / 4; // < WEBAUDIO_BLOCK_SIZE + auto EnsureNextIteration = [&](GraphTime aTime) { + driver->EnsureNextIteration(); + }; + InSequence s; + EXPECT_CALL(*graph, MockIteration(0)) + // Time should not advance on the first iteration to process control + // messages + .WillOnce(EnsureNextIteration) + // ... nor on subsequent. + .WillOnce([&driver, length](GraphTime aTime) { + driver->SetTickCountToRender(length); + driver->EnsureNextIteration(); + }); + EXPECT_CALL(*graph, MockIteration(WEBAUDIO_BLOCK_SIZE)) + // Rendering iteration + .WillOnce(EnsureNextIteration) + // Time should not advance after rendering. + .WillOnce([&](GraphTime aTime) { + // Tell the driver to exit its event loop. + graph->StopIterating(); + doneHolder.Resolve(true, __func__); + }); + } + + graph->SetCurrentDriver(driver); + driver->EnsureNextIteration(); + driver->Start(); + WaitForResolve(done); + // Clean up. + driver->Shutdown(); +} diff --git a/dom/media/gtest/moz.build b/dom/media/gtest/moz.build @@ -58,6 +58,7 @@ UNIFIED_SOURCES += [ "TestMP3Demuxer.cpp", "TestMP4Demuxer.cpp", "TestMuxer.cpp", + "TestOfflineClockDriver.cpp", "TestOggWriter.cpp", "TestOpusParser.cpp", "TestRust.cpp",