iosswiftavfoundationavplayertimecodes

Display Timecode for video track using Swift AVPlayer


I am working on a little app to navigate and play videos which includes framerate and initial timecode information (eg: "TimeCode" : "09:25:15:08"). I am using the AVKit Player View Controller to display my video, and I would like to add a UILabel displaying the current timecode.

I'm all set about the way to add up custom UI elements, but I'm lost about how to calculate the timecode and make it update itself every frame the video plays.

I have read about the AVFoundation - Timecode Support with AVAssetWriter and AVAssetReader, but I'm not sure if I have understood it.

Any explanations, guidance, or content to look at would be really appreciated.

UPDATE:

After thinking for a while, I though that I could use the frame count to build up my own timecode references.

note item is the AVPlayer

using var totalTime = CMTimeGetSeconds(item.currentItem.asset.duration)I can get the total length in seconds of the video track, and currentTime = CMTimeGetSeconds(item.currentItem.currentTime())to get its current time position.

Then I can do var fps = item.currentItem.asset.tracks[0].nominalFrameRateto get the framerate and use this variable to divide totalTime and currentTimeto get the total frame count as well as the current frame.

With this im considering the idea of building up a pre normalized array of time in second for each frame from the total frame count. this way I could know what exact frame is related to a time stamp.

I never had to work with timecode or dates so if someone has an idea about the way to do this I would appreciate the help.

basically, a timecode looks like this: HH:MM:SS:FF (FF being the current frame). If the fps = 24, then every 24 frames a second is added to the SS and so on.

TEST CASE:

import UIKit
import AVKit
import AVFoundation

class ViewController: UIViewController {
    var player = AVPlayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        var url=NSURL(string: "http://km.support.apple.com/library/APPLE/APPLECARE_ALLGEOS/HT1211/sample_iTunes.mov")
        player = AVPlayer(URL: url)
        let playerController = AVPlayerViewController()

        playerController.player = player
        self.addChildViewController(playerController)
        self.view.addSubview(playerController.view)
        playerController.view.frame = self.view.frame

        //Debug btn
        var btn = UIButton()
        btn.frame = CGRect(x:10, y:50, width:100, height:30)
        btn.setTitle("FPS", forState: .Normal)
        btn.addTarget(self, action: "buttonTapAction:", forControlEvents: UIControlEvents.TouchUpInside)
        playerController.view.addSubview(btn)

        player.play()
//        getFps(player)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func getFps(item:AVPlayer) {
        var fps = item.currentItem.asset.tracks[0].nominalFrameRate
        println("FPS: \(fps)")

        var timeRange_src = CMTimeGetSeconds(item.currentItem.asset.duration)
        var timeRange = Float(timeRange_src)
        println("time Range: \(timeRange)")
        var frameCount = timeRange * fps
        println("total frames: \(frameCount)")

        var timeIs = Float(CMTimeGetSeconds(item.currentItem.currentTime()))
        var frameIs = timeIs * fps
        println("current time: \(timeIs)")
        println("current frame: \(frameIs)")

    }

    func buttonTapAction(sender:UIButton!)
    {
        getFps(player)
    }


}

Solution

  • ok so here is how I solved my problem and built the timecode based on the variables I had:

    func pad(fps:Float, currentFrame:Float) -> (String){
            var fps = fps
            var frame = currentFrame + f
            var ff = frame % fps
            var seconds = s + ((currentFrame) / fps)
            var ss = seconds % 60
            var minutes = m + ((seconds - ss) / 60)
            var mm = minutes % 60
            var hh = h + ((minutes - mm) / 60)
            return "\(showTwoDigits(hh)):\(showTwoDigits(mm)):\(showTwoDigits(ss)):\(showTwoDigits(ff))"
        }
    
        func showTwoDigits(number:Float) -> (String){
            var string = ("00" + String(format:"%.f", number))
            var range = Range(start: (advance(string.endIndex, -2)), end: string.endIndex)
            var cutStr = string.substringWithRange(range)
            return cutStr
        }