Converting CMTime To String is wrong value return
Asked Answered
H

1

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??

Hedley answered 13/2, 2019 at 3:27 Comment(15)
Your durationText extension returns 00:30 for me. when I run CMTime(value: 30, timescale: 1).durationTextStilliform
https://mcmap.net/q/975179/-swift-datecomponentsformatter-drop-leading-zeroes-but-keep-at-least-one-digit-in-minutes-placeStilliform
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.Stilliform
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 `Stilliform
@LeoDabus I tried CMTime value is 17945. and timescale is 600Hedley
Why don't you work with timescale 1?Stilliform
the result is correct 18000 should return 30Stilliform
I do not init CMTime. It's just my video file's duration.Hedley
https://mcmap.net/q/1781448/-how-to-get-video-duration-fro-avasset-in-swiftStilliform
@LeoDabus I tried your past answer. but It's just print out 0m 29sHedley
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)Stilliform
@LeoDabus No...In Xcode, this video file's duration is 30s. and Finder QuickTime Player also 30s. But why In my app 29s???Hedley
@LeoDabus Ah...Okay I See. I understandHedley
probably better to use the standard rounded() which uses .toNearestOrAwayFromZero rounding rule.Stilliform
@LeoDabus So much thankyou. I fixed it. You just answer anything. I select your answer.Hedley
S
15

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"
Stilliform answered 13/2, 2019 at 4:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.