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

How to create many audio track item with different start time? #33

Open duylinh opened 4 years ago

duylinh commented 4 years ago

I want to build application add multi music to video. I have a video track item and some audio track item. I need to misc some audio track (with different start time) and video. Ref: I have referenced on page --> https://github.com/vitoziv/VideoCat Example: video: ___[=======================] video track audios : [--------===========] audio track 1 ____[-----========] audio track 2 __[=======================--------] audio track 3 ____[-------------=======================---------] audio track 4 Note: ====== : available -------- : unavailable

video: __[=======================] video track audio track 1:[~~~~===========] offsetTime < 0 audio track 2:____~~~~[===========] offsetTime > 0 ==> ~ : offsetTime

     video__:[========================60s===================] total
     result: 60s --> 20s with lowtime 20s, uptime 40s
     trimed_:[[-----20s-----][======20s=====][______20s_____]
     trimed_:[[+++++++++++++++++++++++++++++]
     ==> [----] lowtime
     ==> [+++] up time

   convenience init --> AudioData :
    self.offsetTime = offsetTime
    self.lowTime = lowTime > 0.0 ? lowTime : 0.0
    let upValue = max(upTime, lowTime)
    self.upTime = upValue
    let durationValue = upValue - lowTime
    self.duration = durationValue
    let startTime = lowTime + offsetTime
    self.startTime = startTime
    let distance = min(durationValue, videoDuration)
    let available = distance - startTime
    self.available = available

My code: Video track item:

let asset = AVAsset(url: url) let resource = AVAssetTrackResource(asset: asset) let lowTime = CMTime(seconds: video.lowTime, preferredTimescale: 600) //default lowtime = 0.0 let durationTime = CMTime(seconds: video.duration, preferredTimescale: 600) //default video.duration = total time of video resource.selectedTimeRange = CMTimeRange(start: CMTime.zero, duration: durationTime) let videoTrackItem = TrackItem(resource: resource) videoTrackItem.startTime = lowTime videoTrackItem.videoConfiguration.contentMode = .aspectFill videoTrackItem.audioConfiguration.volume = video.volume

    context.viewModel.addVideoTrackItem(videoTrackItem)
    context.videoView.player.replaceCurrentItem(context.viewModel.playerItem)
    context.timelineView.reload(with: context.viewModel.videoTrackItems)

A Audio track item:

let asset = AVAsset(url: url) let resource = AVAssetTrackResource(asset: asset)

    print("lowtime \(audio.lowTime)") // default low time = 0.0
    print("upTime \(audio.upTime)")
    print("startTime \(audio.startTime)")
    print("available \(audio.available)")
    print("videoDuration \(self.durationTimeOfVideo)")

    let startTime = CMTime(seconds: audio.startTime, preferredTimescale: 600)
    let availableTime = CMTime(seconds: audio.available, preferredTimescale: 600)

    resource.selectedTimeRange = CMTimeRange(start: CMTime.zero, duration: availableTime)
    let trackItem = TrackItem(resource: resource)
    trackItem.startTime = startTime
    trackItem.audioConfiguration.volume = audio.volume

TimelineViewModel

class TimelineManager { static let current = TimelineManager() var timeline = Timeline() }

 class TimelineViewModel {

      // MARK: - Vars
     private(set) var audioTrackItems = [TrackItem]()
     private(set) var videoTrackItems = [TrackItem]()
     private(set) var renderSize: CGSize = CGSize.zero
     private(set) var lut: String = "original_lut"
     private(set) var playerItem = AVPlayerItem(asset: AVComposition())

    func buildTimeline() -> Timeline {
           let timeline = TimelineManager.current.timeline
           reloadTimeline(timeline)
           return timeline
    }

    // MARK: - Add/Replace

    func addVideoTrackItem(_ trackItem: TrackItem) {
           videoTrackItems.append(trackItem)
           reloadPlayerItems()
    }

    func insertTrackItem(_ trackItem: TrackItem, at index: Int) {
         guard audioTrackItems.count >= index else { return }
         audioTrackItems.insert(trackItem, at: index)
         reloadPlayerItems()
    }

   func updateTrackItem(_ trackItem: TrackItem, at index: Int) {
       guard audioTrackItems.count > index else { return }
       audioTrackItems[index] = trackItem
       reloadPlayerItems()
  }

   func removeTrackItem(_ trackItem: TrackItem) {
        guard let index = audioTrackItems.index(of: trackItem) else { return }
        audioTrackItems.remove(at: index)
        reloadPlayerItems()
   }

   func removeTrackItem(at index: Int) {
         guard audioTrackItems.count > index else { return }
         audioTrackItems.remove(at: index)
         reloadPlayerItems()
   }

   func removeAllAudioTrackItems() {
        audioTrackItems.removeAll()
        reloadPlayerItems()
  }

  func removeAll() {
       audioTrackItems.removeAll()
       videoTrackItems.removeAll()
       reloadPlayerItems()
  }

   func reloadPlayerItems() {
          let timeline = TimelineManager.current.timeline
          timeline.renderSize = renderSize
         reloadTimeline(timeline)
         do {
               try Timeline.reloadVideoStartTime(providers: videoTrackItems)
         } catch {
              assert(false, error.localizedDescription)
         }
         build(with: timeline)
     }

   fileprivate func build(with timeline: Timeline) {
         let compositionGenerator = CompositionGenerator(timeline: timeline)
        let playerItem = compositionGenerator.buildPlayerItem()
        self.playerItem = playerItem
   }

   fileprivate func reloadTimeline(_ timeline: Timeline) {
        timeline.videoChannel = videoTrackItems
        timeline.audios = videoTrackItems + audioTrackItems
   }
 extension TrackItem {
       func reloadTimelineDuration() {
            self.duration = self.resource.selectedTimeRange.duration
       }
}

==> Errors: