Audio error occurred when adding subtitles. Updated version as below;
Note: It is only possible to insert local items (assets) into an AVMutableComposition, remote items (like HTTP video streams) will not work before iOS 15.
Video and Subtitle files
Inserting an HTTP stream into a AVMutableComposition
Swift 5
func play() {
guard let videoUrl = Bundle.main.url(forResource: "ElephantsDream", withExtension: "mp4")?.absoluteURL,
let subtitleUrl = Bundle.main.url(forResource: "subtitles-en", withExtension: "vtt")?.absoluteURL else {
return
}
let videoAsset = AVURLAsset(url: videoUrl)
let subtitleAsset = AVURLAsset(url: subtitleUrl)
// Create AVMutableComposition object. This object will hold your AVMutableCompositionTrack instances.
let mixComposition = AVMutableComposition()
let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
let subtitleTrack = mixComposition.addMutableTrack(withMediaType: .text, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try videoTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration),
of: videoAsset.tracks(withMediaType: .video).first!,
at: .zero)
try audioTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration),
of: videoAsset.tracks(withMediaType: .audio).first!,
at: .zero)
try subtitleTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: subtitleAsset.duration),
of: subtitleAsset.tracks(withMediaType: .text).first!, at: .zero)
} catch let err {
print(err.localizedDescription)
}
let player = AVPlayer(playerItem: AVPlayerItem(asset: mixComposition))
let vc = AVPlayerViewController()
vc.player = player
present(vc, animated: true) {
vc.player?.play()
}
}
If you want to work with server-side video and subtitles you need to use like this.
iOS 15
func play() {
if #available(iOS 15.0.0, *) {
let videoUrl = URL(string: "https://wwww.acme.com/video/elephantsdream.mov")!
let subtitleUrl = URL(string: "https://wwww.acme.com/subtitle/subtitles-en.vtt")!
let videoAsset = AVURLAsset(url: videoUrl)
let subtitleAsset = AVURLAsset(url: subtitleUrl)
let mixComposition = AVMutableComposition()
let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
let subtitleTrack = mixComposition.addMutableTrack(withMediaType: .text, preferredTrackID: kCMPersistentTrackID_Invalid)
Task {
if let videoTrackItem = try await videoAsset.loadTracks(withMediaType: .video).first {
try videoTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration),
of: videoTrackItem,
at: .zero)
}
if let audioTrackItem = try await videoAsset.loadTracks(withMediaType: .audio).first {
try audioTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration),
of: audioTrackItem,
at: .zero)
}
if let subtitleTrackItem = try await subtitleAsset.loadTracks(withMediaType: .text).first {
try subtitleTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration),
of: subtitleTrackItem,
at: .zero)
}
DispatchQueue.main.async {
let player = AVPlayer(playerItem: AVPlayerItem(asset: mixComposition))
let vc = AVPlayerViewController()
vc.player = player
self.present(vc, animated: true) {
vc.player?.play()
}
}
}
}
}