Changeset 27866 in webkit


Ignore:
Timestamp:
Nov 16, 2007, 6:10:16 PM (18 years ago)
Author:
Antti Koivisto
Message:

WebCore:

Reviewed by Adele.


Seeking related fixes, updates match to the latest specification

  • rename loopCount of HTMLMediaElement to playCount
  • add explicit seeking attribute to HTMLMediaElement to get semantics right
  • implement the specification behavior that currentTime must immediately return seeked position in HTMLMediaElement instead of MoviePrivateQTKit
  • fix broken behavior when seeking past end of the media, add tests
  • replace Movie didEnd callback with broader timeChanged callback (which gets called in didEnd case too)
  • use setDelayCallbacks: in various MoviePrivateQTKit methods to avoid bug prone synchronous callbacks from QT, make HTMLMediaElement not depend on synchronous callbacks
  • do some cleanups and simplifications in MoviePrivateQTKit, get rid of m_rateBeforeSeek and m_blockStateUpdate variables

Tests: http/tests/media/video-seekable-stall.html

media/video-seeking.html
media/video-seek-past-end-paused.html
media/video-seek-past-end-playing.html

  • html/HTMLAttributeNames.in:
  • html/HTMLMediaElement.cpp: (WebCore::HTMLMediaElement::HTMLMediaElement): (WebCore::HTMLMediaElement::load): (WebCore::HTMLMediaElement::movieNetworkStateChanged): (WebCore::HTMLMediaElement::setReadyState): (WebCore::HTMLMediaElement::seek): (WebCore::HTMLMediaElement::seeking): (WebCore::HTMLMediaElement::currentTime): (WebCore::HTMLMediaElement::ended): (WebCore::HTMLMediaElement::play): (WebCore::HTMLMediaElement::pause): (WebCore::HTMLMediaElement::playCount): (WebCore::HTMLMediaElement::setPlayCount): (WebCore::HTMLMediaElement::checkIfSeekNeeded): (WebCore::HTMLMediaElement::movieTimeChanged): (WebCore::HTMLMediaElement::endedPlayback): (WebCore::HTMLMediaElement::updateMovie):
  • html/HTMLMediaElement.h:
  • html/HTMLMediaElement.idl:
  • platform/graphics/Movie.cpp: (WebCore::Movie::timeChanged):
  • platform/graphics/Movie.h: (WebCore::MovieClient::movieTimeChanged):
  • platform/graphics/mac/MoviePrivateQTKit.h:
  • platform/graphics/mac/MoviePrivateQTKit.mm: (WebCore::MoviePrivate::MoviePrivate): (WebCore::MoviePrivate::load): (WebCore::MoviePrivate::play): (WebCore::MoviePrivate::pause): (WebCore::MoviePrivate::currentTime): (WebCore::MoviePrivate::seek): (WebCore::MoviePrivate::doSeek): (WebCore::MoviePrivate::cancelSeek): (WebCore::MoviePrivate::seekTimerFired): (WebCore::MoviePrivate::startCuePointTimerIfNeeded): (WebCore::MoviePrivate::paused): (WebCore::MoviePrivate::updateStates): (WebCore::MoviePrivate::timeChanged): (WebCore::MoviePrivate::didEnd):

LayoutTests:

Reviewed by Adele.


Seeking related test updates

  • update to match specificiation
    • rename loopCount to playCount everywhere
    • timeupdate events during seek
  • fix that some tests (like audio ones) depended on timing sensitive ordering of play and load events
  • add text about what is being tested


Add new tests for seeking past end, seeking past loaded position and 'seeking' DOM attribute.

  • http/tests/media/video-seekable-stall-expected.txt: Added.
  • http/tests/media/video-seekable-stall.html: Added.
  • media/audio-constructor-expected.txt:
  • media/audio-constructor-src-expected.txt:
  • media/audio-constructor-src.html:
  • media/audio-constructor.html:
  • media/video-dom-loopcount-expected.txt:
  • media/video-dom-loopcount.html:
  • media/video-dom-loopend-expected.txt:
  • media/video-dom-loopend.html:
  • media/video-dom-loopstart-expected.txt:
  • media/video-dom-loopstart.html:
  • media/video-dom-start-expected.txt:
  • media/video-dom-start.html:
  • media/video-loopcount-expected.txt:
  • media/video-loopcount.html:
  • media/video-loopend-expected.txt:
  • media/video-loopend.html:
  • media/video-loopstart-expected.txt:
  • media/video-loopstart.html:
  • media/video-seek-past-end-paused-expected.txt: Added.
  • media/video-seek-past-end-paused.html: Added.
  • media/video-seek-past-end-playing-expected.txt: Added.
  • media/video-seek-past-end-playing.html: Added.
  • media/video-seeking-expected.txt: Added.
  • media/video-seeking.html: Added.
  • media/video-test.js:
