2

Already achieved most of the work hopefully but still struggling in this part.

I want to get playback milliseconds. With this code. i get the duration and the current playing time. But i also want the milliseconds as a part of my subtitle application.

Okay so we start with the seconds. this code gives me what i want:

func getHoursMinutesSecondsFrom(seconds: Double) -> (hours: Int, minutes: Int, seconds: Int) {
        let secs = Int(seconds)
        let hours = secs / 3600
        let minutes = (secs % 3600) / 60
        let seconds = (secs % 3600) % 60

        // return the millisecond here aswell?

        return (hours, minutes, seconds)
}

Now with this function. we format the time and calculate:

func formatTimeFor(seconds: Double) -> String {
    let result = getHoursMinutesSecondsFrom(seconds: seconds)
    let hoursString = "\(result.hours)"
    var minutesString = "\(result.minutes)"
    if minutesString.characters.count == 1 {
        minutesString = "0\(result.minutes)"
    }
    var secondsString = "\(result.seconds)"
    if secondsString.characters.count == 1 {
        secondsString = "0\(result.seconds)"
    }
    var time = "\(hoursString):"
    if result.hours >= 1 {
        time.append("\(minutesString):\(secondsString)")
    }
    else {
        time = "\(minutesString):\(secondsString)"
    }
    return time
}

Now calling this function gets us what we want:

 func updateTime() {
    // Access current item
    if let currentItem = player?.currentItem {
        // Get the current time in seconds
        let playhead = currentItem.currentTime().seconds
        let duration = currentItem.duration.seconds
        // Format seconds for human readable string
        self.statics.text = "Current time: \(formatTimeFor(seconds: playhead)) ---> Full: \(formatTimeFor(seconds: duration))"

    }
}

Thanks. your help is truly appreciated

Update

The current way of checking for my playback duration is to have a timer that hits a function every 0.5 seconds. like this:

func scheduledTimerWithTimeInterval(){
    // Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds
    timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(self.updateCounting), userInfo: nil, repeats: true)
}

Side note. I do get the milliseconds in a way:

let millisecondsInt =  (seconds.truncatingRemainder(dividingBy: 1) * 1000)

The problem is. at every 0.5 seconds the update_time() function is hit. that gives me corrupt info. for example. if i wanted to put a subtitle at: 00:16:267 there's a 10% chance the code will run cause the numbers are random.

I assume that this code is somehow not correct, I'd like to have the counter do the job this way:

Current time: 00:14:266 ---> Full: 04:12:610
Current time: 00:14:267 ---> Full: 04:12:610 // 267 milliseconds +1
Current time: 00:14:268 ---> Full: 04:12:610
Current time: 00:14:269 ---> Full: 04:12:610
Current time: 00:14:270 ---> Full: 04:12:610
Current time: 00:14:271 ---> Full: 04:12:610
Current time: 00:14:272 ---> Full: 04:12:610

Not this random number jumping way:

Current time: 00:13:767 ---> Full: 04:12:610
Current time: 00:14:267 ---> Full: 04:12:610
Current time: 00:14:767 ---> Full: 04:12:610
Current time: 00:15:266 ---> Full: 04:12:610
Current time: 00:15:767 ---> Full: 04:12:610
Current time: 00:16:267 ---> Full: 04:12:610
Current time: 00:16:767 ---> Full: 04:12:610
  • You should be able to do something like `let milliseconds = seconds * 1000`... – l'L'l Sep 27 '18 at 19:20
  • 1
    You're basically asking how to convert seconds to milliseconds... the goal/intent of the question isn't related to AVPlayer or any specific thing besides the math – Stephen J Sep 27 '18 at 21:21

2 Answers2

2

I think your big question is, how can I get an callback from the avplayer at certain times, so I can add the correct subtitle.

For that question I found the addBoundaryTimeObserver.

I didn't test this, but it looks like you can add the start times of the subtitles (and possibly the end times) and add your addSubtitle(or similar) function in the callback.

Here some code example:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    let videoURL = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
    let player = AVPlayer(url: videoURL! as URL)
    let controller = AVPlayerViewController()
    controller.player = player

    present(controller, animated: true) {
        player.play()
    }
    var times = [NSValue]()

    let smallValue1 = 2.0
    let frameTime1 = CMTime(seconds: smallValue1, preferredTimescale: 1000000)
    times.append(frameTime1 as NSValue)

    let smallValue2 = 4.0
    let frameTime2 = CMTime(seconds: smallValue2, preferredTimescale: 1000000)
    times.append(frameTime2 as NSValue)

    var timeObserverToken = player.addBoundaryTimeObserver(forTimes: times, queue: DispatchQueue.main, using: {
            [weak self] in
            print("time observer callback")
            // do your thing here
    })
}
Thom
  • 157
  • 7
  • I understand that `times` has to be a set of `CMTime`, for that case. whats your suggestion to invoke a block on e.g `14:556` using something like this `let time = CMTime(seconds: <#T##Double#>, preferredTimescale: <#T##CMTimeScale#>)` –  Oct 02 '18 at 10:28
  • Yeah, you are right, see this example: https://stackoverflow.com/a/42410798/7727137 – Thom Oct 02 '18 at 10:39
  • Still unable to invoke a block on a specific second, for example. i want to print happy birthday on second 4 of the video. is this possible? –  Oct 02 '18 at 10:42
  • @KimberlyLife I have added and tested this piece of code and it should callback at 2 and 4 seconds, I gave the full example so you can modify it for your needs – Thom Oct 03 '18 at 15:52
  • Thank you for your continued support @Thom. With your code; I assume that smallValue1 or 2 is a Double And is it possible to trigger at 4.556? Like the second four and 556 milliseconds? As you may now subtitles is all about milliseconds. Thank you –  Oct 03 '18 at 16:08
2

Your code works fine. it is giving times every 0.5 seconds which in milliseconds will be 500. Look 14:267-13:767 = 0:500 . If you want to achieve to work code on every 1 milliseconds you must run code with

Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(self.updateCounting), userInfo: nil, repeats: true)

But i don't think that would work. Because calling function on every 1 milliseconds not good idea.

Axbor Axrorov
  • 2,720
  • 2
  • 17
  • 35
  • Thanks for the answer, you’re actually right calling a function every millisecond is a headache. I tried this the first time I wrote this question. It just won’t work – excitedmicrobe Oct 09 '18 at 06:13