Subtitles for AVPlayer/MPMoviePlayerController

Viewed 20728

I am using m3u8 video format for streaming the video and now I need to display subtitles for the same.

I searched in Apple Documentation and found that I can achieve this by using the closedCaptionDisplayEnabled property of AVPlayer.

I am interested to know what should be the format of subtitles? Will the .srt format do?

Also can I achieve the same using MPMoviePlayerController?

Any help is appreciated.

5 Answers

This is the Swift version of @JohannesFahrenkrug answer. Hope this is useful for someone:

    let localVideoAsset = Bundle.main.url(forResource: "L1C1P1C3", withExtension: "mp4")

    //Create AVMutableComposition
    let videoPlusSubtitles = AVMutableComposition()

    //Adds video track
    let videoTrack = videoPlusSubtitles.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)

    try? videoTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, localVideoAsset.duration),
                                of: localVideoAsset.tracks(withMediaType: .video)[0],
                                at: kCMTimeZero)

    //Adds subtitle track
    let subtitleAsset = AVURLAsset(url: Bundle.main.url(forResource: "L1C1P1C3", withExtension: ".mp4.vtt")!)

    let subtitleTrack = videoPlusSubtitles.addMutableTrack(withMediaType: .text, preferredTrackID: kCMPersistentTrackID_Invalid)

    try? subtitleTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, localVideoAsset.duration),
                                        of: subtitleAsset.tracks(withMediaType: .text)[0],
                                        at: kCMTimeZero)

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()
                }
            }
        }
    }
}
Related