VideoFlint / Cabbage

A video composition framework build on top of AVFoundation. It's simple to use and easy to extend.
MIT License
1.52k stars 221 forks source link

关于TrackItem拷贝的问题。About TrackItem Copy. #21

Closed AbySwifter closed 5 years ago

AbySwifter commented 5 years ago

当我通过以下方式去填充音频轨道时,我遇到了以下问题: 1、对resouorce实例不进行深拷贝,所有音频资源的selectTimeRange属性总是会与最后一次循环的设置一致。 2、对resource实例进行深拷贝,音频资源的scaledDuration属性,总是为音频长度。 代码如下:

private func caculateMusicTrack(resource: AVAssetTrackResource, duration: CMTime) -> [TrackItem] {
        Log.out(">> Total VIDEO DURATION: \(duration.seconds)")
        Log.out(">> Music File Duration: \(resource.duration.seconds)")
        let numOfLoops = (duration.seconds - currentMusicStartOffset) / resource.duration.seconds
        let numOfLoopsRoundedUp = numOfLoops.rounded(.up)
        var sumPartsTotals = CMTime.zero
        var endS = CMTime.zero
        var result: [TrackItem] = []
        //Audio Trim
        for i in 0..<Int(numOfLoopsRoundedUp) {
            let mResource = resource.copy() as! AVAssetTrackResource
            Log.out(mResource)
            guard let musicAsset = mResource.asset else {
                continue
            }
            //Audio Trim
            let start = CMTimeMake(value: Int64(0.0 * 600), timescale: 600)
            if i == Int(numOfLoopsRoundedUp) - 1 { //is the last chunk of audio
                let lastChunkTimeFrac = numOfLoops.truncatingRemainder(dividingBy: 1) // ex 1.5 will give 0.5
                let lastChunkTimeSecs = musicAsset.duration.seconds * lastChunkTimeFrac //music from 0 to this value
                endS = CMTimeMake(value: Int64((lastChunkTimeSecs-0.05) * 600), timescale: 600)
            } else {
                endS = CMTimeMake(value: Int64(musicAsset.duration.seconds * 600), timescale: 600)
            }
            let timeOffset = CMTime.init(seconds: currentMusicStartOffset, preferredTimescale: 600)
            if i == 0 {
                let startTime = currentMusicStartOffset < 0 ? start - timeOffset : start
                mResource.selectedTimeRange = CMTimeRange.init(start: startTime, end: endS)
            } else {
                mResource.selectedTimeRange = CMTimeRange(start:start , end: endS)
            }
            mResource.selectedTimeRange = CMTimeRange(start:start , end: endS)
            Log.out("selectedStart:\(mResource.selectedTimeRange.start.seconds) - totalPart:\(mResource.selectedTimeRange.end.seconds)")
            let partMyTrackItem = TrackItem(resource: mResource)
            let zeroOffsetTime = CMTimeMultiply(musicAsset.duration, multiplier: Int32(i))
            if i == 0 {
                 partMyTrackItem.startTime = zeroOffsetTime + (currentMusicStartOffset < 0 ? CMTime.zero : timeOffset)
            } else {
                partMyTrackItem.startTime = zeroOffsetTime + timeOffset
            }
            partMyTrackItem.startTime = zeroOffsetTime
            Log.out("start:\(partMyTrackItem.startTime.seconds) - totalPart:\(mResource.scaledDuration.seconds)")
            sumPartsTotals = CMTimeAdd(sumPartsTotals, mResource.scaledDuration)
            result.append(partMyTrackItem)
        }
        return result
    }
AbySwifter commented 5 years ago

最终,我通过如下方式解决了问题: 源代码: AVAssetTrackResource.swift line:96

// MARK: - NSCopying
    override public func copy(with zone: NSZone? = nil) -> Any {
        let resource = super.copy(with: zone) as! AVAssetTrackResource
        resource.asset = asset
        return resource
    }

修改后的代码:

// MARK: - NSCopying
    override public func copy(with zone: NSZone? = nil) -> Any {
        let resource = super.copy(with: zone) as! AVAssetTrackResource
        resource.asset = asset?.copy() as? AVAsset
        resource.scaledDuration = CMTime.invalid
        return resource
    }
AbySwifter commented 5 years ago

这里的疑问是关于resource.scaledDuration属性,何时应该将其值赋为 invalid?

vitoziv commented 5 years ago

如果期望视频选择时间就是时间线上的时间那么 resource.scaledDuration 就应该为 invalid。 如果视频选择的时间需要做变速效果就需要设置 scaledDuration。 如果设置了 scaledDuration 之后要记得已经设置了这个,无论之后视频选择了多少视频时间都会变成 scaledDuration,所以要同步修改选择时间和 scaledDuration。看业务需求