Bug 938022. Part 1: Update mAudioEndTime from SendStreamData. r=cpearce

--HG--
extra : rebase_source : 2cbb5dcbe4d97e21f276559c5fb7dac99d919a2b
This commit is contained in:
Robert O'Callahan 2013-11-23 00:33:24 +13:00
parent cd7355c0de
commit 6748566aed
3 changed files with 139 additions and 92 deletions

View File

@ -627,10 +627,6 @@ void MediaDecoderStateMachine::SendStreamData()
if (mState == DECODER_STATE_DECODING_METADATA)
return;
if (!mDecoder->IsSameOriginMedia()) {
return;
}
// If there's still an audio thread alive, then we can't send any stream
// data yet since both SendStreamData and the audio thread want to be in
// charge of popping the audio queue. We're waiting for the audio thread
@ -639,97 +635,99 @@ void MediaDecoderStateMachine::SendStreamData()
return;
int64_t minLastAudioPacketTime = INT64_MAX;
SourceMediaStream* mediaStream = stream->mStream;
StreamTime endPosition = 0;
if (!stream->mStreamInitialized) {
if (mInfo.HasAudio()) {
AudioSegment* audio = new AudioSegment();
mediaStream->AddTrack(TRACK_AUDIO, mInfo.mAudio.mRate, 0, audio);
}
if (mInfo.HasVideo()) {
VideoSegment* video = new VideoSegment();
mediaStream->AddTrack(TRACK_VIDEO, RATE_VIDEO, 0, video);
}
stream->mStreamInitialized = true;
}
if (mInfo.HasAudio()) {
nsAutoTArray<AudioData*,10> audio;
// It's OK to hold references to the AudioData because while audio
// is captured, only the decoder thread pops from the queue (see below).
mReader->AudioQueue().GetElementsAfter(stream->mLastAudioPacketTime, &audio);
AudioSegment output;
for (uint32_t i = 0; i < audio.Length(); ++i) {
SendStreamAudio(audio[i], stream, &output);
}
if (output.GetDuration() > 0) {
mediaStream->AppendToTrack(TRACK_AUDIO, &output);
}
if (mReader->AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) {
mediaStream->EndTrack(TRACK_AUDIO);
stream->mHaveSentFinishAudio = true;
}
minLastAudioPacketTime = std::min(minLastAudioPacketTime, stream->mLastAudioPacketTime);
endPosition = std::max(endPosition,
TicksToTimeRoundDown(mInfo.mAudio.mRate, stream->mAudioFramesWritten));
}
if (mInfo.HasVideo()) {
nsAutoTArray<VideoData*,10> video;
// It's OK to hold references to the VideoData only the decoder thread
// pops from the queue.
mReader->VideoQueue().GetElementsAfter(stream->mNextVideoTime + mStartTime, &video);
VideoSegment output;
for (uint32_t i = 0; i < video.Length(); ++i) {
VideoData* v = video[i];
if (stream->mNextVideoTime + mStartTime < v->mTime) {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing last video to MediaStream %p for %lld ms",
mDecoder.get(), mediaStream,
v->mTime - (stream->mNextVideoTime + mStartTime)));
// Write last video frame to catch up. mLastVideoImage can be null here
// which is fine, it just means there's no video.
WriteVideoToMediaStream(stream->mLastVideoImage,
v->mTime - (stream->mNextVideoTime + mStartTime), stream->mLastVideoImageDisplaySize,
&output);
stream->mNextVideoTime = v->mTime - mStartTime;
}
if (stream->mNextVideoTime + mStartTime < v->GetEndTime()) {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing video frame %lld to MediaStream %p for %lld ms",
mDecoder.get(), v->mTime, mediaStream,
v->GetEndTime() - (stream->mNextVideoTime + mStartTime)));
WriteVideoToMediaStream(v->mImage,
v->GetEndTime() - (stream->mNextVideoTime + mStartTime), v->mDisplay,
&output);
stream->mNextVideoTime = v->GetEndTime() - mStartTime;
stream->mLastVideoImage = v->mImage;
stream->mLastVideoImageDisplaySize = v->mDisplay;
} else {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder skipping writing video frame %lld to MediaStream",
mDecoder.get(), v->mTime));
}
}
if (output.GetDuration() > 0) {
mediaStream->AppendToTrack(TRACK_VIDEO, &output);
}
if (mReader->VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) {
mediaStream->EndTrack(TRACK_VIDEO);
stream->mHaveSentFinishVideo = true;
}
endPosition = std::max(endPosition,
TicksToTimeRoundDown(RATE_VIDEO, stream->mNextVideoTime - stream->mInitialTime));
}
if (!stream->mHaveSentFinish) {
stream->mStream->AdvanceKnownTracksTime(endPosition);
}
bool finished =
(!mInfo.HasAudio() || mReader->AudioQueue().IsFinished()) &&
(!mInfo.HasVideo() || mReader->VideoQueue().IsFinished());
if (finished && !stream->mHaveSentFinish) {
stream->mHaveSentFinish = true;
stream->mStream->Finish();
if (mDecoder->IsSameOriginMedia()) {
SourceMediaStream* mediaStream = stream->mStream;
StreamTime endPosition = 0;
if (!stream->mStreamInitialized) {
if (mInfo.HasAudio()) {
AudioSegment* audio = new AudioSegment();
mediaStream->AddTrack(TRACK_AUDIO, mInfo.mAudio.mRate, 0, audio);
}
if (mInfo.HasVideo()) {
VideoSegment* video = new VideoSegment();
mediaStream->AddTrack(TRACK_VIDEO, RATE_VIDEO, 0, video);
}
stream->mStreamInitialized = true;
}
if (mInfo.HasAudio()) {
nsAutoTArray<AudioData*,10> audio;
// It's OK to hold references to the AudioData because while audio
// is captured, only the decoder thread pops from the queue (see below).
mReader->AudioQueue().GetElementsAfter(stream->mLastAudioPacketTime, &audio);
AudioSegment output;
for (uint32_t i = 0; i < audio.Length(); ++i) {
SendStreamAudio(audio[i], stream, &output);
}
if (output.GetDuration() > 0) {
mediaStream->AppendToTrack(TRACK_AUDIO, &output);
}
if (mReader->AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) {
mediaStream->EndTrack(TRACK_AUDIO);
stream->mHaveSentFinishAudio = true;
}
minLastAudioPacketTime = std::min(minLastAudioPacketTime, stream->mLastAudioPacketTime);
endPosition = std::max(endPosition,
TicksToTimeRoundDown(mInfo.mAudio.mRate, stream->mAudioFramesWritten));
}
if (mInfo.HasVideo()) {
nsAutoTArray<VideoData*,10> video;
// It's OK to hold references to the VideoData only the decoder thread
// pops from the queue.
mReader->VideoQueue().GetElementsAfter(stream->mNextVideoTime + mStartTime, &video);
VideoSegment output;
for (uint32_t i = 0; i < video.Length(); ++i) {
VideoData* v = video[i];
if (stream->mNextVideoTime + mStartTime < v->mTime) {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing last video to MediaStream %p for %lld ms",
mDecoder.get(), mediaStream,
v->mTime - (stream->mNextVideoTime + mStartTime)));
// Write last video frame to catch up. mLastVideoImage can be null here
// which is fine, it just means there's no video.
WriteVideoToMediaStream(stream->mLastVideoImage,
v->mTime - (stream->mNextVideoTime + mStartTime), stream->mLastVideoImageDisplaySize,
&output);
stream->mNextVideoTime = v->mTime - mStartTime;
}
if (stream->mNextVideoTime + mStartTime < v->GetEndTime()) {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing video frame %lld to MediaStream %p for %lld ms",
mDecoder.get(), v->mTime, mediaStream,
v->GetEndTime() - (stream->mNextVideoTime + mStartTime)));
WriteVideoToMediaStream(v->mImage,
v->GetEndTime() - (stream->mNextVideoTime + mStartTime), v->mDisplay,
&output);
stream->mNextVideoTime = v->GetEndTime() - mStartTime;
stream->mLastVideoImage = v->mImage;
stream->mLastVideoImageDisplaySize = v->mDisplay;
} else {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder skipping writing video frame %lld to MediaStream",
mDecoder.get(), v->mTime));
}
}
if (output.GetDuration() > 0) {
mediaStream->AppendToTrack(TRACK_VIDEO, &output);
}
if (mReader->VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) {
mediaStream->EndTrack(TRACK_VIDEO);
stream->mHaveSentFinishVideo = true;
}
endPosition = std::max(endPosition,
TicksToTimeRoundDown(RATE_VIDEO, stream->mNextVideoTime - stream->mInitialTime));
}
if (!stream->mHaveSentFinish) {
stream->mStream->AdvanceKnownTracksTime(endPosition);
}
if (finished && !stream->mHaveSentFinish) {
stream->mHaveSentFinish = true;
stream->mStream->Finish();
}
}
if (mAudioCaptured) {
@ -748,6 +746,7 @@ void MediaDecoderStateMachine::SendStreamData()
mReader->AudioQueue().PushFront(a.forget());
break;
}
mAudioEndTime = std::max(mAudioEndTime, a->GetEndTime());
}
if (finished) {

View File

@ -257,6 +257,7 @@ support-files =
[test_streams_element_capture.html]
[test_streams_element_capture_reset.html]
[test_streams_element_capture_createObjectURL.html]
[test_streams_element_capture_playback.html]
[test_streams_gc.html]
[test_streams_tracks.html]
[test_texttrack.html]

View File

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test that capturing a stream doesn't stop the underlying element from firing events</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<audio id="a"></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var a = document.getElementById('a');
var validTimeUpdate = false;
function startTest() {
a.src = "big.wav";
var context = new AudioContext();
var node = context.createMediaElementSource(a);
node.connect(context.destination);
a.addEventListener("timeupdate", function() {
if (a.currentTime > 0.0 && a.currentTime < 5.0 && !validTimeUpdate) {
validTimeUpdate = true;
ok(true, "Received reasonable currentTime in a timeupdate");
SimpleTest.finish();
}
});
a.addEventListener("ended", function() {
if (!validTimeUpdate) {
ok(false, "Received reasonable currentTime in a timeupdate");
SimpleTest.finish();
}
});
a.play();
}
if (a.canPlayType("audio/wave")) {
startTest();
} else {
todo(false, "No playable audio");
}
</script>
</pre>
</body>
</html>