3

I want CMTime to String an human readable.
So I Found below code.

extension CMTime {

    var durationText:String {
        let totalSeconds = CMTimeGetSeconds(self)
        let hours:Int = Int(totalSeconds / 3600)
        let minutes:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 3600) / 60)
        let seconds:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 60))

        if hours > 0 {
            return String(format: "%i:%02i:%02i", hours, minutes, seconds)
        } else {
            return String(format: "%02i:%02i", minutes, seconds)
        }
    }

}

And I Have 30 second video files. It's CMTime value is 17945.
I expect this durationText 00:30.
But result is 00:29.
And other video files same.
What Should I fix??

Kaushik Makwana
  • 1,329
  • 2
  • 14
  • 24
oddK
  • 261
  • 4
  • 14
  • Your durationText extension returns `00:30` for me. when I run `CMTime(value: 30, timescale: 1).durationText` – Leo Dabus Feb 13 '19 at 03:44
  • https://stackoverflow.com/a/54641797/2303865 – Leo Dabus Feb 13 '19 at 03:45
  • Just some notes. You can use CMTime instance property `seconds`. No need to create a variable for that. `let hours = Int(seconds / 3600)`. Btw Swift is a type inferred language. No need to explicitly set the type to Int. – Leo Dabus Feb 13 '19 at 03:48
  • `extension CMTime { var hrs: Int { return Int(seconds / 3600) } var mins: Int { return Int(seconds.truncatingRemainder(dividingBy: 3600) / 60) } var secs: Int { return Int(seconds.truncatingRemainder(dividingBy: 60)) } var duration: String { return hrs > 0 ? String(format: "%i:%02i:%02i", hrs, mins, secs) : String(format: "%02i:%02i", mins, secs) } }` `CMTime(value: 30, timescale: 1).duration` ` – Leo Dabus Feb 13 '19 at 03:58
  • @LeoDabus I tried CMTime value is 17945. and timescale is 600 – oddK Feb 13 '19 at 04:19
  • Why don't you work with timescale 1? – Leo Dabus Feb 13 '19 at 04:30
  • the result is correct 18000 should return 30 – Leo Dabus Feb 13 '19 at 04:30
  • I do not init CMTime. It's just my video file's duration. – oddK Feb 13 '19 at 04:30
  • https://stackoverflow.com/a/47585714/2303865 – Leo Dabus Feb 13 '19 at 04:32
  • @LeoDabus I tried your past answer. but It's just print out `0m 29s` – oddK Feb 13 '19 at 04:40
  • 1
    You video duration is less than 30s. the duration is `29.90833333333333` what would you expect? Round up the result yourself if you need it. `seconds.rounded(.up)` – Leo Dabus Feb 13 '19 at 04:40
  • @LeoDabus No...In Xcode, this video file's duration is 30s. and Finder QuickTime Player also 30s. But why In my app 29s??? – oddK Feb 13 '19 at 04:43
  • @LeoDabus Ah...Okay I See. I understand – oddK Feb 13 '19 at 04:44
  • 1
    probably better to use the standard `rounded()` which uses `.toNearestOrAwayFromZero` rounding rule. – Leo Dabus Feb 13 '19 at 04:46
  • 1
    @LeoDabus So much thankyou. I fixed it. You just answer anything. I select your answer. – oddK Feb 13 '19 at 04:52

1 Answers1

13

You need to round your seconds before calculating your time components.

extension CMTime {
    var roundedSeconds: TimeInterval {
        return seconds.rounded()
    }
    var hours:  Int { return Int(roundedSeconds / 3600) }
    var minute: Int { return Int(roundedSeconds.truncatingRemainder(dividingBy: 3600) / 60) }
    var second: Int { return Int(roundedSeconds.truncatingRemainder(dividingBy: 60)) }
    var positionalTime: String {
        return hours > 0 ?
            String(format: "%d:%02d:%02d",
                   hours, minute, second) :
            String(format: "%02d:%02d",
                   minute, second)
    }
}

Testing all the possible edge rounding cases:

CMTime(value: 0, timescale: 600).positionalTime              // "00:00"
CMTime(value: 300, timescale: 600).positionalTime            // "00:01"
CMTime(value: 600, timescale: 600).positionalTime            // "00:01"

CMTime(value: 18000 - 600, timescale: 600).positionalTime      // "00:29"
CMTime(value: 17945, timescale: 600).positionalTime            // "00:30"
CMTime(value: 18000, timescale: 600).positionalTime            // "00:30"
CMTime(value: 18055, timescale: 600).positionalTime            // "00:30"
CMTime(value: 18000 + 600, timescale: 600).positionalTime      // "00:31"


CMTime(value: 2160000 - 600, timescale: 600).positionalTime  // "59:59"
CMTime(value: 2160000 - 300, timescale: 600).positionalTime  // "1:00:00"
CMTime(value: 2160000, timescale: 600).positionalTime        // "1:00:00"
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571