Split CMTimeRange into multiple CMTimeRange chunks

淺唱寂寞╮ 提交于 2019-12-24 00:24:55

问题


Lets assume I have a CMTimeRange constructed from start time zero, and duration of 40 seconds.

I want to split this CMTimeRange into multiple chunks by a X seconds divider. So the total duration of the chunks will be the same duration as the original duration, and each startTime will reflect the endTime of of the previous chunk. The last chunk will be the modulus of the left over seconds.

For example, for video of 40 seconds, and divider of 15 seconds per chunk:

  1. First CMTimeRange - start time: 0, duration: 15 seconds.
  2. Second CMTimeRange - start time: 15, duration: 15 seconds.
  3. Third CMTimeRange - start time: 30, duration: 10 seconds. (left overs)

What I've tried:

I tried using CMTimeSubtract on the total duration and use the result again, in recursive way untill the CMTime in invalid, But it doesn't seems to work.

Any help will be highly appreciated.

Best Regards, Roi


回答1:


Starting at range.start, create ranges of the given length until range.end is reached:

func splitIntoChunks(range: CMTimeRange, length: CMTime) -> [CMTimeRange] {

    var chunks: [CMTimeRange] = []
    var from = range.start
    while from < range.end {
        chunks.append(CMTimeRange(start: from, duration: length).intersection(range))
        from = from + length
    }

    return chunks
}

intersection is used here to prune the last chunk to the original range.

Alternative solution:

func splitIntoChunks(range: CMTimeRange, length: CMTime) -> [CMTimeRange] {

    return stride(from: range.start.seconds, to: range.end.seconds, by: length.seconds).map {
        CMTimeRange(start: CMTime(seconds: $0, preferredTimescale: length.timescale), duration: length)
            .intersection(range)
    }

}

With a custom extension to make CMTime adopt the Strideable protocol

extension CMTime: Strideable {
    public func distance(to other: CMTime) -> TimeInterval {
        return other - self
    }

    public func advanced(by n: TimeInterval) -> CMTime {
        return self + n
    }
}

this can be further simplified to

func splitIntoChunks(range: CMTimeRange, length: CMTime) -> [CMTimeRange] {

    return stride(from: range.start, to: range.end, by: length.seconds).map {
        CMTimeRange(start: $0, duration: length) .intersection(range)
    }
}

In any case, you'll might want to add a check

precondition(length.seconds > 0, "length must be positive")

to your function, in order to detect invalid calls during development.




回答2:


I too needed to stride CMTime, to deal with AVCaptureDevice exposure durations & showing these to users.

Turns out Martin's answer doesn't work anymore with the changes in Swift 4.x / XCode 10. Here's my version of CMTime conformance to Strideable:

extension CMTime: Strideable {
    public func distance(to other: CMTime) -> TimeInterval {
        return TimeInterval((Double(other.value) / Double(other.timescale)) - (Double(self.value) /  Double(self.timescale)))
    }

    public func advanced(by n: TimeInterval) -> CMTime {
        var retval = self
        retval.value += CMTimeValue(n * TimeInterval(self.timescale))
        return retval
    }
}

I derped with it in a playground and it seems to work.



来源:https://stackoverflow.com/questions/48787869/split-cmtimerange-into-multiple-cmtimerange-chunks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!