Search code examples
video-streamingtvoshdr

How to Accurately Detect HDR Playback Capability on Apple TV?


I've been working with the eligibleforhdrplayback property to determine if HDR playback is supported. However, I've noticed an inconsistency. When the Video Format switches from HDR to SDR in the Settings menu on Apple TV, the property still returns true, indicating HDR is playable even when it's not (This seems to contradict what was mentioned around the [20:40] mark of this WWDC video).

I've tried using the eligibleForHDRPlaybackDidChangeNotification and even restarted the app, but I still encounter the same issue.

Are there any alternative approaches to accurately determine if the app can play HDR or else can only play SDR content on an Apple TV?

edit:
The issue is that when I play a stream that contains HDR and the setting for HDR is off on a capable device then AVPlayer will throw an error.

AVFoundationErrorDomain -11868 cannot open

Solution

  • "A way to know if the user selected HDR or SDR in the video format settings."

    Consider using APIs like AVDisplayCriteria and also AVDisplayManager, where for example isDisplayCriteriaMatchingEnabled tells you the user's "Match Content" settings and from such details you could get some hints about whether the device is on HDR or SDR mode.

    If all attempts are failing, then finally see if this quick hack helps you out:

    do { try my_Player.play() }
    catch { print("Could not play as HDR content --> IS Possible SDR content? ...") }
    

    The idea of above solution is that if you do a (sandboxed) Try to "play a stream that contains HDR and the setting for HDR is off on a capable device then AVPlayer will throw an error", so now this caught error becomes the answer itself to knowing whether the device mode is HDR or SDR.

    From research of AVDisplayManager, my understanding is that you need a code flow similar to the one shown below.

    //# assuming you are using:  AVPlayer via AVKit...
    //# assuming you can use:  self.view.window.avDisplayManager 
    //# assuming: AVPlayer loaded the url okay (eg: can get some metadata from it like Width or Height etc)
    

    Below is a flow of instructions you can experiment with (eg: it gives you hints of possible commands to try within your own code. Don't need all. Pick and test pieces that seem useful):

    func attemptTestPlay_HDR() 
    {
        //# (1) Setting up the Player...
        
        //# Setup your AVPlayer with your video content
        let my_vidUrl = URL(string: "https:// ... videofile.mp4")!
        let my_Asset = AVAsset(url: my_vidUrl)
        let my_Item = AVPlayerItem(asset: my_Asset) //# or might be possible to use "url: my_vidUrl" here instead
        
        var my_Player = AVPlayer(playerItem: my_Item)
        //var my_Player = AVPlayer.init(playerItem: my_Item) //# or maybe this way works better???
        
        //# Associate the AVPlayer with the AVPlayerViewController
        var playerViewController = AVPlayerViewController()
        playerViewController.player = my_Player
        playerViewController?.delegate = self
        
        //# (2) Get status of possible HDR / SDR setting... 
        
        playerViewController.appliesPreferredDisplayCriteriaAutomatically = false
        //playerViewController.appliesPreferredDisplayCriteriaAutomatically = true //# or maybe this way is the working trick?
        
        //# Make TV auto-adjust to suit the playing video settings (not user settings)
        //# Might be enough to stop the error on play (since it auto-adjusts)
        let my_displayCriteria = my_Asset.preferredDisplayCriteria
        let displayManager = self.view.window.avDisplayManager 
        displayManager.preferredDisplayCriteria = my_displayCriteria //# makes it auto-adjust to match video type.
        
        //# Check setting of TV --> Video and Audio > "Match Content"
        //# eg: would test to see if the ON/OFF setting affects if HDR can play or not
        let bool_MatchEnabled = displayManager.isDisplayCriteriaMatchingEnabled
        print("isDisplayCriteriaMatchingEnabled : ", bool_MatchEnabled)
        
        //# Finally Attempt to play a video
        my_Player.play()
        
        //### Possible method 02: Do a Try/Catch to "estimate" if using HDR or SDR
        
        //# Attempt to play a video (of HDR content)
        do { try my_Player.play() }
        catch {
                // Finally Assume HDR is not available at current time (eg: is user setting of SDR?)
                print("Could not play as HDR content. TO FIX: Check if the device settings is using SDR...")
                
        }
        
    }
    

    (older version)

    "I've noticed an inconsistency (with using eligibleforhdrplayback).

    When the Video Format switches from HDR to SDR in the Settings menu on Apple TV, the property still returns true, indicating HDR is playable even when it's not"

    If you read carefully in the documentation, they say..

    eligibleForHDRPlayback
    "A Boolean value that indicates whether the current device can present content to an HDR display."

    My understanding of device is: That boolean tells you if the hardware can support HDR playback.
    It does not care about any user settings in the menu, only what the device/chip itself can achieve.

    It will be true because a user setting of SDR does not mean the device itself cannot support HDR.