Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift AVPlayer seekToTime issue

i have this code :

let punto = sender.locationInView(self.barraVideo).x
        let larghezzaImg : Double = Double((self.barraVideoInvisTocco?.frame.width)!)

        let percTocco : Double = (100 / larghezzaImg) * Double(punto)

        let lunghezzaVideo : Double = Double(CGFloat((avPlayer?.currentItem?.asset.duration.value)!) / CGFloat(1000.0));
        let frameRate : Int32 = (avPlayer?.currentItem?.currentTime().timescale)!

        let secondo : Float64 = Float64((lunghezzaVideo/100)*percTocco)
        print("Val : \(secondo)");

        avPlayer?.currentItem?.seekToTime(CMTimeMakeWithSeconds(secondo, frameRate))

        avPlayer?.play()
        videoInPlay = true;
        playPauseBtn!.image = UIImage(named: "iconaPausa.png")!;

That recive a x cordinate of a tap on the seekBar and seek the video. The code works with logs video but work very bad with short videos. When i tap on the short video the AVPlayer seek only near the begining or near the end of the video, not in the middle

print :

Val : 3.36008475976495
Val : 7.14189817632069
Val : 13.3303201306846
Val : 3.70388597945183
Val : 3.93308679257642
Val : 3.24548435320265
Val : 3.70388597945183
Val : 18.8311396456748
Val : 27.0823689181601
Val : 40.1468152662618
Val : 51.3776551093667
Val : 17.2267339538027
Val : 9.54850671412889
Val : 23.4151559081666
Val : 37.6256063218913

The val rappresents the right value of seconds where i wont to seek. Sorry for bad english, i'm italian

like image 939
Alessandro Zago Avatar asked Jul 20 '16 08:07

Alessandro Zago


3 Answers

For AVPlayer, there are different ways to let the play seeking time;

player.seekToTime(<#time: CMTime#CMTime#>)

for this method,player will seek to seekingTime quickly but not seeking the exactly where the seekingTime is, it means there will be some offset of the seekingTime.

Apple's Doc

Use this method to seek to a specified time for the current player item.

The time seeked to may differ from the specified time for efficiency.

For sample accurate seeking see seekToTime:toleranceBefore:toleranceAfter:.

player.seekToTime(<#T##time: CMTime##CMTime#>, completionHandler: <#T##(Bool) -> Void#>)

for this method,player will seek to seekingTime quickly and also,with the offset,you can do something with completionHandler

Apple's Doc

Use this method to seek to a specified time for the current player item and to be notified when the seek operation is complete.

The completion handler for any prior seek request that is still in process will be invoked immediately with the finished parameter

set to NO. If the new request completes without being interrupted by another seek request or by any other operation the specified

completion handler will be invoked with the finished parameter set to YES.

If you want to seeking more accurate, use the following method instead

player.seekToTime(<#T##time: CMTime##CMTime#>, toleranceBefore: <#T##CMTime#>, toleranceAfter: <#T##CMTime#>)

player.seekToTime(<#T##time: CMTime##CMTime#>, toleranceBefore: <#T##CMTime#>, toleranceAfter: <#T##CMTime#>, completionHandler: <#T##(Bool) -> Void#>)

this method will let the player seeking to specified time you asking,but will be slower(well..mostly depending on current media)

Apple's Doc

Use this method to seek to a specified time for the current player item.

The time seeked to will be within the range [time-toleranceBefore, time+toleranceAfter] and may differ from the specified time for efficiency.

Pass kCMTimeZero for both toleranceBefore and toleranceAfter to request sample accurate seeking which may incur additional decoding delay.

Messaging this method with beforeTolerance:kCMTimePositiveInfinity and afterTolerance:kCMTimePositiveInfinity is the same as messaging seekToTime: directly.

i've notice that current you are using

avPlayer?.currentItem?.seekToTime(CMTimeMakeWithSeconds(secondo, frameRate))

you can try to use this method to seeking the specified time (slower of course)

avPlayer?.currentItem?.seekToTime(CMTimeMakeWithSeconds(secondo, frameRate), toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)

Wish could help u out

like image 183
R. JA Avatar answered Oct 19 '22 10:10

R. JA


In Swift 4.2 and Xcode 10.1

player?.seek(to: CMTimeMakeWithSeconds(100, preferredTimescale: 1), toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero)
like image 4
Naresh Avatar answered Oct 19 '22 10:10

Naresh


I doubt any of the answers supplied actually worked. Try this:

Technical Q&A QA1820 How do I achieve smooth video scrubbing with AVPlayer seekToTime:?

Q: My app allows the user to scrub video files using a slider control in combination with AVPlayer seekToTime: but there is a considerable lag in the display of the video frames. How can I achieve smoother scrubbing?

A: Avoid making calls to AVPlayer seekToTime: in rapid succession. This will cancel the seeks in progress, resulting in a lot of seeking and not a lot of displaying of the target frames. Instead, use the completion handler variant of AVPlayer seekToTime:, and wait for a seek in progress to complete first before issuing another. Listing 1 and Listing 2 give examples of this technique (Note: these examples assume a valid player object has been created and the player's current item status is being maintained via key-value observing. See the AV Foundation Programming Guide for more information):

Using the completion handler variant of AVPlayer seekToTime: for smoother scrubbing (Swift).

import AVFoundation

class MyClass {

    var isSeekInProgress = false
    let player = <#A valid player object #>
    var chaseTime = kCMTimeZero
    // your player.currentItem.status
    var playerCurrentItemStatus:AVPlayerItemStatus = .Unknown

    ...

    func stopPlayingAndSeekSmoothlyToTime(newChaseTime:CMTime)
    {
        player.pause()

        if CMTimeCompare(newChaseTime, chaseTime) != 0
        {
            chaseTime = newChaseTime;

            if !isSeekInProgress
            {
                trySeekToChaseTime()
            }
        }
    }

    func trySeekToChaseTime()
    {
        if playerCurrentItemStatus == .Unknown
        {
            // wait until item becomes ready (KVO player.currentItem.status)
        }
        else if playerCurrentItemStatus == .ReadyToPlay
        {
            actuallySeekToTime()
        }
    }

    func actuallySeekToTime()
    {
        isSeekInProgress = true
        let seekTimeInProgress = chaseTime
        player.seekToTime(seekTimeInProgress, toleranceBefore: kCMTimeZero,
                toleranceAfter: kCMTimeZero, completionHandler:
        { (isFinished:Bool) -> Void in

            if CMTimeCompare(seekTimeInProgress, chaseTime) == 0
            {
                isSeekInProgress = false
            }
            else
            {
                trySeekToChaseTime()
            }
        })
    }

}
like image 2
James Bush Avatar answered Oct 19 '22 11:10

James Bush