简体   繁体   English

将 CMTimeRange 拆分为多个 CMTimeRange 块

[英]Split CMTimeRange into multiple CMTimeRange chunks

Lets assume I have a CMTimeRange constructed from start time zero, and duration of 40 seconds.让我们假设我有一个CMTimeRange start time和 40 秒duration构建的CMTimeRange

I want to split this CMTimeRange into multiple chunks by a X seconds divider.我想通过X秒分隔符将此CMTimeRange拆分为多个块。 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.所以, total duration大块的将是相同的duration为原始持续时间,以及每个startTime将反映endTime的前一块的。 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:例如,对于40秒的视频,以及每块15秒的分隔符:

  1. First CMTimeRange - start time: 0, duration: 15 seconds.第一个CMTimeRange - 开始时间:0,持续时间:15 秒。
  2. Second CMTimeRange - start time: 15, duration: 15 seconds.第二个CMTimeRange - 开始时间:15,持续时间:15 秒。
  3. Third CMTimeRange - start time: 30, duration: 10 seconds.第三个CMTimeRange - 开始时间:30,持续时间:10 秒。 (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.我尝试在总持续时间上使用CMTimeSubtract并再次使用结果,以递归方式直到 CMTime 无效,但它似乎不起作用。

Any help will be highly appreciated.任何帮助将不胜感激。

Best Regards, Roi最好的问候, Roi

Starting at range.start , create ranges of the given length until range.end is reached:在开始range.start ,创建给定长度的范围,直到range.end达到:

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.在这里使用intersection将最后一个块修剪到原始范围。

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带有自定义扩展,使CMTime采用Strideable协议

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.到您的函数,以便在开发过程中检测无效调用。

I too needed to stride CMTime , to deal with AVCaptureDevice exposure durations & showing these to users.我也需要跨越CMTime来处理AVCaptureDevice曝光持续时间并将这些显示给用户。

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 :原来 Martin 的答案不再适用于 Swift 4.x/XCode 10 中的更改。这是我的CMTime符合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.我在操场上玩弄它,它似乎有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM