From f69c4dc5769dedf36bc4b30b9e5a42f1c0acd1fe Mon Sep 17 00:00:00 2001 From: roytam1 Date: Fri, 28 Jun 2024 23:13:42 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - bug 1191889 skip Close() when not initialized r=roc (9bbb78c01e) - Bug 1175319 - Remove outdated MDSM documenation. r=jya DONTBUILD (775d7b1cb4) - Bug 1265978. Part 1 - add mozDumpDebugInfo() to HTMLMediaElement.webidl. r=bz. (6518ad6a08) - Bug 1265978. Part 2 - add methods to MediaDecoder and MDSM to dump debugging info. r=jya. (1b8ad138c8) - Bug 1265978. Part 2.5 - also dump reader data. r=jya. (d866537269) - Bug 1265978. Part 3 - invoke mozDumpDebugInfo() from JS. r=jya. (7a5a7b5837) --- dom/html/HTMLMediaElement.cpp | 8 + dom/html/HTMLMediaElement.h | 2 + dom/media/MediaCache.cpp | 3 + dom/media/MediaDecoder.cpp | 21 +++ dom/media/MediaDecoder.h | 179 +--------------------- dom/media/MediaDecoderStateMachine.cpp | 26 ++++ dom/media/MediaDecoderStateMachine.h | 2 + dom/media/mediasource/test/mediasource.js | 13 +- dom/media/test/manifest.js | 13 +- dom/webidl/HTMLMediaElement.webidl | 3 + toolkit/devtools/debugger/utils.js | 8 +- 11 files changed, 97 insertions(+), 181 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 9dc401a005..80faedcda3 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -557,6 +557,14 @@ HTMLMediaElement::GetMozDebugReaderData(nsAString& aString) } } +void +HTMLMediaElement::MozDumpDebugInfo() +{ + if (mDecoder) { + mDecoder->DumpDebugInfo(); + } +} + already_AddRefed HTMLMediaElement::GetSrcObject() const { diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 5f69aa95b9..c114270e6e 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -611,6 +611,8 @@ public: // data. Used for debugging purposes. void GetMozDebugReaderData(nsAString& aString); + void MozDumpDebugInfo(); + already_AddRefed GetSrcObject() const; void SetSrcObject(DOMMediaStream& aValue); void SetSrcObject(DOMMediaStream* aValue); diff --git a/dom/media/MediaCache.cpp b/dom/media/MediaCache.cpp index 0667265ce7..6582113738 100644 --- a/dom/media/MediaCache.cpp +++ b/dom/media/MediaCache.cpp @@ -1975,6 +1975,9 @@ MediaCacheStream::Close() { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); + if (!mInitialized) + return; + if (gMediaCache) { ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor()); CloseInternal(mon); diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index cef367f76a..03174335a1 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -51,11 +51,15 @@ static const uint64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2; // avoid redefined macro in unified build #undef DECODER_LOG +#undef DUMP_LOG LazyLogModule gMediaDecoderLog("MediaDecoder"); #define DECODER_LOG(x, ...) \ MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("Decoder=%p " x, this, ##__VA_ARGS__)) +#define DUMP_LOG(x, ...) \ + NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString("Decoder=%p " x, this, ##__VA_ARGS__).get(), nullptr, nullptr, -1) + static const char* ToPlayStateStr(MediaDecoder::PlayState aState) { @@ -1876,6 +1880,23 @@ MediaDecoder::NextFrameBufferedStatus() : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; } +void +MediaDecoder::DumpDebugInfo() +{ + DUMP_LOG("metadata: channels=%u rate=%u hasAudio=%d hasVideo=%d, " + "state: mPlayState=%s mIsDormant=%d, mShuttingDown=%d", + mInfo->mAudio.mChannels, mInfo->mAudio.mRate, mInfo->HasAudio(), mInfo->HasVideo(), + PlayStateStr(), mIsDormant, mShuttingDown); + + nsString str; + GetMozDebugReaderData(str); + DUMP_LOG("reader data:\n%s", NS_ConvertUTF16toUTF8(str).get()); + + if (!mShuttingDown && GetStateMachine()) { + GetStateMachine()->DumpDebugInfo(); + } +} + void MediaDecoder::NotifyAudibleStateChanged() { diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 23ce9b3c32..a40f7d3844 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -3,184 +3,7 @@ /* 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/. */ -/* -Each video element based on MediaDecoder has a state machine to manage -its play state and keep the current frame up to date. All state machines -share time in a single shared thread. Each decoder also has a TaskQueue -running in a SharedThreadPool to decode audio and video data. -Each decoder also has a thread to push decoded audio -to the hardware. This thread is not created until playback starts, but -currently is not destroyed when paused, only when playback ends. -The decoder owns the resources for downloading the media file, and the -high level state. It holds an owning reference to the state machine that -owns all the resources related to decoding data, and manages the low level -decoding operations and A/V sync. - -Each state machine runs on the shared state machine thread. Every time some -action is required for a state machine, it is scheduled to run on the shared -the state machine thread. The state machine runs one "cycle" on the state -machine thread, and then returns. If necessary, it will schedule itself to -run again in future. While running this cycle, it must not block the -thread, as other state machines' events may need to run. State shared -between a state machine's threads is synchronised via the monitor owned -by its MediaDecoder object. - -The Main thread controls the decode state machine by setting the value -of a mPlayState variable and notifying on the monitor based on the -high level player actions required (Seek, Pause, Play, etc). - -The player states are the states requested by the client through the -DOM API. They represent the desired state of the player, while the -decoder's state represents the actual state of the decoder. - -The high level state of the player is maintained via a PlayState value. -It can have the following states: - -START - The decoder has been initialized but has no resource loaded. -PAUSED - A request via the API has been received to pause playback. -LOADING - A request via the API has been received to load a resource. -PLAYING - A request via the API has been received to start playback. -SEEKING - A request via the API has been received to start seeking. -COMPLETED - Playback has completed. -SHUTDOWN - The decoder is about to be destroyed. - -State transition occurs when the Media Element calls the Play, Seek, -etc methods on the MediaDecoder object. When the transition -occurs MediaDecoder then calls the methods on the decoder state -machine object to cause it to behave as required by the play state. -State transitions will likely schedule the state machine to run to -affect the change. - -An implementation of the MediaDecoderStateMachine class is the event -that gets dispatched to the state machine thread. Each time the event is run, -the state machine must cycle the state machine once, and then return. - -The state machine has the following states: - -DECODING_METADATA - The media headers are being loaded, and things like framerate, etc are - being determined. -DECODING_FIRSTFRAME - The first frame of audio/video data is being decoded. -DECODING - The decode has started. If the PlayState is PLAYING, the decode thread - should be alive and decoding video and audio frame, the audio thread - should be playing audio, and the state machine should run periodically - to update the video frames being displayed. -SEEKING - A seek operation is in progress. The decode thread should be seeking. -BUFFERING - Decoding is paused while data is buffered for smooth playback. If playback - is paused (PlayState transitions to PAUSED) we'll destory the decode thread. -COMPLETED - The resource has completed decoding, but possibly not finished playback. - The decode thread will be destroyed. Once playback finished, the audio - thread will also be destroyed. -SHUTDOWN - The decoder object and its state machine are about to be destroyed. - Once the last state machine has been destroyed, the shared state machine - thread will also be destroyed. It will be recreated later if needed. - -The following result in state transitions. - -Shutdown() - Clean up any resources the MediaDecoderStateMachine owns. -Play() - Start decoding and playback of media data. -Buffer - This is not user initiated. It occurs when the - available data in the stream drops below a certain point. -Complete - This is not user initiated. It occurs when the - stream is completely decoded. -Seek(double) - Seek to the time position given in the resource. - -A state transition diagram: - - |---<-- DECODING_METADATA ----->--------| - | | | - Seek(t) v Shutdown() - | | | - -->--- DECODING_FIRSTFRAME |------->-----------------| - | | | - | Shutdown() | - | | | - v |-->----------------->--------------------------| - |---------------->----->------------------------| v - DECODING | | | | | - ^ v Seek(t) | | | | - | Play() | v | | | - ^-----------<----SEEKING | v Complete v v - | | | | | | - | | | COMPLETED SHUTDOWN-<-| - ^ ^ | |Shutdown() | - | | | >-------->-----^ - | Play() |Seek(t) |Buffer() | - -----------<--------<-------BUFFERING | - | ^ - v Shutdown() | - | | - ------------>-----| - -The following represents the states that the MediaDecoder object -can be in, and the valid states the MediaDecoderStateMachine can be in at that -time: - -player LOADING decoder DECODING_METADATA, DECODING_FIRSTFRAME -player PLAYING decoder DECODING, BUFFERING, SEEKING, COMPLETED -player PAUSED decoder DECODING, BUFFERING, SEEKING, COMPLETED -player SEEKING decoder SEEKING -player COMPLETED decoder SHUTDOWN -player SHUTDOWN decoder SHUTDOWN - -The general sequence of events is: - -1) The video element calls Load on MediaDecoder. This creates the - state machine and starts the channel for downloading the - file. It instantiates and schedules the MediaDecoderStateMachine. The - high level LOADING state is entered, which results in the decode - thread being created and starting to decode metadata. These are - the headers that give the video size, framerate, etc. Load() returns - immediately to the calling video element. - -2) When the metadata has been loaded by the decode thread, the state machine - will call a method on the video element object to inform it that this - step is done, so it can do the things required by the video specification - at this stage. The decode thread then continues to decode the first frame - of data. - -3) When the first frame of data has been successfully decoded the state - machine calls a method on the video element object to inform it that - this step has been done, once again so it can do the required things - by the video specification at this stage. - - This results in the high level state changing to PLAYING or PAUSED - depending on any user action that may have occurred. - - While the play state is PLAYING, the decode thread will decode - data, and the audio thread will push audio data to the hardware to - be played. The state machine will run periodically on the shared - state machine thread to ensure video frames are played at the - correct time; i.e. the state machine manages A/V sync. - -The Shutdown method on MediaDecoder closes the download channel, and -signals to the state machine that it should shutdown. The state machine -shuts down asynchronously, and will release the owning reference to the -state machine once its threads are shutdown. - -The owning object of a MediaDecoder object *MUST* call Shutdown when -destroying the MediaDecoder object. - -*/ #if !defined(MediaDecoder_h_) #define MediaDecoder_h_ @@ -676,6 +499,8 @@ private: // data. Used for debugging purposes. virtual void GetMozDebugReaderData(nsAString& aString) {} + virtual void DumpDebugInfo(); + protected: virtual ~MediaDecoder(); diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 8a07186e4e..6989b3daf3 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -63,6 +63,7 @@ using namespace mozilla::media; #undef LOG #undef DECODER_LOG #undef VERBOSE_LOG +#undef DUMP_LOG #define LOG(m, l, x, ...) \ MOZ_LOG(m, l, ("Decoder=%p " x, mDecoderID, ##__VA_ARGS__)) @@ -79,6 +80,9 @@ using namespace mozilla::media; #define DECODER_WARN(x, ...) \ DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get())) +#define DUMP_LOG(x, ...) \ + NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get(), nullptr, nullptr, -1) + // Certain constants get stored as member variables and then adjusted by various // scale factors on a per-decoder basis. We want to make sure to avoid using these // constants directly, so we put them in a namespace. @@ -2867,6 +2871,28 @@ uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const : std::max(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE); } +void +MediaDecoderStateMachine::DumpDebugInfo() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // It is fine to capture a raw pointer here because MediaDecoder only call + // this function before shutdown begins. + nsCOMPtr r = NS_NewRunnableFunction([this] () { + DUMP_LOG( + "GetMediaTime=%lld GetClock=%lld " + "mState=%s mPlayState=%d mDecodingFirstFrame=%d IsPlaying=%d " + "mAudioStatus=%s mVideoStatus=%s mDecodedAudioEndTime=%lld mDecodedVideoEndTime=%lld " + "mIsAudioPrerolling=%d mIsVideoPrerolling=%d", + GetMediaTime(), mMediaSink->IsStarted() ? GetClock() : -1, + gMachineStateStr[mState], mPlayState.Ref(), mDecodingFirstFrame, IsPlaying(), + AudioRequestStatus(), VideoRequestStatus(), mDecodedAudioEndTime, mDecodedVideoEndTime, + mIsAudioPrerolling, mIsVideoPrerolling); + }); + + OwnerThread()->DispatchStateChange(r.forget()); +} + void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded) { diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 0954219757..b9ca7647f0 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -157,6 +157,8 @@ public: DECODER_STATE_ERROR }; + void DumpDebugInfo(); + void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded); // Remove an output stream added with AddOutputStream. void RemoveOutputStream(MediaStream* aStream); diff --git a/dom/media/mediasource/test/mediasource.js b/dom/media/mediasource/test/mediasource.js index bd25e19ba8..f65b6943f1 100644 --- a/dom/media/mediasource/test/mediasource.js +++ b/dom/media/mediasource/test/mediasource.js @@ -18,7 +18,8 @@ function runWithMSE(testFunction) { addLoadEvent(function () { SpecialPowers.pushPrefEnv({"set": [ - [ "media.mediasource.enabled", true ], + [ "media.mediasource.enabled", true ], + [ "media.test.dumpDebugInfo", true ], ]}, bootstrapTest); }); @@ -105,3 +106,13 @@ function fetchAndLoad(sb, prefix, chunks, suffix) { return rv; }); } + +//Register timeout function to dump debugging logs. +SimpleTest.registerTimeoutFunction(function() { + for (var v of document.getElementsByTagName("video")) { + v.mozDumpDebugInfo(); + } + for (var a of document.getElementsByTagName("audio")) { + a.mozDumpDebugInfo(); + } +}); \ No newline at end of file diff --git a/dom/media/test/manifest.js b/dom/media/test/manifest.js index 7286e89207..8645d3a92c 100644 --- a/dom/media/test/manifest.js +++ b/dom/media/test/manifest.js @@ -797,7 +797,8 @@ var PARALLEL_TESTS = 2; var gTestPrefs = [ ['media.recorder.max_memory', 1024], ["media.preload.default", 2], // default preload = metadata - ["media.preload.auto", 3] // auto preload = enough + ["media.preload.auto", 3], // auto preload = enough + ["media.test.dumpDebugInfo", true], ]; // When true, we'll loop forever on whatever test we run. Use this to debug @@ -962,4 +963,14 @@ function isSlowPlatform() { // like file_access_controls.html. if ("SimpleTest" in window) { SimpleTest.requestFlakyTimeout("untriaged"); + + // Register timeout function to dump debugging logs. + SimpleTest.registerTimeoutFunction(function() { + for (var v of document.getElementsByTagName("video")) { + v.mozDumpDebugInfo(); + } + for (var a of document.getElementsByTagName("audio")) { + a.mozDumpDebugInfo(); + } + }); } diff --git a/dom/webidl/HTMLMediaElement.webidl b/dom/webidl/HTMLMediaElement.webidl index 82c3446ab0..0385c6a01a 100644 --- a/dom/webidl/HTMLMediaElement.webidl +++ b/dom/webidl/HTMLMediaElement.webidl @@ -107,6 +107,9 @@ partial interface HTMLMediaElement { [ChromeOnly] readonly attribute DOMString mozDebugReaderData; + [Pref="media.test.dumpDebugInfo"] + void mozDumpDebugInfo(); + attribute MediaStream? srcObject; // TODO: remove prefixed version soon (1183495). attribute MediaStream? mozSrcObject; diff --git a/toolkit/devtools/debugger/utils.js b/toolkit/devtools/debugger/utils.js index 5cf1dbd1ba..31f4092cc3 100644 --- a/toolkit/devtools/debugger/utils.js +++ b/toolkit/devtools/debugger/utils.js @@ -106,7 +106,7 @@ const SourceUtils = { clearCache: function() { this._labelsCache.clear(); this._groupsCache.clear(); - this._minifiedCache = new WeakMap(); + this._minifiedCache.clear(); }, /** @@ -304,7 +304,11 @@ const SourceUtils = { } // Prepend the hostname and port number. if (aSeq == 4) { - let host = aUrl.hostPort; + let host; + try { + // Bug 1261860: jar: URLs throw when accessing `hostPost` + host = aUrl.hostPort; + } catch(e) {} if (host) { return this.trimUrl(aUrl, host + "/" + aLabel, aSeq + 1); }