mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
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)
This commit is contained in:
@@ -557,6 +557,14 @@ HTMLMediaElement::GetMozDebugReaderData(nsAString& aString)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::MozDumpDebugInfo()
|
||||
{
|
||||
if (mDecoder) {
|
||||
mDecoder->DumpDebugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream>
|
||||
HTMLMediaElement::GetSrcObject() const
|
||||
{
|
||||
|
||||
@@ -611,6 +611,8 @@ public:
|
||||
// data. Used for debugging purposes.
|
||||
void GetMozDebugReaderData(nsAString& aString);
|
||||
|
||||
void MozDumpDebugInfo();
|
||||
|
||||
already_AddRefed<DOMMediaStream> GetSrcObject() const;
|
||||
void SetSrcObject(DOMMediaStream& aValue);
|
||||
void SetSrcObject(DOMMediaStream* aValue);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
+2
-177
@@ -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();
|
||||
|
||||
|
||||
@@ -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<uint32_t>(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<nsIRunnable> 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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user