mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 449307 - Support duration attribute on media elements - r+sr=roc
This commit is contained in:
parent
b3cdf58216
commit
1379f310fb
@ -67,7 +67,9 @@ class nsChannelToPipeListener : public nsIStreamListener
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
public:
|
||||
nsChannelToPipeListener(nsMediaDecoder* aDecoder);
|
||||
// If aSeeking is PR_TRUE then this listener was created as part of a
|
||||
// seek request and is expecting a byte range partial result.
|
||||
nsChannelToPipeListener(nsMediaDecoder* aDecoder, PRBool aSeeking = PR_FALSE);
|
||||
nsresult Init();
|
||||
nsresult GetInputStream(nsIInputStream** aStream);
|
||||
void Stop();
|
||||
@ -95,6 +97,9 @@ private:
|
||||
|
||||
// Total bytes transferred so far
|
||||
PRInt64 mTotalBytes;
|
||||
|
||||
// PR_TRUE if this listener is expecting a byte range request result
|
||||
PRPackedBool mSeeking;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -142,6 +142,12 @@ class nsMediaDecoder : public nsIObserver
|
||||
// Set the size of the video file in bytes.
|
||||
virtual void SetTotalBytes(PRInt64 aBytes) = 0;
|
||||
|
||||
// Set a flag indicating whether seeking is supported
|
||||
virtual void SetSeekable(PRBool aSeekable) = 0;
|
||||
|
||||
// Return PR_TRUE if seeking is supported.
|
||||
virtual PRBool GetSeekable() = 0;
|
||||
|
||||
// Called when the HTML DOM element is bound.
|
||||
virtual void ElementAvailable(nsHTMLMediaElement* anElement);
|
||||
|
||||
|
@ -338,6 +338,12 @@ class nsOggDecoder : public nsMediaDecoder
|
||||
// Get the size of the media file in bytes. Called on the main thread only.
|
||||
virtual void SetTotalBytes(PRInt64 aBytes);
|
||||
|
||||
// Set a flag indicating whether seeking is supported
|
||||
virtual void SetSeekable(PRBool aSeekable);
|
||||
|
||||
// Return PR_TRUE if seeking is supported.
|
||||
virtual PRBool GetSeekable();
|
||||
|
||||
protected:
|
||||
// Change to a new play state. This updates the mState variable and
|
||||
// notifies any thread blocking on this objects monitor of the
|
||||
@ -449,6 +455,10 @@ private:
|
||||
// True if we are registered with the observer service for shutdown.
|
||||
PRPackedBool mNotifyOnShutdown;
|
||||
|
||||
// True if the media resource is seekable (server supports byte range
|
||||
// requests).
|
||||
PRPackedBool mSeekable;
|
||||
|
||||
/******
|
||||
* The following member variables can be accessed from any thread.
|
||||
******/
|
||||
|
@ -42,11 +42,17 @@
|
||||
#include "nsChannelToPipeListener.h"
|
||||
#include "nsICachingChannel.h"
|
||||
|
||||
nsChannelToPipeListener::nsChannelToPipeListener(nsMediaDecoder* aDecoder) :
|
||||
#define HTTP_OK_CODE 200
|
||||
#define HTTP_PARTIAL_RESPONSE_CODE 206
|
||||
|
||||
nsChannelToPipeListener::nsChannelToPipeListener(
|
||||
nsMediaDecoder* aDecoder,
|
||||
PRBool aSeeking) :
|
||||
mDecoder(aDecoder),
|
||||
mIntervalStart(0),
|
||||
mIntervalEnd(0),
|
||||
mTotalBytes(0)
|
||||
mTotalBytes(0),
|
||||
mSeeking(aSeeking)
|
||||
{
|
||||
}
|
||||
|
||||
@ -98,10 +104,25 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
|
||||
if (hc) {
|
||||
PRUint32 responseStatus = 0;
|
||||
hc->GetResponseStatus(&responseStatus);
|
||||
if (responseStatus == 200) {
|
||||
if (mSeeking && responseStatus == HTTP_OK_CODE) {
|
||||
// If we get an OK response but we were seeking,
|
||||
// and therefore expecting a partial response of
|
||||
// HTTP_PARTIAL_RESPONSE_CODE, tell the decoder
|
||||
// we don't support seeking.
|
||||
mDecoder->SetSeekable(PR_FALSE);
|
||||
}
|
||||
else if (!mSeeking &&
|
||||
(responseStatus == HTTP_OK_CODE ||
|
||||
responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
|
||||
// We weren't seeking and got a valid response status,
|
||||
// set the length of the content.
|
||||
PRInt32 cl = 0;
|
||||
hc->GetContentLength(&cl);
|
||||
mDecoder->SetTotalBytes(cl);
|
||||
|
||||
// If we get an HTTP_OK_CODE response to our byte range
|
||||
// request, then we don't support seeking.
|
||||
mDecoder->SetSeekable(responseStatus == HTTP_PARTIAL_RESPONSE_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -425,6 +425,16 @@ nsresult nsHttpStreamStrategy::Open(nsIStreamListener **aStreamListener)
|
||||
*aStreamListener = mListener;
|
||||
NS_ADDREF(*aStreamListener);
|
||||
} else {
|
||||
// Use a byte range request from the start of the resource.
|
||||
// This enables us to detect if the stream supports byte range
|
||||
// requests, and therefore seeking, early.
|
||||
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
|
||||
if (hc) {
|
||||
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
|
||||
NS_LITERAL_CSTRING("bytes=0-"),
|
||||
PR_FALSE);
|
||||
}
|
||||
|
||||
rv = mChannel->AsyncOpen(mListener, nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
@ -525,7 +535,7 @@ public:
|
||||
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
|
||||
}
|
||||
|
||||
mListener = new nsChannelToPipeListener(mDecoder);
|
||||
mListener = new nsChannelToPipeListener(mDecoder, PR_TRUE);
|
||||
NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
mResult = mListener->Init();
|
||||
|
@ -35,6 +35,7 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#include <limits>
|
||||
#include "prlog.h"
|
||||
#include "prmem.h"
|
||||
#include "nsIFrame.h"
|
||||
@ -267,6 +268,18 @@ public:
|
||||
// monitor must be obtained before calling this.
|
||||
float GetCurrentTime();
|
||||
|
||||
// Called from the main thread to get the duration. The decoder monitor
|
||||
// must be obtained before calling this. It is in units of milliseconds.
|
||||
PRInt64 GetDuration();
|
||||
|
||||
// Called from the main thread to set the content length of the media
|
||||
// resource. The decoder monitor must be obtained before calling this.
|
||||
void SetContentLength(PRInt64 aLength);
|
||||
|
||||
// Called from the main thread to set whether the media resource can
|
||||
// be seeked. The decoder monitor must be obtained before calling this.
|
||||
void SetSeekable(PRBool aSeekable);
|
||||
|
||||
// Get and set the audio volume. The decoder monitor must be
|
||||
// obtained before calling this.
|
||||
float GetVolume();
|
||||
@ -427,6 +440,20 @@ private:
|
||||
// monitor.
|
||||
float mVolume;
|
||||
|
||||
// Duration of the media resource. It is accessed from the decoder and main
|
||||
// threads. Synchronised via decoder monitor. It is in units of
|
||||
// milliseconds.
|
||||
PRInt64 mDuration;
|
||||
|
||||
// Content Length of the media resource if known. If it is -1 then the
|
||||
// size is unknown. Accessed from the decoder and main threads. Synchronised
|
||||
// via decoder monitor.
|
||||
PRInt64 mContentLength;
|
||||
|
||||
// PR_TRUE if the media resource can be seeked. Accessed from the decoder
|
||||
// and main threads. Synchronised via decoder monitor.
|
||||
PRPackedBool mSeekable;
|
||||
|
||||
// PR_TRUE if an event to notify about a change in the playback
|
||||
// position has been queued, but not yet run. It is set to PR_FALSE when
|
||||
// the event is run. This allows coalescing of these events as they can be
|
||||
@ -455,6 +482,9 @@ nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder, nsChann
|
||||
mSeekTime(0.0),
|
||||
mCurrentFrameTime(0.0),
|
||||
mVolume(1.0),
|
||||
mDuration(-1),
|
||||
mContentLength(-1),
|
||||
mSeekable(PR_TRUE),
|
||||
mPositionChangeQueued(PR_FALSE)
|
||||
{
|
||||
}
|
||||
@ -773,6 +803,23 @@ float nsOggDecodeStateMachine::GetCurrentTime()
|
||||
return mCurrentFrameTime;
|
||||
}
|
||||
|
||||
PRInt64 nsOggDecodeStateMachine::GetDuration()
|
||||
{
|
||||
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "GetDuration() called without acquiring decoder monitor");
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::SetContentLength(PRInt64 aLength)
|
||||
{
|
||||
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "SetContentLength() called without acquiring decoder monitor");
|
||||
mContentLength = aLength;
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::SetSeekable(PRBool aSeekable)
|
||||
{
|
||||
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "SetSeekable() called without acquiring decoder monitor");
|
||||
mSeekable = aSeekable;
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::Shutdown()
|
||||
{
|
||||
@ -1044,12 +1091,12 @@ void nsOggDecodeStateMachine::LoadOggHeaders()
|
||||
oggplay_get_audio_channels(mPlayer, i, &mAudioChannels);
|
||||
LOG(PR_LOG_DEBUG, ("samplerate: %d, channels: %d", mAudioRate, mAudioChannels));
|
||||
}
|
||||
|
||||
|
||||
if (oggplay_set_track_active(mPlayer, i) < 0) {
|
||||
LOG(PR_LOG_ERROR, ("Could not set track %d active", i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mVideoTrack == -1) {
|
||||
oggplay_set_callback_num_frames(mPlayer, mAudioTrack, OGGPLAY_FRAMES_PER_CALLBACK);
|
||||
mCallbackPeriod = 1.0 / (float(mAudioRate) / OGGPLAY_FRAMES_PER_CALLBACK);
|
||||
@ -1058,6 +1105,27 @@ void nsOggDecodeStateMachine::LoadOggHeaders()
|
||||
|
||||
oggplay_use_buffer(mPlayer, OGGPLAY_BUFFER_SIZE);
|
||||
|
||||
// Get the duration from the Ogg file. We only do this if the
|
||||
// content length of the resource is known as we need to seek
|
||||
// to the end of the file to get the last time field. We also
|
||||
// only do this if the resource is seekable.
|
||||
{
|
||||
nsAutoMonitor mon(mDecoder->GetMonitor());
|
||||
if (mState != DECODER_STATE_SHUTDOWN &&
|
||||
mContentLength >= 0 &&
|
||||
mSeekable) {
|
||||
// Don't hold the monitor during the duration
|
||||
// call as it can issue seek requests
|
||||
// and blocks until these are completed.
|
||||
mon.Exit();
|
||||
PRInt64 d = oggplay_get_duration(mPlayer);
|
||||
mon.Enter();
|
||||
mDuration = d;
|
||||
}
|
||||
if (mState == DECODER_STATE_SHUTDOWN)
|
||||
return;
|
||||
}
|
||||
|
||||
// Inform the element that we've loaded the Ogg metadata
|
||||
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
|
||||
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, MetadataLoaded);
|
||||
@ -1097,10 +1165,13 @@ void nsOggDecoder::SetVolume(float volume)
|
||||
|
||||
float nsOggDecoder::GetDuration()
|
||||
{
|
||||
// Currently not implemented. Video Spec says to return
|
||||
// NaN if unknown.
|
||||
// TODO: return NaN
|
||||
return 0.0;
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
PRInt64 d = mDecodeStateMachine ? mDecodeStateMachine->GetDuration() : -1;
|
||||
if (d >= 0) {
|
||||
return static_cast<float>(d) / 1000.0;
|
||||
}
|
||||
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
|
||||
nsOggDecoder::nsOggDecoder() :
|
||||
@ -1109,8 +1180,9 @@ nsOggDecoder::nsOggDecoder() :
|
||||
mCurrentTime(0.0),
|
||||
mInitialVolume(0.0),
|
||||
mRequestedSeekTime(-1.0),
|
||||
mContentLength(0),
|
||||
mContentLength(-1),
|
||||
mNotifyOnShutdown(PR_FALSE),
|
||||
mSeekable(PR_TRUE),
|
||||
mReader(0),
|
||||
mMonitor(0),
|
||||
mPlayState(PLAY_STATE_PAUSED),
|
||||
@ -1201,6 +1273,11 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mDecodeStateMachine = new nsOggDecodeStateMachine(this, mReader);
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mDecodeStateMachine->SetContentLength(mContentLength);
|
||||
mDecodeStateMachine->SetSeekable(mSeekable);
|
||||
}
|
||||
|
||||
ChangeState(PLAY_STATE_LOADING);
|
||||
|
||||
@ -1373,6 +1450,10 @@ PRInt64 nsOggDecoder::GetTotalBytes()
|
||||
void nsOggDecoder::SetTotalBytes(PRInt64 aBytes)
|
||||
{
|
||||
mContentLength = aBytes;
|
||||
if (mDecodeStateMachine) {
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mDecodeStateMachine->SetContentLength(aBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecoder::UpdateBytesDownloaded(PRUint64 aBytes)
|
||||
@ -1540,3 +1621,18 @@ void nsOggDecoder::PlaybackPositionChanged()
|
||||
mElement->DispatchSimpleEvent(NS_LITERAL_STRING("timeupdate"));
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecoder::SetSeekable(PRBool aSeekable)
|
||||
{
|
||||
mSeekable = aSeekable;
|
||||
if (mDecodeStateMachine) {
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mDecodeStateMachine->SetSeekable(aSeekable);
|
||||
}
|
||||
}
|
||||
|
||||
PRBool nsOggDecoder::GetSeekable()
|
||||
{
|
||||
return mSeekable;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ _TEST_FILES = test_autoplay.html \
|
||||
test_controls.html \
|
||||
test_currentTime.html \
|
||||
test_defaultPlaybackRate.html \
|
||||
test_duration1.html \
|
||||
test_ended1.html \
|
||||
test_ended2.html \
|
||||
test_networkState.html \
|
||||
|
39
content/media/video/test/test_duration1.html
Normal file
39
content/media/video/test/test_duration1.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Media test: seek test 1</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id='v'
|
||||
src='seek.ogg'
|
||||
onloadedmetadata='return startTest();'></video>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
// Test that getting the duration from a file works
|
||||
var completed = false;
|
||||
var timeout;
|
||||
|
||||
function startTest() {
|
||||
if (completed)
|
||||
return false;
|
||||
var v = document.getElementById('v');
|
||||
ok(Math.round(v.duration*1000) == 3833, "Check duration of video: " + v.duration);
|
||||
completed = true;
|
||||
clearTimeout(timeout);
|
||||
SimpleTest.finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
timeout = setTimeout(function () {
|
||||
ok(false, "Test timed out");
|
||||
SimpleTest.finish();
|
||||
}, 60000);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user