Skip to content
Permalink
Browse files
Add support for HTMLMediaElement.fastSeek()
https://bugs.webkit.org/show_bug.cgi?id=124262

Reviewed by Eric Carlson.

Source/WebCore:

Test: media/video-fast-seek.html

Add the fastSeek() method to HTMLMediaElement, and use fastSeek() in
the JavaScript media controls.

Add the new fastSeek() method:
* html/HTMLMediaElement.cpp:
(HTMLMediaElement::fastSeek): Call seekWithTolerance.
(HTMLMediaElement::seek): Call seekWithTolerance with 0 tolerance.
(HTMLMediaElement::seekWithTolerance): Renamed from seek().
* html/HTMLMediaElement.h:
* html/HTMLMediaElement.idl:

Add seekWithTolerance() to MediaPlayer:
* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::seekWithTolerance): Pass to MediaPlayerPrivate.
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::seekWithTolerance): Default implementation which
    calls seek().
* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:
(WebCore::MediaPlayerPrivateAVFoundation::seek): Call seekWithTolerance with 0 tolerance.
(WebCore::MediaPlayerPrivateAVFoundation::seekWithTolerance): Renamed from seek().
* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::seekToTime): Take tolerance parameters.

Use the new fastSeek() method while actively scrubbing.
* Modules/mediacontrols/mediaControlsApple.js:
(Controller.prototype.createControls): Add mouse up and down handlers.
(Controller.prototype.handleTimeUpdate): Only update the timeline when not scrubbing.
(Controller.prototype.handleTimelineChange): Use fastSeek().
(Controller.prototype.handleTimelineMouseDown): Start scrubbing.
(Controller.prototype.handleTimelineMouseUp): Stop scrubbing.

LayoutTests:

* media/video-fast-seek-expected.txt: Added.
* media/video-fast-seek.html: Added.

Canonical link: https://commits.webkit.org/142492@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@159208 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
jernoble committed Nov 13, 2013
1 parent 4f2dec9 commit 18d16c96e5e1d719237868cfc792450191298ccb
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 8 deletions.
@@ -1,3 +1,13 @@
2013-11-12 Jer Noble <jer.noble@apple.com>

Add support for HTMLMediaElement.fastSeek()
https://bugs.webkit.org/show_bug.cgi?id=124262

Reviewed by Eric Carlson.

* media/video-fast-seek-expected.txt: Added.
* media/video-fast-seek.html: Added.

2013-11-13 Antti Koivisto <antti@apple.com>

* TestExpectations: Skip crypto/subtle/rsassa-pkcs1-v1_5-import-jwk.html
@@ -0,0 +1,22 @@

Test that fastSeek() commands work correctly.

EVENT(canplaythrough)
Seek past the 4th sync sample:
RUN(video.currentTime = 2.5)
EVENT(timeupdate)
EXPECTED (video.currentTime == '2.5') OK
Test that fastSeek() past the currentTime will not result in a seek before the currentTime:
RUN(video.fastSeek(2.6))
EVENT(timeupdate)
EXPECTED (video.currentTime >= '2.6') OK
Seek before the 4th sync sample:
RUN(video.currentTime = 2.3)
EVENT(timeupdate)
EXPECTED (video.currentTime == '2.3') OK
Test that fastSeek() before the currentTime will not result in a seek past the currentTime:
RUN(video.fastSeek(2.2))
EVENT(timeupdate)
EXPECTED (video.currentTime <= '2.2') OK
END OF TEST

@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<script src=media-file.js></script>
<script src=video-test.js></script>
<script>

// The test.mp4 file has sync samples at the following presentation time stamps:
// 0.0000, 0.7968, 1.5936, 2.3904, 3.1872, 3.9840, 4.7808, 5.5776

function runTest()
{
findMediaElement();
waitForEvent('canplaythrough', canplaythrough);

// Other media files may have sync samples at completely different points, so
// explicitly use the .mp4 here.
video.src = "content/test.mp4";
}

function canplaythrough()
{
waitForEventOnce('timeupdate', seek1);
consoleWrite('Seek past the 4th sync sample:');
run('video.currentTime = 2.5');
}

function seek1()
{
testExpected('video.currentTime', 2.5);
consoleWrite('Test that fastSeek() past the currentTime will not result in a seek before the currentTime:');
waitForEventOnce('timeupdate', seek2);
run('video.fastSeek(2.6)');
}

function seek2()
{
testExpected('video.currentTime', 2.6, '>=');
consoleWrite('Seek before the 4th sync sample:');
waitForEventOnce('timeupdate', seek3);
run('video.currentTime = 2.3');
}

function seek3()
{
testExpected('video.currentTime', 2.3);
consoleWrite('Test that fastSeek() before the currentTime will not result in a seek past the currentTime:');
waitForEventOnce('timeupdate', seek4);
run('video.fastSeek(2.2)');
}

function seek4()
{
testExpected('video.currentTime', 2.2, '<=');
endTest();
}

</script>
</head>
<body onload="runTest()">
<video controls></video>
<p>Test that fastSeek() commands work correctly.</p>
</body>
</html>
@@ -1,3 +1,46 @@
2013-11-12 Jer Noble <jer.noble@apple.com>