Location:
trunk
Files:
8 added
29 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r27865 r27866  
     12007-11-16  Antti Koivisto  <antti@apple.com>
     2
     3        Reviewed by Adele.
     4       
     5        Seeking related test updates
     6        - update to match specificiation
     7            - rename loopCount to playCount everywhere
     8            - timeupdate events during seek
     9        - fix that some tests (like audio ones) depended on timing sensitive ordering of play and load events
     10        - add text about what is being tested
     11       
     12        Add new tests for seeking past end, seeking past loaded position and 'seeking' DOM attribute.
     13
     14        * http/tests/media/video-seekable-stall-expected.txt: Added.
     15        * http/tests/media/video-seekable-stall.html: Added.
     16        * media/audio-constructor-expected.txt:
     17        * media/audio-constructor-src-expected.txt:
     18        * media/audio-constructor-src.html:
     19        * media/audio-constructor.html:
     20        * media/video-dom-loopcount-expected.txt:
     21        * media/video-dom-loopcount.html:
     22        * media/video-dom-loopend-expected.txt:
     23        * media/video-dom-loopend.html:
     24        * media/video-dom-loopstart-expected.txt:
     25        * media/video-dom-loopstart.html:
     26        * media/video-dom-start-expected.txt:
     27        * media/video-dom-start.html:
     28        * media/video-loopcount-expected.txt:
     29        * media/video-loopcount.html:
     30        * media/video-loopend-expected.txt:
     31        * media/video-loopend.html:
     32        * media/video-loopstart-expected.txt:
     33        * media/video-loopstart.html:
     34        * media/video-seek-past-end-paused-expected.txt: Added.
     35        * media/video-seek-past-end-paused.html: Added.
     36        * media/video-seek-past-end-playing-expected.txt: Added.
     37        * media/video-seek-past-end-playing.html: Added.
     38        * media/video-seeking-expected.txt: Added.
     39        * media/video-seeking.html: Added.
     40        * media/video-test.js:
     41
    1422007-11-16  Anders Carlsson  <andersca@apple.com>
    243
  • trunk/LayoutTests/media/audio-constructor-expected.txt

    r27286 r27866  
     1Test that Audio() object loads the resource after src attribute is set and load() is called.
     2
    13TEST(audio instanceof HTMLAudioElement) OK
    2 EVENT(load) TEST(relativeURL(audio.currentSrc)=='content/test.wav') OK
    3 EVENT(play)
     4RUN(audio.load())
     5EVENT(begin) TEST(relativeURL(audio.currentSrc)=='content/test.wav') OK
     6EVENT(load)
    47END OF TEST
    58
  • trunk/LayoutTests/media/audio-constructor-src-expected.txt

    r27286 r27866  
     1Test that Audio("url") constructor loads the specified resource.
     2
    13TEST(audio instanceof HTMLAudioElement) OK
    2 EVENT(load) TEST(relativeURL(audio.currentSrc)=='content/test.wav') OK
    3 EVENT(play)
     4EVENT(begin) TEST(relativeURL(audio.currentSrc)=='content/test.wav') OK
     5EVENT(load)
    46END OF TEST
    57
  • trunk/LayoutTests/media/audio-constructor-src.html

    r27286 r27866  
    11<body>
     2<p>Test that Audio("url") constructor loads the specified resource.</p>
    23<script src=video-test.js></script>
    34<script>
    45var audio = new Audio("content/test.wav");
    56media = audio;
    6 
    77test("audio instanceof HTMLAudioElement");
    8 waitForEventAndTest("load", "relativeURL(audio.currentSrc)=='content/test.wav'");
    9 waitForEventAndEnd("play");
    10 audio.play();
     8waitForEventAndTest("begin", "relativeURL(audio.currentSrc)=='content/test.wav'");
     9waitForEventAndEnd("load");
    1110</script>
  • trunk/LayoutTests/media/audio-constructor.html

    r27286 r27866  
    11<body>
     2<p>Test that Audio() object loads the resource after src attribute is set and load() is called.</p>
    23<script src=video-test.js></script>
    34<script>
    45var audio = new Audio();
    56media = audio;
    6 
    77test("audio instanceof HTMLAudioElement");
    8 waitForEventAndTest("load", "relativeURL(audio.currentSrc)=='content/test.wav'");
    9 waitForEventAndEnd("play");
     8waitForEventAndTest("begin", "relativeURL(audio.currentSrc)=='content/test.wav'");
     9waitForEventAndEnd("load");
    1010audio.src = "content/test.wav";
    11 audio.play();
     11run("audio.load()");
    1212</script>
  • trunk/LayoutTests/media/video-dom-loopcount-expected.txt

    r27286 r27866  
    1 TEST(video.loopCount == 2) OK
     1Test that playcount DOM attribute causes playback to end after right number of repeats.
     2
     3TEST(video.playCount == 3) OK
    24TEST(video.currentLoop == 0) OK
    3 TEST(video.getAttribute('loopCount') == 2) OK
    4 EVENT(ended) TEST(video.currentLoop == 1) OK
     5TEST(video.getAttribute('playCount') == 3) OK
     6RUN(video.play())
     7EVENT(ended) TEST(video.currentLoop == 2) OK
    58END OF TEST
    69
  • trunk/LayoutTests/media/video-dom-loopcount.html

    r27286 r27866  
    1 <video src=content/test.mp4 loopend=0.2s end=0.2s autoplay></video>
     1<video src=content/test.mp4 loopend=0.2s end=0.2s></video>
     2<p>Test that playcount DOM attribute causes playback to end after right number of repeats.</p>
    23<script src=video-test.js></script>
    34<script>
    4 video.loopCount = 2;
    5 test("video.loopCount == 2");
     5video.playCount = 3;
     6test("video.playCount == 3");
    67test("video.currentLoop == 0");
    7 test("video.getAttribute('loopCount') == 2");
    8 waitForEventTestAndEnd('ended', "video.currentLoop == 1");
     8test("video.getAttribute('playCount') == 3");
     9waitForEventTestAndEnd('ended', "video.currentLoop == 2");
     10run("video.play()");
    911</script>
  • trunk/LayoutTests/media/video-dom-loopend-expected.txt

    r27286 r27866  
     1Test that video loops when time reaches the value specified by the loopEnd DOM attribute.
     2
    13TEST(video.loopEnd==0.5) OK
    24TEST(video.getAttribute('loopend') == '0.5s') OK
    3 EVENT(timeupdate) TEST(video.currentTime.toFixed(1) == 0 && video.currentLoop==1) OK
     5RUN(video.play())
     6EVENT(timeupdate)
     7EVENT(timeupdate)
     8TEST(video.currentLoop == 1) OK
     9TEST(video.currentTime.toFixed(1) == 0) OK
    410END OF TEST
    511
  • trunk/LayoutTests/media/video-dom-loopend.html

    r27286 r27866  
    1 <video src=content/test.mp4 loopcount=2 autoplay></video>
     1<video src=content/test.mp4 playcount=2></video>
     2<p>Test that video loops when time reaches the value specified by the loopEnd DOM attribute.</p>
    23<script src=video-test.js></script>
    34<script>
     
    67test("video.getAttribute('loopend') == '0.5s'");
    78setTimeout(function () { test("video.currentTime<=0.5"); }, 2000);
    8 waitForEventTestAndEnd('timeupdate', "video.currentTime.toFixed(1) == 0 && video.currentLoop==1");
     9waitForEvent('timeupdate', function () {
     10    if (video.currentLoop == 1) {
     11        test("video.currentLoop == 1");
     12        test("video.currentTime.toFixed(1) == 0");
     13        endTest();
     14    }
     15});
     16run("video.play()");
    917</script>
  • trunk/LayoutTests/media/video-dom-loopstart-expected.txt

    r27286 r27866  
     1Test that playing video rewinds to time specified by the loopStart DOM attribute when it loops.
     2
    13TEST(video.loopStart==1.0) OK
    24TEST(video.getAttribute('loopstart') == '1s') OK
    3 EVENT(play)
    4 EVENT(timeupdate) TEST(video.currentTime.toFixed(1) && video.currentLoop==1) OK
     5RUN(video.play())
     6EVENT(load)
     7EVENT(timeupdate)
     8EVENT(timeupdate)
     9EVENT(timeupdate)
     10TEST(video.currentLoop == 1) OK
     11TEST(video.currentTime.toFixed(1) == 1.0) OK
    512END OF TEST
    613
  • trunk/LayoutTests/media/video-dom-loopstart.html

    r27758 r27866  
    1 <video src=content/test.mp4 loopcount=2></video>
     1<video playcount=2></video>
     2<p>Test that playing video rewinds to time specified by the loopStart DOM attribute when it loops.</p>
    23<script src=video-test.js></script>
    34<script>
     
    56test("video.loopStart==1.0");
    67test("video.getAttribute('loopstart') == '1s'");
    7 waitForEvent('play', function () { video.currentTime = 500; });
    8 waitForEventTestAndEnd('timeupdate', "video.currentTime.toFixed(1) && video.currentLoop==1");
    9 video.play();
     8waitForEvent('load', function () {
     9    waitForEvent('timeupdate', function () {
     10        failTestIn(1000);
     11        if (video.currentLoop == 1) {
     12            test("video.currentLoop == 1");
     13            test("video.currentTime.toFixed(1) == 1.0");
     14            endTest();
     15        }
     16    });
     17    video.currentTime = video.duration - 0.2;
     18});
     19video.src = "content/test.mp4";
     20run("video.play()");
    1021</script>
  • trunk/LayoutTests/media/video-dom-start-expected.txt

    r27286 r27866  
     1Test that when start DOM attribute is set, playback starts from that time.
     2
    13TEST(video.start == 1.0) OK
    24TEST(video.getAttribute('start') == '1s') OK
     5RUN(video.load())
    36EVENT(canplaythrough) TEST(video.currentTime == 1.0) OK
    47END OF TEST
  • trunk/LayoutTests/media/video-dom-start.html

    r27286 r27866  
    1 <video src=content/test.mp4></video>
     1<video></video>
     2<p>Test that when start DOM attribute is set, playback starts from that time.</p>
    23<script src=video-test.js></script>
    34<script>
     
    67test("video.getAttribute('start') == '1s'");
    78waitForEventTestAndEnd('canplaythrough', "video.currentTime == 1.0");
     9video.src = "content/test.mp4";
     10run("video.load()");
    811</script>
  • trunk/LayoutTests/media/video-loopcount-expected.txt

    r27286 r27866  
    1 TEST(video.loopCount == 2) OK
     1Test that playcount attribute causes playback to end after right number of repeats.
     2
     3TEST(video.playCount == 3) OK
    24TEST(video.currentLoop == 0) OK
    3 EVENT(ended) TEST(video.currentLoop == 1) OK
     5EVENT(ended) TEST(video.currentLoop == 2) OK
    46END OF TEST
    57
  • trunk/LayoutTests/media/video-loopcount.html

    r27758 r27866  
    1 <video src=content/test.mp4 loopcount=2 loopend=0.2s end=0.2s></video>
     1<video src=content/test.mp4 playcount=3 loopend=0.2s end=0.2s></video>
     2<p>Test that playcount attribute causes playback to end after right number of repeats.</p>
    23<script src=video-test.js></script>
    34<script>
    4 test("video.loopCount == 2");
     5test("video.playCount == 3");
    56test("video.currentLoop == 0");
    6 waitForEventTestAndEnd('ended', "video.currentLoop == 1");
     7waitForEventTestAndEnd('ended', "video.currentLoop == 2");
    78video.play();
    89</script>
  • trunk/LayoutTests/media/video-loopend-expected.txt

    r27286 r27866  
     1Test that video loops when time reaches the value specified by the loopEnd attribute.
     2
    13TEST(video.loopEnd==0.5) OK
    2 EVENT(timeupdate) TEST(video.currentTime.toFixed(1) == 0 && video.currentLoop==1) OK
     4RUN(video.play())
     5EVENT(timeupdate)
     6EVENT(timeupdate)
     7TEST(video.currentLoop == 1) OK
     8TEST(video.currentTime.toFixed(1) == 0) OK
    39END OF TEST
    410
  • trunk/LayoutTests/media/video-loopend.html

    r27758 r27866  
    1 <video src=content/test.mp4 loopcount=2></video>
     1<video src=content/test.mp4 playcount=2 loopend=0.5s></video>
     2<p>Test that video loops when time reaches the value specified by the loopEnd attribute.</p>
    23<script src=video-test.js></script>
    34<script>
    4 video.loopEnd = 0.5;
    55test("video.loopEnd==0.5");
    66setTimeout(function () { test("video.currentTime<=0.5"); }, 2000);
    7 waitForEventTestAndEnd('timeupdate', "video.currentTime.toFixed(1) == 0 && video.currentLoop==1");
    8 video.play();
     7waitForEvent('timeupdate', function () {
     8    if (video.currentLoop == 1) {
     9        test("video.currentLoop == 1");
     10        test("video.currentTime.toFixed(1) == 0");
     11        endTest();
     12    }
     13});
     14run("video.play()");
    915</script>
  • trunk/LayoutTests/media/video-loopstart-expected.txt

    r27286 r27866  
     1Test that playing video rewinds to time specified by the loopstart attribute when it loops.
     2
    13TEST(video.loopStart==1.0) OK
    2 EVENT(play)
    3 EVENT(timeupdate) TEST(video.currentTime.toFixed(1) == 1.0 && video.currentLoop==1) OK
     4RUN(video.play())
     5EVENT(load)
     6EVENT(timeupdate)
     7EVENT(timeupdate)
     8EVENT(timeupdate)
     9TEST(video.currentLoop == 1) OK
     10TEST(video.currentTime.toFixed(1) == 1.0) OK
    411END OF TEST
    512
  • trunk/LayoutTests/media/video-loopstart.html

    r27758 r27866  
    1 <video src=content/test.mp4 loopcount=2 loopstart=1s></video>
     1<video playcount=2 loopstart=1s></video>
     2<p>Test that playing video rewinds to time specified by the loopstart attribute when it loops.</p>
    23<script src=video-test.js></script>
    34<script>
    45test("video.loopStart==1.0");
    5 waitForEvent('play', function () { video.currentTime = 500; });
    6 waitForEventTestAndEnd('timeupdate', "video.currentTime.toFixed(1) == 1.0 && video.currentLoop==1");
    7 video.play();
     6waitForEvent('load', function () {
     7    waitForEvent('timeupdate', function () {
     8        failTestIn(1000);
     9        if (video.currentLoop == 1) {
     10            test("video.currentLoop == 1");
     11            test("video.currentTime.toFixed(1) == 1.0");
     12            endTest();
     13        }
     14    });
     15    video.currentTime = video.duration - 0.2;
     16});
     17video.src = "content/test.mp4";
     18run("video.play()");
    819</script>
  • trunk/LayoutTests/media/video-test.js

    r27696 r27866  
    119119}
    120120
     121function failTestIn(ms)
     122{
     123    setTimeout(function () {
     124        consoleWrite("FAIL: did not end fast enough");
     125        endTest();
     126    }, ms);
     127}
     128
    121129function consoleWrite(text)
    122130{
  • trunk/WebCore/ChangeLog

    r27865 r27866  
     12007-11-16  Antti Koivisto  <antti@apple.com>
     2
     3        Reviewed by Adele.
     4       
     5        Seeking related fixes, updates match to the latest specification
     6        - rename loopCount of HTMLMediaElement to playCount
     7        - add explicit seeking attribute to HTMLMediaElement to get semantics right
     8        - implement the specification behavior that currentTime must immediately return seeked position in HTMLMediaElement
     9          instead of MoviePrivateQTKit
     10        - fix broken behavior when seeking past end of the media, add tests
     11        - replace Movie didEnd callback with broader timeChanged callback (which gets called in didEnd case too)
     12        - use setDelayCallbacks: in various MoviePrivateQTKit methods to avoid bug prone synchronous callbacks from QT, make
     13          HTMLMediaElement not depend on synchronous callbacks
     14        - do some cleanups and simplifications in MoviePrivateQTKit, get rid of m_rateBeforeSeek and m_blockStateUpdate variables
     15
     16        Tests: http/tests/media/video-seekable-stall.html
     17               media/video-seeking.html
     18               media/video-seek-past-end-paused.html
     19               media/video-seek-past-end-playing.html
     20
     21        * html/HTMLAttributeNames.in:
     22        * html/HTMLMediaElement.cpp:
     23        (WebCore::HTMLMediaElement::HTMLMediaElement):
     24        (WebCore::HTMLMediaElement::load):
     25        (WebCore::HTMLMediaElement::movieNetworkStateChanged):
     26        (WebCore::HTMLMediaElement::setReadyState):
     27        (WebCore::HTMLMediaElement::seek):
     28        (WebCore::HTMLMediaElement::seeking):
     29        (WebCore::HTMLMediaElement::currentTime):
     30        (WebCore::HTMLMediaElement::ended):
     31        (WebCore::HTMLMediaElement::play):
     32        (WebCore::HTMLMediaElement::pause):
     33        (WebCore::HTMLMediaElement::playCount):
     34        (WebCore::HTMLMediaElement::setPlayCount):
     35        (WebCore::HTMLMediaElement::checkIfSeekNeeded):
     36        (WebCore::HTMLMediaElement::movieTimeChanged):
     37        (WebCore::HTMLMediaElement::endedPlayback):
     38        (WebCore::HTMLMediaElement::updateMovie):
     39        * html/HTMLMediaElement.h:
     40        * html/HTMLMediaElement.idl:
     41        * platform/graphics/Movie.cpp:
     42        (WebCore::Movie::timeChanged):
     43        * platform/graphics/Movie.h:
     44        (WebCore::MovieClient::movieTimeChanged):
     45        * platform/graphics/mac/MoviePrivateQTKit.h:
     46        * platform/graphics/mac/MoviePrivateQTKit.mm:
     47        (WebCore::MoviePrivate::MoviePrivate):
     48        (WebCore::MoviePrivate::load):
     49        (WebCore::MoviePrivate::play):
     50        (WebCore::MoviePrivate::pause):
     51        (WebCore::MoviePrivate::currentTime):
     52        (WebCore::MoviePrivate::seek):
     53        (WebCore::MoviePrivate::doSeek):
     54        (WebCore::MoviePrivate::cancelSeek):
     55        (WebCore::MoviePrivate::seekTimerFired):
     56        (WebCore::MoviePrivate::startCuePointTimerIfNeeded):
     57        (WebCore::MoviePrivate::paused):
     58        (WebCore::MoviePrivate::updateStates):
     59        (WebCore::MoviePrivate::timeChanged):
     60        (WebCore::MoviePrivate::didEnd):
     61
    1622007-11-16  Anders Carlsson  <andersca@apple.com>
    263
  • trunk/WebCore/html/HTMLAttributeNames.in

    r27465 r27866  
    7474longdesc
    7575loop
    76 loopcount
     76playcount
    7777loopend
    7878loopstart
  • trunk/WebCore/html/HTMLMediaElement.cpp

    r27826 r27866  
    7373    , m_muted(false)
    7474    , m_paused(true)
     75    , m_seeking(false)
     76    , m_currentTimeDuringSeek(0)
    7577    , m_previousProgress(0)
    7678    , m_previousProgressTime(numeric_limits<double>::max())
     
    262264        m_readyState = DATA_UNAVAILABLE;
    263265        m_paused = true;
     266        m_seeking = false;
    264267        if (m_movie) {
    265268            m_movie->pause();
     
    355358    if (state >= Movie::LoadedMetaData && m_networkState < LOADED_METADATA) {
    356359        m_movie->seek(effectiveStart());
    357         m_movie->setEndTime(currentLoop() == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
    358360        m_networkState = LOADED_METADATA;
    359361       
     
    417419    bool wasActivelyPlaying = activelyPlaying();
    418420    m_readyState = state;
     421   
     422    if (state >= CAN_PLAY)
     423        m_seeking = false;
    419424   
    420425    if (networkState() == EMPTY)
     
    443448        }
    444449    }
    445     updatePlayState();
     450    updateMovie();
    446451}
    447452
     
    486491 
    487492    // 3
    488     float maxTime = currentLoop() == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd();
     493    float maxTime = currentLoop() == playCount() - 1 ? effectiveEnd() : effectiveLoopEnd();
    489494   
    490495    // 4
     
    502507   
    503508    // 7
    504     if (m_movie) {
    505         m_movie->seek(time);
    506         m_movie->setEndTime(maxTime);
    507     }
    508    
     509    m_currentTimeDuringSeek = time;
     510
    509511    // 8
    510     // The seeking DOM attribute is implicitly set to true
     512    m_seeking = true;
    511513   
    512514    // 9
     
    516518    // As soon as the user agent has established whether or not the media data for the new playback position is available,
    517519    // and, if it is, decoded enough data to play back that position, the seeking DOM attribute must be set to false.
     520    if (m_movie) {
     521        m_movie->setEndTime(maxTime);
     522        m_movie->seek(time);
     523    }
    518524}
    519525
     
    525531bool HTMLMediaElement::seeking() const
    526532{
    527     if (!m_movie)
    528         return false;
    529     RefPtr<TimeRanges> seekableRanges = seekable();
    530     return m_movie->seeking() && seekableRanges->contain(currentTime());
     533    return m_seeking;
    531534}
    532535
     
    534537float HTMLMediaElement::currentTime() const
    535538{
    536     return m_movie ? m_movie->currentTime() : 0;
     539    if (!m_movie)
     540        return 0;
     541    if (m_seeking)
     542        return m_currentTimeDuringSeek;
     543    return m_movie->currentTime();
    537544}
    538545
     
    588595bool HTMLMediaElement::ended()
    589596{
    590     return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == loopCount() - 1;
     597    return endedPlayback();
    591598}
    592599
     
    619626    if (m_paused) {
    620627        m_paused = false;
    621         updatePlayState();
    622628        dispatchEventAsync(playEvent);
    623629    }
    624630
    625631    m_autoplaying = false;
     632   
     633    updateMovie();
    626634}
    627635
     
    638646    if (!m_paused) {
    639647        m_paused = true;
    640         updatePlayState();
    641648        dispatchEventAsync(timeupdateEvent);
    642649        dispatchEventAsync(pauseEvent);
     
    644651
    645652    m_autoplaying = false;
    646 }
    647 
    648 unsigned HTMLMediaElement::loopCount() const
    649 {
    650     String val = getAttribute(loopcountAttr);
     653   
     654    updateMovie();
     655}
     656
     657unsigned HTMLMediaElement::playCount() const
     658{
     659    String val = getAttribute(playcountAttr);
    651660    int count = val.toInt();
    652661    return max(count, 1);
    653662}
    654663
    655 void HTMLMediaElement::setLoopCount(unsigned count, ExceptionCode& ec)
     664void HTMLMediaElement::setPlayCount(unsigned count, ExceptionCode& ec)
    656665{
    657666    if (!count) {
     
    659668        return;
    660669    }
    661     setAttribute(loopcountAttr, String::number(count));
     670    setAttribute(playcountAttr, String::number(count));
    662671    checkIfSeekNeeded();
    663672}
     
    798807    // 3.14.9.5. Offsets into the media resource
    799808    // 1
    800     if (loopCount() - 1 < m_currentLoop)
    801         m_currentLoop = loopCount() - 1;
     809    if (playCount() <= m_currentLoop)
     810        m_currentLoop = playCount() - 1;
    802811   
    803812    // 2
     
    816825       
    817826    // 5
    818     if (m_currentLoop < loopCount() - 1 && time > effectiveLoopEnd()) {
     827    if (m_currentLoop < playCount() - 1 && time > effectiveLoopEnd()) {
    819828        seek(effectiveLoopStart(), ec);
    820829        m_currentLoop++;
     
    822831   
    823832    // 6
    824     if (m_currentLoop == loopCount() - 1 && time > effectiveEnd())
     833    if (m_currentLoop == playCount() - 1 && time > effectiveEnd())
    825834        seek(effectiveEnd(), ec);
    826835
    827     updatePlayState();
     836    updateMovie();
    828837}
    829838
     
    839848}
    840849
    841 void HTMLMediaElement::movieDidEnd(Movie*)
    842 {
    843     if (m_currentLoop < loopCount() - 1 && currentTime() >= effectiveLoopEnd()) {
    844         m_movie->seek(effectiveLoopStart());
     850void HTMLMediaElement::movieTimeChanged(Movie*)
     851{
     852    if (readyState() >= CAN_PLAY)
     853        m_seeking = false;
     854   
     855    if (m_currentLoop < playCount() - 1 && currentTime() >= effectiveLoopEnd()) {
     856        ExceptionCode ec;
     857        seek(effectiveLoopStart(), ec);
    845858        m_currentLoop++;
    846         m_movie->setEndTime(m_currentLoop == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
    847         updatePlayState();
    848859        dispatchHTMLEvent(timeupdateEvent, false, true);
    849860    }
    850861   
    851     if (m_currentLoop == loopCount() - 1 && currentTime() >= effectiveEnd()) {
     862    if (m_currentLoop == playCount() - 1 && currentTime() >= effectiveEnd()) {
    852863        dispatchHTMLEvent(timeupdateEvent, false, true);
    853864        dispatchHTMLEvent(endedEvent, false, true);
    854865    }
     866
     867    updateMovie();
    855868}
    856869
     
    957970bool HTMLMediaElement::endedPlayback() const
    958971{
    959     return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == loopCount() - 1;
    960 }
    961 
    962 void HTMLMediaElement::updatePlayState()
     972    return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == playCount() - 1;
     973}
     974
     975void HTMLMediaElement::updateMovie()
    963976{
    964977    if (!m_movie)
    965978        return;
    966     bool shouldBePlaying = activelyPlaying();
     979   
     980    m_movie->setEndTime(currentLoop() == playCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
     981
     982    bool shouldBePlaying = activelyPlaying() && currentTime() < effectiveEnd();
    967983    if (shouldBePlaying && m_movie->paused())
    968984        m_movie->play();
  • trunk/WebCore/html/HTMLMediaElement.h

    r27758 r27866  
    109109    float loopEnd() const;
    110110    void setLoopEnd(float time);
    111     unsigned loopCount() const;
    112     void setLoopCount(unsigned, ExceptionCode&);
     111    unsigned playCount() const;
     112    void setPlayCount(unsigned, ExceptionCode&);
    113113    unsigned currentLoop() const;
    114114    void setCurrentLoop(unsigned);
     
    141141    virtual void movieNetworkStateChanged(Movie*);
    142142    virtual void movieReadyStateChanged(Movie*);
     143    virtual void movieTimeChanged(Movie*);
    143144    virtual void movieVolumeChanged(Movie*);
    144     virtual void movieDidEnd(Movie*);
    145145    virtual void movieCuePointReached(Movie*, float cueTime);
    146146       
     
    153153   
    154154    String pickMedia();
    155     void updatePlayState();
     155    void updateMovie();
    156156    float effectiveStart() const;
    157157    float effectiveEnd() const;
     
    186186    bool m_paused;
    187187    bool m_seeking;
     188   
     189    float m_currentTimeDuringSeek;
    188190   
    189191    unsigned m_previousProgress;
  • trunk/WebCore/html/HTMLMediaElement.idl

    r27277 r27866  
    7676    attribute float loopStart;
    7777    attribute float loopEnd;
    78     attribute unsigned long loopCount
     78    attribute unsigned long playCount
    7979        setter raises (DOMException);
    8080    attribute unsigned long currentLoop;
  • trunk/WebCore/platform/graphics/Movie.cpp

    r27597 r27866  
    264264}
    265265
    266 void Movie::didEnd()
    267 {
    268     if (m_movieClient)
    269         m_movieClient->movieDidEnd(this);
     266void Movie::timeChanged()
     267{
     268    if (m_movieClient)
     269        m_movieClient->movieTimeChanged(this);
    270270}
    271271
  • trunk/WebCore/platform/graphics/Movie.h

    r27277 r27866  
    5050    virtual void movieReadyStateChanged(Movie*) { }
    5151    virtual void movieVolumeChanged(Movie*) { }
    52     virtual void movieDidEnd(Movie*) { }
     52    virtual void movieTimeChanged(Movie*) { }
    5353    virtual void movieCuePointReached(Movie*, float cueTime) { }
    5454};
     
    120120    void readyStateChanged();
    121121    void volumeChanged();
    122     void didEnd();
     122    void timeChanged();
    123123    void cuePointReached(float cueTime);
    124124
  • trunk/WebCore/platform/graphics/mac/MoviePrivateQTKit.h

    r27277 r27866  
    114114   
    115115private:
    116    
    117116    void updateStates();
     117    void doSeek();
    118118    void cancelSeek();
    119119    void seekTimerFired(Timer<MoviePrivate>*);
     
    132132    Timer<MoviePrivate> m_cuePointTimer;
    133133    float m_previousTimeCueTimerFired;
    134     float m_rateBeforeSeek;
    135134    Movie::NetworkState m_networkState;
    136135    Movie::ReadyState m_readyState;
    137136    bool m_startedPlaying;
    138     bool m_blockStateUpdate;
    139137    bool m_isStreaming;
    140138};
  • trunk/WebCore/platform/graphics/mac/MoviePrivateQTKit.mm

    r27600 r27866  
    7171    , m_cuePointTimer(this, &MoviePrivate::cuePointTimerFired)
    7272    , m_previousTimeCueTimerFired(0)
    73     , m_rateBeforeSeek(0)
    7473    , m_networkState(Movie::Empty)
    7574    , m_readyState(Movie::DataUnavailable)
    7675    , m_startedPlaying(false)
    77     , m_blockStateUpdate(false)
    7876    , m_isStreaming(false)
    7977{
     
    170168    cancelSeek();
    171169    m_cuePointTimer.stop();
     170   
     171    [m_objcObserver.get() setDelayCallbacks:YES];
     172
    172173    createQTMovie(url);
    173174    if (m_movie->visible())
    174175        createQTMovieView();
    175    
    176     updateStates();
     176
     177    [m_objcObserver.get() loadStateChanged:nil];
     178    [m_objcObserver.get() setDelayCallbacks:NO];
    177179}
    178180
    179181void MoviePrivate::play()
    180182{
    181     cancelSeek();
    182183    if (!m_qtMovie)
    183184        return;
    184185    m_startedPlaying = true;
     186    [m_objcObserver.get() setDelayCallbacks:YES];
    185187    [m_qtMovie.get() setRate: m_movie->rate()];
     188    [m_objcObserver.get() setDelayCallbacks:NO];
    186189    startCuePointTimerIfNeeded();
    187190}
     
    189192void MoviePrivate::pause()
    190193{
    191     cancelSeek();
    192194    if (!m_qtMovie)
    193195        return;
    194196    m_startedPlaying = false;
     197    [m_objcObserver.get() setDelayCallbacks:YES];
    195198    [m_qtMovie.get() stop];
     199    [m_objcObserver.get() setDelayCallbacks:NO];
    196200    m_cuePointTimer.stop();
    197201}
     
    211215    if (!m_qtMovie)
    212216        return 0;
    213     if (seeking())
    214         return m_seekTo;
    215217    QTTime time = [m_qtMovie.get() currentTime];
    216218    float current = (float)time.timeValue / time.timeScale;   
     
    229231        time = duration();
    230232   
    231     if (maxTimeLoaded() < time) {
    232         m_seekTo = time;
    233         m_seekTimer.startRepeating(0.5f);
    234         m_rateBeforeSeek = [m_qtMovie.get() rate];
    235         [m_qtMovie.get() setRate:0.0f];
     233    m_seekTo = time;
     234    if (maxTimeLoaded() >= m_seekTo)
     235        doSeek();
     236    else
     237        m_seekTimer.start(0, 0.5f);
     238}
     239   
     240void MoviePrivate::doSeek()
     241{
     242    QTTime qttime = createQTTime(m_seekTo);
     243    // setCurrentTime generates several event callbacks, update afterwards
     244    [m_objcObserver.get() setDelayCallbacks:YES];
     245    float oldRate = [m_qtMovie.get() rate];
     246    [m_qtMovie.get() setRate:0];
     247    [m_qtMovie.get() setCurrentTime: qttime];
     248    float timeAfterSeek = currentTime();
     249    // restore playback only if not at end, othewise QTMovie will loop
     250    if (timeAfterSeek < duration() && timeAfterSeek < m_endTime)
     251        [m_qtMovie.get() setRate:oldRate];
     252    cancelSeek();
     253    [m_objcObserver.get() setDelayCallbacks:NO];
     254}
     255
     256void MoviePrivate::cancelSeek()
     257{
     258    m_seekTo = -1;
     259    m_seekTimer.stop();
     260}
     261
     262void MoviePrivate::seekTimerFired(Timer<MoviePrivate>*)
     263{       
     264    if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) {
     265        cancelSeek();
    236266        updateStates();
    237     } else {
    238         QTTime qttime = createQTTime(time);
    239         // setCurrentTime generates several event callbacks, update afterwards
    240         m_blockStateUpdate = true;
    241         [m_qtMovie.get() setCurrentTime: qttime];
    242         m_blockStateUpdate = false;
    243         updateStates();
     267        m_movie->timeChanged();
     268        return;
     269    }
     270   
     271    if (maxTimeLoaded() >= m_seekTo)
     272        doSeek();
     273    else {
     274        Movie::NetworkState state = networkState();
     275        if (state == Movie::Empty || state == Movie::Loaded) {
     276            cancelSeek();
     277            updateStates();
     278            m_movie->timeChanged();
     279        }
    244280    }
    245281}
     
    267303void MoviePrivate::startCuePointTimerIfNeeded()
    268304{
    269    
    270305    if ((m_endTime < duration() || !m_movie->m_cuePoints.isEmpty())
    271306        && m_startedPlaying && !m_cuePointTimer.isActive()) {
    272307        m_previousTimeCueTimerFired = currentTime();
    273308        m_cuePointTimer.startRepeating(0.020f);
    274     }
    275 }
    276 
    277 void MoviePrivate::cancelSeek()
    278 {
    279     if (m_seekTo > -1) {
    280         m_seekTo = -1;
    281         if (m_qtMovie)
    282             [m_qtMovie.get() setRate:m_rateBeforeSeek];
    283     }
    284     m_rateBeforeSeek = 0.0f;
    285     m_seekTimer.stop();
    286 }
    287 
    288 void MoviePrivate::seekTimerFired(Timer<MoviePrivate>*)
    289 {       
    290     if (!m_qtMovie) {
    291         cancelSeek();
    292         return;
    293     }
    294     if (!seeking()) {
    295         updateStates();
    296         return;
    297     }
    298    
    299     if (maxTimeLoaded() > m_seekTo) {
    300         QTTime qttime = createQTTime(m_seekTo);
    301         // setCurrentTime generates several event callbacks, update afterwards
    302         m_blockStateUpdate = true;
    303         [m_qtMovie.get() setCurrentTime: qttime];
    304         m_blockStateUpdate = false;
    305         cancelSeek();
    306         updateStates();
    307     }
    308 
    309     Movie::NetworkState state = networkState();
    310     if (state == Movie::Empty || state == Movie::Loaded) {
    311         cancelSeek();
    312         updateStates();
    313309    }
    314310}
     
    337333    if (!m_qtMovie)
    338334        return true;
    339     return [m_qtMovie.get() rate] == 0.0f && (!seeking() || m_rateBeforeSeek == 0.0f);
     335    return [m_qtMovie.get() rate] == 0.0f;
    340336}
    341337
     
    462458void MoviePrivate::updateStates()
    463459{
    464     if (m_blockStateUpdate)
    465         return;
    466    
    467460    Movie::NetworkState oldNetworkState = m_networkState;
    468461    Movie::ReadyState oldReadyState = m_readyState;
     
    474467        if (m_networkState < Movie::Loaded)
    475468            m_networkState = Movie::Loaded;
    476             m_readyState = Movie::CanPlayThrough;
     469        m_readyState = Movie::CanPlayThrough;
    477470    } else if (loadState >= 20000) {
    478471        // 20000 is kMovieLoadStatePlaythroughOK
     
    498491        m_readyState = Movie::DataUnavailable;
    499492    }
    500    
     493
    501494    if (seeking())
    502495        m_readyState = Movie::DataUnavailable;
     
    526519    m_previousTimeCueTimerFired = -1;
    527520    updateStates();
     521    m_movie->timeChanged();
    528522}
    529523
     
    537531    m_cuePointTimer.stop();
    538532    m_startedPlaying = false;
    539     m_movie->didEnd();
     533    updateStates();
     534    m_movie->timeChanged();
    540535}
    541536
Note: See TracChangeset for help on using the changeset viewer.