iosswift3cmtime

How to pass Float in CMTimeMake's timescale Int32


I am working on CMTimeMake in order to add slow and fast motion effect to Video. Where we have to divide by Video scale for Fast effect and multiply by Video scale for Slow effect.

Here it is:

let videoScaleFactor = Int64(2)

// Get the scaled video duration
let scaledVideoDuration = (mode == .Faster) ? CMTimeMake(videoAsset.duration.value / videoScaleFactor, videoAsset.duration.timescale) : CMTimeMake(videoAsset.duration.value * videoScaleFactor, videoAsset.duration.timescale)

Now as per my requirement, there is one Slider (between 0.1 to 2.0) where User will select the particular Video scale value for Slow and Fast effect. This Value is coming in Float.

My problem is when I am passing my Float value like 0.8 in my above code, then:

let videoScaleFactor = Int64(0.8) // this returns me 0

How can I return exact value 0.8 into this? Please advise me.


Solution

  • You wrote:

    let videoScaleFactor = Int64(0.8) // this returns me 0
    

    That's normal, because by definition can't have decimal value. So 0.8 => 0.

    Instead use a Float (or Double) depending on the precision you need.

    So let's try it:

    let videoScaleFactor = Float(0.8)
    
    // Get the scaled video duration
    let scaledVideoDuration = (mode == .Faster) ? CMTimeMake(videoAsset.duration.value / videoScaleFactor, videoAsset.duration.timescale) : CMTimeMake(videoAsset.duration.value * videoScaleFactor, videoAsset.duration.timescale)
    

    That rises another issue:

    Binary operator '/' cannot be applied to operands of type 'CMTimeValue' (aka 'Int64') and 'Float'

    Indeed in Swift you can't manipulate various types of Int/Float etc like that.

    So to fix it:

    let videoScaleFactor = Float(0.8)
    
    // Get the scaled video duration
    let scaledVideoDuration = (mode == .Faster) ? CMTimeMake(Float(videoAsset.duration.value) / videoScaleFactor, videoAsset.duration.timescale) : CMTimeMake(Float(videoAsset.duration.value) * videoScaleFactor, videoAsset.duration.timescale)
    

    Now you multiply/divide Float with other Float

    But

    func CMTimeMake(_ value: Int64, _ timescale: Int32) -> CMTime
    

    So CMTimeMake(_:_:) awaits for a Int64 value, so you get an error because Float(videoAsset.duration.value) / videoScaleFactor (for the first one) is returning a Float while the method wants an Int64.

    So just do

    let videoScaleFactor = Float(0.8)
    
    // Get the scaled video duration
    let scaledVideoDuration = (mode == .Faster) ? CMTimeMake(Int64(Float(videoAsset.duration.value) / videoScaleFactor), videoAsset.duration.timescale) : CMTimeMake(Int64(Float(videoAsset.duration.value) * videoScaleFactor), videoAsset.duration.timescale)
    

    That should work now.

    But I can't leave with that code. Your line is quite long and it's hard to read. In fact, you just modify the value param of CMTimeMake(_:_:).

    Let's factorize:

    let videoScaleFactor = Float(0.8)
    
    // Get the scaled video duration
    let scaledVideoDuration = CMTimeMake((mode == .Faster) ? Int64(Float(videoAsset.duration.value) / videoScaleFactor) : Int64(Float(videoAsset.duration.value) * videoScaleFactor), videoAsset.duration.timescale)
    

    Now, it's personal, by I'd prefer (nothing wrong with an extra line explicit):

    let videoScaleFactor = Float(0.8) 
    let value = (mode == .Faster) ? Float(videoAsset.duration.value) / videoScaleFactor : Float(videoAsset.duration.value) * videoScaleFactor 
    let scaledVideoDuration = CMTimeMake(Int64(value), videoAsset.duration.timescale)