Add support for HTMLMediaElement.fastSeek()
https://bugs.webkit.org/show_bug.cgi?id=124262

Reviewed by Eric Carlson.

Test: media/video-fast-seek.html

Add the fastSeek() method to HTMLMediaElement, and use fastSeek() in
the JavaScript media controls.

Add the new fastSeek() method:
* html/HTMLMediaElement.cpp:
(HTMLMediaElement::fastSeek): Call seekWithTolerance.
(HTMLMediaElement::seek): Call seekWithTolerance with 0 tolerance.
(HTMLMediaElement::seekWithTolerance): Renamed from seek().
* html/HTMLMediaElement.h:
* html/HTMLMediaElement.idl:

Add seekWithTolerance() to MediaPlayer:
* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::seekWithTolerance): Pass to MediaPlayerPrivate.
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::seekWithTolerance): Default implementation which
calls seek().
* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:
(WebCore::MediaPlayerPrivateAVFoundation::seek): Call seekWithTolerance with 0 tolerance.
(WebCore::MediaPlayerPrivateAVFoundation::seekWithTolerance): Renamed from seek().
* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::seekToTime): Take tolerance parameters.

Use the new fastSeek() method while actively scrubbing.
* Modules/mediacontrols/mediaControlsApple.js:
(Controller.prototype.createControls): Add mouse up and down handlers.
(Controller.prototype.handleTimeUpdate): Only update the timeline when not scrubbing.
(Controller.prototype.handleTimelineChange): Use fastSeek().
(Controller.prototype.handleTimelineMouseDown): Start scrubbing.
(Controller.prototype.handleTimelineMouseUp): Stop scrubbing.

2013-11-13 Andreas Kling <akling@apple.com>

Generate casting helpers for scrolling tree classes.
@@ -295,6 +295,8 @@ Controller.prototype = {
this.listenFor(timeline, 'mouseover', this.handleTimelineMouseOver);
this.listenFor(timeline, 'mouseout', this.handleTimelineMouseOut);
this.listenFor(timeline, 'mousemove', this.handleTimelineMouseMove);
this.listenFor(timeline, 'mousedown', this.handleTimelineMouseDown);
this.listenFor(timeline, 'mouseup', this.handleTimelineMouseUp);
timeline.step = .01;

var thumbnailTrack = this.controls.thumbnailTrack = document.createElement('div');
@@ -463,7 +465,8 @@ Controller.prototype = {

handleTimeUpdate: function(event)
{
this.updateTime();
if (!this.scrubbing)
this.updateTime();
},

handleDurationChange: function(event)
@@ -622,7 +625,7 @@ Controller.prototype = {

handleTimelineChange: function(event)
{
this.video.currentTime = this.controls.timeline.value;
this.video.fastSeek(this.controls.timeline.value);
},

handleTimelineDown: function(event)
@@ -675,6 +678,19 @@ Controller.prototype = {
}
},

handleTimelineMouseDown: function(event)
{
this.scrubbing = true;
},

handleTimelineMouseUp: function(event)
{
this.scrubbing = false;

// Do a precise seek when we lift the mouse:
this.video.currentTime = this.controls.timeline.value;
},

handleMuteButtonClicked: function(event)
{
this.video.muted = !this.video.muted;
@@ -2146,10 +2146,31 @@ void HTMLMediaElement::prepareToPlay()
m_player->prepareToPlay();
}

void HTMLMediaElement::fastSeek(double time, ExceptionCode& ec)
{
LOG(Media, "HTMLMediaElement::fastSeek(%f)", time);
// 4.7.10.9 Seeking
// 9. If the approximate-for-speed flag is set, adjust the new playback position to a value that will
// allow for playback to resume promptly. If new playback position before this step is before current
// playback position, then the adjusted new playback position must also be before the current playback
// position. Similarly, if the new playback position before this step is after current playback position,
// then the adjusted new playback position must also be after the current playback position.
refreshCachedTime();
double delta = time - currentTime();
double negativeTolerance = delta >= 0 ? delta : std::numeric_limits<double>::infinity();
double positiveTolerance = delta < 0 ? -delta : std::numeric_limits<double>::infinity();

seekWithTolerance(time, negativeTolerance, positiveTolerance, ec);
}

void HTMLMediaElement::seek(double time, ExceptionCode& ec)
{
LOG(Media, "HTMLMediaElement::seek(%f)", time);
seekWithTolerance(time, 0, 0, ec);
}

void HTMLMediaElement::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance, ExceptionCode& ec)
{
// 4.8.9.9 Seeking

// 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception.
@@ -2232,7 +2253,7 @@ void HTMLMediaElement::seek(double time, ExceptionCode& ec)
m_sentEndEvent = false;

// 8 - Set the current playback position to the given new playback position
m_player->seek(time);
m_player->seekWithTolerance(time, negativeTolerance, positiveTolerance);

// 9 - Queue a task to fire a simple event named seeking at the element.
scheduleEvent(eventNames().seekingEvent);
@@ -178,6 +178,7 @@ class HTMLMediaElement : public HTMLElement, private MediaPlayerClient, public M
void setLoop(bool b);
virtual void play() OVERRIDE;
virtual void pause() OVERRIDE;
void fastSeek(double, ExceptionCode&);

// captions
bool webkitHasClosedCaptions() const;
@@ -536,6 +537,7 @@ class HTMLMediaElement : public HTMLElement, private MediaPlayerClient, public M
void stopPeriodicTimers();

void seek(double time, ExceptionCode&);
void seekWithTolerance(double time, double negativeTolerance, double positiveTolerance, ExceptionCode&);
void finishSeek();
void checkIfSeekNeeded();
void addPlayedRange(double start, double end);
@@ -75,6 +75,7 @@ readonly attribute boolean ended;
[Reflect] attribute boolean loop;
void play();
void pause();
[RaisesException] void fastSeek(double time);

// controls
attribute boolean controls;
@@ -538,6 +538,11 @@ double MediaPlayer::currentTime() const
return m_private->currentTimeDouble();
}

void MediaPlayer::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance)
{
m_private->seekWithTolerance(time, negativeTolerance, positiveTolerance);
}

