Search code examples
iosswiftavfoundationavplayeravkit

Subtitles won't display if closed caption is off in system settings


I am trying to get subtitles to display regardless of what the device has set under the accessibilities. Currently, if the device is set to English with closed captioning enabled in settings, English subtitles will play, and if the device is set to Spanish, Spanish subtitles will play. I would like subtitles to play regardless of whether or not closed captioning is on.

I attempted to add this code from apple's documentation, but it did not help. It seems to be reading the options fine, detecting both the Spanish and English subtitles, but I cannot get them to display on the screen.

import UIKit
import AVFoundation
import AVKit

class ViewController : UIViewController
{
    override func loadView()
    {
        let view = UIView()
        view.backgroundColor = .white

        self.view = view
    }

    override func viewDidLoad()
    {
        super.viewDidLoad()
        guard let path = Bundle.main.path(forResource: "Lebron", ofType:"m4v")
        else
        {
            debugPrint("File not Found")
            return
        }

        let player = AVPlayer(url: URL(fileURLWithPath: path))
        player.appliesMediaSelectionCriteriaAutomatically = false

        let asset = AVAsset(url: URL(fileURLWithPath: path))

        for characteristic in asset.availableMediaCharacteristicsWithMediaSelectionOptions {
            print("\(characteristic)")

            // Retrieve the AVMediaSelectionGroup for the specified characteristic.
            if let group = asset.mediaSelectionGroup(forMediaCharacteristic: characteristic) {
                // Print its options.
                for option in group.options {
                    print("  Option: \(option.displayName)")
                }
            }
        }

        if let group = asset.mediaSelectionGroup(forMediaCharacteristic: AVMediaCharacteristic.legible) {
            let locale = Locale(identifier: "en")
            let options =
                AVMediaSelectionGroup.mediaSelectionOptions(from: group.options, with: locale)
            if let option = options.first {
                // Select Spanish-language subtitle option
                player.currentItem!.select(option, in: group)
                print("\(player.currentItem!.select(option, in: group))")
            }
        }

        let playerViewController = AVPlayerViewController()
        playerViewController.player = player

        addChild(playerViewController)
        playerViewController.view.frame = view.frame
        view.addSubview(playerViewController.view)
        playerViewController.didMove(toParent: self)
        playerViewController.player?.seek(to: CMTime(seconds: 0, preferredTimescale: 1))

    }
}

This is what is output to the console

2019-05-30 21:52:21.432911-0400 subtitleTest[6438:1450671] [Accessibility] ****************** Loading GAX Client Bundle ****************
AVMediaCharacteristic(_rawValue: AVMediaCharacteristicAudible)
  Option: Unknown language
AVMediaCharacteristic(_rawValue: AVMediaCharacteristicLegible)
  Option: Spanish
  Option: Spanish Forced
  Option: English
  Option: English Forced
()

The device should display whichever subtitle file matches the input locale, but instead it doesn't display any subtitles at all.


Solution

  • I eventually got this working. This was the code I ended up using. Just need to change LebronTestProdNamed to the name of your video file, and .m4v to the filetype of your video file.

    import UIKit
    import AVFoundation
    import AVKit
    
    class ViewController : UIViewController
    {
        override func loadView()
        {
            let view = UIView()
            view.backgroundColor = .white
    
            self.view = view
        }
        var test = AVPlayerMediaSelectionCriteria()
    
        override func viewDidLoad()
        {
            super.viewDidLoad()
            guard let path = Bundle.main.path(forResource: "LebronTestProdNamed", ofType:"m4v")
                else
            {
                debugPrint("File not Found")
                return
            }
    
            let player = AVPlayer(url: URL(fileURLWithPath: path))
            player.appliesMediaSelectionCriteriaAutomatically = false
    
            let asset = AVAsset(url: URL(fileURLWithPath: path))
            let playerItem = AVPlayerItem(asset: asset)
    
            for characteristic in asset.availableMediaCharacteristicsWithMediaSelectionOptions {
                print("\(characteristic)")
    
                // Retrieve the AVMediaSelectionGroup for the specified characteristic.
                if let group = asset.mediaSelectionGroup(forMediaCharacteristic: characteristic) {
                    // Print its options.
                    for option in group.options {
                        print("  Option: \(option.displayName)")
                    }
                }
            }
    
            // Create a group of the legible AVMediaCharacteristics (Subtitles)
            if let group = asset.mediaSelectionGroup(forMediaCharacteristic: AVMediaCharacteristic.legible) {
                // Set the locale identifier to the value of languageID to select the current language
                let locale = Locale(identifier: "en-EN")
                // Create an option group to hold the options in the group that match the locale
                let options =
                    AVMediaSelectionGroup.mediaSelectionOptions(from: group.options, with: locale)
                // Assign the first option from options to the variable option
                if let option = options.first {
                    // Select the option for the selected locale
                    playerItem.select(option, in: group)
                }
            }
    
    
    
            let playerViewController = AVPlayerViewController()
            playerViewController.player = player
            playerViewController.player?.replaceCurrentItem(with: playerItem)
    
            addChild(playerViewController)
            playerViewController.view.frame = view.frame
            view.addSubview(playerViewController.view)
            playerViewController.didMove(toParent: self)
            playerViewController.player?.seek(to: CMTime(seconds: 0, preferredTimescale: 1))
    
        }
    }