Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.0k views
in Technique[技术] by (71.8m points)

ios - AVPlayerViewController not displaying captions control

I have an app that plays Vimeo videos in AVPlayer. Previously I played them in AVPlayerLayer and handled switching caption language (or hiding captions) with UI I implemented. Now I've been asked to switch to AVPlayerViewController, but when it plays the same videos its caption language switching UI (basically a little text bubble icon for the languages menu) never appears, and the video always plays with English captions, regardless of language selected in iOS Settings:General.

To create the AVAsset for AVPlayer, I fetch the captions as .vtt files, and create a combined AVMutableComposition with both the video, audio and text tracks using the following code. The videos can be cached locally or streamed so the URL path can be to a remote server or local, but I always download the text tracks so the textTrackPath is always local.

func createCaptionedVideoFrom(_ localVideoAsset: AVAsset, url: URL) -> AVMutableComposition? {
    do {
        let fileName = url.lastPathComponent
        
        guard let tracksRecord = TextTracks.tracksForGroupLesson(self) else {
            return nil // no langauge tracks
        }
        
        // Create resource for combined tracks
        let videoPlusSubtitles = AVMutableComposition()
        
        // Add video track
        guard let existingVideo = localVideoAsset.tracks(withMediaType: .video)[safe: 0] else {
            Logger.error("No video track in local video asset named: (name) fileName: (fileName)")
            return nil
        }
        guard let videoTrack = videoPlusSubtitles.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else {
            Logger.error("Could not create video track in new video named: (name) fileName: (fileName)")
            return nil
        }
        try videoTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: localVideoAsset.duration), of: existingVideo, at: CMTime.zero)
        
        // Add audio track
        guard let existingAudio = localVideoAsset.tracks(withMediaType: .audio)[safe: 0] else {
            Logger.error("No audio track in local video asset named: (name) fileName: (fileName)")
            return nil
        }
        guard let audioTrack = videoPlusSubtitles.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) else {
            Logger.error("Can't create audio track in new video named: (name) fileName: (fileName)")
            return nil
        }
        try audioTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: localVideoAsset.duration), of: existingAudio, at: CMTime.zero)
        
        for textTrack in tracksRecord.tracks {
            // add text track
            guard let textTrackPath = textTrack.cachedLocalPath else {
                Logger.error("Text track: (textTrack) for group lesson: (id) not cached for offline use.")
                return nil
            }
            let subtitleAsset = AVURLAsset(url: URL(fileURLWithPath: textTrackPath))
            guard let existingTrack = subtitleAsset.tracks(withMediaType: .text).first else {
                Logger.error("No text track in local video asset named: (name) fileName: (fileName)")
                return nil
            }
            
            guard let subtitleTrack = videoPlusSubtitles.addMutableTrack(withMediaType: .text, preferredTrackID: kCMPersistentTrackID_Invalid) else {
                Logger.error("Could not create text track in new video named: (name) fileName: (fileName)")
                return nil
            }
            
            subtitleTrack.languageCode = textTrack.language
            subtitleTrack.extendedLanguageTag = textTrack.language
            try subtitleTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: localVideoAsset.duration), of: existingTrack, at: CMTime.zero)
            
            if subtitleTrack.hasMediaCharacteristic(AVMediaCharacteristic.legible) {
                print("New (textTrack.language) is legible")
            }
        }
        return videoPlusSubtitles
    } catch {
        Logger.error("Could not write text track for video lesson: (name), error: (error)")
    }
    return nil
}

func playVideo(_ video: AVAsset) {
    let assetKeys = [
        "playable"
    ]
    
    let playerItem = AVPlayerItem(asset: video,
                                  automaticallyLoadedAssetKeys: assetKeys)
    let videoPlayer = AVPlayer(playerItem: playerItem)
    videoPlayer.allowsExternalPlayback = true
    
    let avPVC = AVPlayerViewController()
    avPVC.player = videoPlayer
    avPVC.showsPlaybackControls = true
    self.present(avPVC, animated: true) {
        self.playerController = avPVC
        videoPlayer.play()
    }
}

The video, audio and text tracks are always created properly, and the text tracks always have the mediaCharacteristic of legible, and videos always have Spanish and English tracks. But weirdly the completed AVMutableComposition has no mediaCharacteristics.

a) videoPlusSubtitles.availableMediaCharacteristicsWithMediaSelectionOptions always returns nil,

b) videoPlusSubtitles.hasMediaCharacteristic(.legible) always returns nil.

So why does AVPlayerViewController not display caption controls for both the English and Spanish tracks if it can see the English captions track?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
等待大神解答

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...