void MediaPlayer::seek(double time)
{
m_private->seekDouble(time);
@@ -326,6 +326,7 @@ class MediaPlayer {
double duration() const;
double currentTime() const;
void seek(double time);
void seekWithTolerance(double time, double negativeTolerance, double positiveTolerance);

double startTime() const;

@@ -79,6 +79,7 @@ class MediaPlayerPrivateInterface {

virtual void seek(float) { }
virtual void seekDouble(double time) { seek(time); }
virtual void seekWithTolerance(double time, double, double) { seek(time); }

virtual bool seeking() const = 0;

@@ -265,6 +265,11 @@ float MediaPlayerPrivateAVFoundation::duration() const
}

void MediaPlayerPrivateAVFoundation::seek(float time)
{
seekWithTolerance(time, 0, 0);
}

void MediaPlayerPrivateAVFoundation::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance)
{
if (!metaDataAvailable())
return;
@@ -282,7 +287,7 @@ void MediaPlayerPrivateAVFoundation::seek(float time)
m_seekTo = time;

++m_seekCount;
seekToTime(time);
seekToTime(time, negativeTolerance, positiveTolerance);
}

void MediaPlayerPrivateAVFoundation::setRate(float rate)
@@ -159,6 +159,7 @@ class MediaPlayerPrivateAVFoundation : public MediaPlayerPrivateInterface, publi
virtual float duration() const OVERRIDE;
virtual float currentTime() const = 0;
virtual void seek(float) OVERRIDE;
virtual void seekWithTolerance(double, double, double) OVERRIDE;
virtual bool seeking() const OVERRIDE;
virtual void setRate(float) OVERRIDE;
virtual bool paused() const OVERRIDE;
@@ -223,7 +224,7 @@ class MediaPlayerPrivateAVFoundation : public MediaPlayerPrivateInterface, publi
virtual void checkPlayability() = 0;
virtual void updateRate() = 0;
virtual float rate() const = 0;
virtual void seekToTime(double time) = 0;
virtual void seekToTime(double time, double negativeTolerance, double positiveTolerance) = 0;
virtual unsigned totalBytes() const = 0;
virtual PassRefPtr<TimeRanges> platformBufferedTimeRanges() const = 0;
virtual double platformMaxTimeSeekable() const = 0;
@@ -142,7 +142,7 @@ class MediaPlayerPrivateAVFoundationObjC : public MediaPlayerPrivateAVFoundation
virtual void checkPlayability();
virtual void updateRate();
virtual float rate() const;
virtual void seekToTime(double time);
virtual void seekToTime(double time, double negativeTolerance, double positiveTolerance);
virtual unsigned totalBytes() const;
virtual PassRefPtr<TimeRanges> platformBufferedTimeRanges() const;
virtual double platformMinTimeSeekable() const;
@@ -644,13 +644,16 @@ static dispatch_queue_t globalLoaderDelegateQueue()
return 0;
}

void MediaPlayerPrivateAVFoundationObjC::seekToTime(double time)
void MediaPlayerPrivateAVFoundationObjC::seekToTime(double time, double negativeTolerance, double positiveTolerance)
{
// setCurrentTime generates several event callbacks, update afterwards.
setDelayCallbacks(true);

WebCoreAVFMovieObserver *observer = m_objcObserver.get();
[m_avPlayerItem.get() seekToTime:CMTimeMakeWithSeconds(time, 600) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
CMTime cmTime = CMTimeMakeWithSeconds(time, 600);
CMTime cmBefore = CMTimeMakeWithSeconds(negativeTolerance, 600);
CMTime cmAfter = CMTimeMakeWithSeconds(positiveTolerance, 600);
[m_avPlayerItem.get() seekToTime:cmTime toleranceBefore:cmBefore toleranceAfter:cmAfter completionHandler:^(BOOL finished) {
[observer seekCompleted:finished];
}];

0 comments on commit 18d16c9

Please sign in to comment.