3

The scaleTimeRange(timeRange: CMTimeRange, toDuration duration: CMTime) method works very well if one wants to apply Slow motion effect to video.

But I noticed that it only works if applied to the entire video's duration. If an arbitrary timeRange, e.g. a CMTimeRangeMake(_ start: 2, duration: 3) is passed , the method doesn't seem to work at all. i.e. When the mp4 video is exported it doesn't have the desired slow motion effect (from 0:00:02 - 0:00:05)

Q 1) Is there a way to apply this scaleTimeRange method to only a part of the video? If so, how can it be done?

Q2)If not , how can this slow motion effect be applied to only a part of the video? Is there any other way?

CODE :

var asset: AVAsset?


func  setupAsset(){

let videoAsset = AVURLAsset(url: Bundle.main.url(forResource: "Sample", withExtension: "mp4")!)

let comp = AVMutableComposition()

let videoAssetSourceTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo).first! as AVAssetTrack


let videoCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)


do {

    try videoCompositionTrack.insertTimeRange(
        CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(9 , 600)),
        of: videoAssetSourceTrack,
        at: kCMTimeZero)


    let videoScaleFactor = Int64(3.0)
    let videoDuration: CMTime = videoAsset.duration



    let tstStartTime = CMTime(value: 2, timescale: videoDuration.timescale)
    let tstDuration =  CMTime(value: 1 , timescale: videoDuration.timescale)

    //1.  Applies slow motion correctly (to entire video)

    videoCompositionTrack.scaleTimeRange(CMTimeRangeMake(kCMTimeZero , videoDuration), toDuration: CMTimeMake(videoDuration.value * videoScaleFactor, videoDuration.timescale))

    //2. Replace with 1 , the exported video plays as is with no slow motion effect

    videoCompositionTrack.scaleTimeRange(CMTimeRangeMake(kCMTimeZero , tstDuration), toDuration: CMTimeMake(tstDuration.value * videoScaleFactor, videoDuration.timescale))

    // 3. Replace with 1, unexpected behaviour : video only displays first frame for CMTimeMakes's value then proceeds to play video normally.
    videoCompositionTrack.scaleTimeRange(CMTimeRangeMake(tstStartTime , tstDuration), toDuration: CMTimeMake(tstDuration.value * videoScaleFactor, videoDuration.timescale))



     videoCompositionTrack.preferredTransform = videoAssetSourceTrack.preferredTransform



}catch { print(error) }

asset = comp
}

1 Answers1

1

My guess is that it's working "correctly", but the portion of the video that you are slowing down is much, much smaller than you expect.

CMTime is a very unusual data structure, so it can be very confusing to wrap your head around it. What is the value of videoDuration.timescale that you are using to construct the tstStartTime and tstDuration variables? The larger that timescale value is, the smaller the portion of time that is represented by the CMTime value.

For example, if the timescale is 4, then CMTime(value: 2, timescale: 4) represents 2/4 seconds, or half a second.

For more information, see the documentation for CMTime: https://developer.apple.com/reference/coremedia/1669288-cmtime

Dave Weston
  • 6,527
  • 1
  • 29
  • 44
  • The value of `videoDuration.timescale` is 600. I have a slight idea of what the `timeScale` does , but I have seen code where it's value is `90000` , and that is very confusing , because I don't know why such a high value is necessary. This is from apple's StopNGo sample app `frameDuration = CMTimeMakeWithSeconds = CMTimeMake(1.0/5/0 , 90000)` //5fps - taking five pictures will equal 1 sec of video. –  Jan 23 '17 at 01:34
  • I'm not sure of the specific reason why they chose a timescale of 90000 for that sample, but the reason why CMTime works the way it does is to enable extremely high precision across a wide range with no rounding error. Here's a blog post that attempts to explain it in more depth: https://warrenmoore.net/understanding-cmtime – Dave Weston Jan 23 '17 at 04:41
  • 1
    Thanks for pointing out the problem. My assumption was that `scaleTimeRange` could only be used for the entire video and not a segment but perhaps I need to deepen my understanding of `CMTime`. –  Jan 23 '17 at 05:02