Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pause video on last frame in QML?

Tags:

video

qt

qt5

qml

I'm trying to stop a QML video and show its last frame when playback has finished. Does anybody know how to do this? (Sorry, this seems to be not as trivial as it sounds...)

At the moment, my problem is that the Video element simply becomes invisible/hidden after playback is done. (onVisibleChanged is never called.)

When I use the hack in onStatusChanged in my code, the video disappears for a moment after the end and then shows the end of the video.

What I'm doing is simply:

Video {
    anchors.fill: parent
    fillMode: VideoOutput.PreserveAspectFit;
    source: "path/to/file"
    autoPlay: true

    onStatusChanged: {
        console.warn("StatusChanged:"+status+"|"+MediaPlayer.Loaded)
        if (status == MediaPlayer.EndOfMedia)
        {
        // seek a bit before the end of the video since the last frames
        // are the same here, anyway
            seek(metaData.duration-200)
            play()
            pause()
        }
    }
    onVisibleChanged:
    {
        console.log(visible)
    }
}

It's possible that I'm missing something, but I could not find anything on this topic in the docs. Also, using separate MediaPlayer and VideoOutput does not change the behavior.

For the record, I'm using the latest Qt 5.2 on Windows (msvc2010+OpenGL-build).

like image 461
anderas Avatar asked Jan 15 '14 13:01

anderas


3 Answers

I'm still looking for a better solution to this. What I've come up with is to pause the video one second before it's over:

MediaPlayer {
    autoLoad: true
    id: video

    onPositionChanged: {
        if (video.position > 1000 && video.duration - video.position < 1000) {
            video.pause();
        }
    }
}

Why one second? On my machine if you try to pause it about 500ms before the end, the video manages to run to completion and disappear from view without even registering the pause() call. Thus 1 second is sort of a good safe value for me.

Frankly, I'd prefer if there was a more explicit way to tell the MediaPlayer what to do at the end of the video. I know for a fact that GStreamer, which is what Qt uses on Linux and Mac, notifies you when the video is almost over so that you can decide what to do next - e.g. pause the video or loop it seamlessly.

like image 56
Stefan Dragnev Avatar answered Oct 22 '22 02:10

Stefan Dragnev


I share your pain (mainly interested in OSX and iOS, where the same problem occurs). The only solution I have is to pair each video (which at least are "canned" app resources, not dynamic content off the net) with a png image of their final frame. When the video starts, enable display of the image under it (although it's not actually visible at that point). When the video ends abruptly, the image is left visible.

This works perfectly on iOS, but on (some?) Macs there may a slight brightness jump between the video and the image (guessing: something to do with OSX display preferences' screen calibration not affecting video?)

An option to the MediaPlayer or VideoOutput element types to freeze on the last frame would indeed be much simpler.

One other possibility I've considered but not tried would be stacking two videos. The one on top is the main player, but the one underneath would just be seeked to, say, the last millisecond of the video and paused. Then when the main video finishes and disappears... there's as-good-as the final frame of the video showing there underneath. This'd basically be the same as the image-based solution, but with the image underneath being dynamically created using a video player. I've found mobile HW's video and Qt's wrappings of it to be temperamental enough (admittedly more back in the earlier days of Qt5) to really not want to try and do anything too clever with it at all though.

like image 36
timday Avatar answered Oct 22 '22 03:10

timday


A few years later and I am facing the same issue. However, I found a workaround (for Qt >= 5.9) that allows me to pause the video within 100 ms of the end:

It seems that the issue is related to the notifyInterval property (introduced in Qt 5.9). It is by default set to 1000ms (same as the 1000ms observed in other answers, not a coincidence I believe). Thus, changing it to a very small value when the video is almost done allows for catching a position very close to the end and pausing the video there:

Video {
    id: videoelem
    source: "file:///my/video.mp4"
    notifyInterval: videoelem.duration>2000 ? 1000 : 50
    onPositionChanged: {
        if(position > videoelem.duration-2*notifyInterval) {
            if(notifyInterval == 1000)
                notifyInterval = 50
            else if(notifyInterval == 50)
                videoelem.pause()
        }
    }
    onStatusChanged: {
        if(status == MediaPlayer.Loaded)
            videoelem.play()
    }
}

Hope this helps somebody!

like image 4
Irigum Avatar answered Oct 22 '22 01:10

Irigum