13

I am trying to detect when a device is capable of outputting at 4K UHD (3840x2160) resolution. A number of devices such as the nVidia Shield TV and the Sony Xperia Z5 Premium will report that they are running at 1080p even though they are capable of UHD, because they default to 1080p output for non-video layouts. I need some way to detect if they are 4K capable to distinguish between them and non-4K devices like the Nexus Player.

Here is the code I'm using to determine the current resolution:

WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);

On the Shield TV, this returns 1920x1080 all the time, even when ExoPlayer reports that it's outputting video at 3840x2160.

Cobar
  • 223
  • 2
  • 7

2 Answers2

5

UPDATE: This has been fixed as of ExoPlayer 1.5.1 (see https://github.com/google/ExoPlayer/commit/79055066813123c939c29e5a5e223a5ff043b91e)


I followed up with ExoPlayer's developers and found the answer:

The only way to reliably detect 4K devices is using Device.Mode which is only available in api level 23+. See the Android M note on that here:

https://developer.android.com/preview/api-overview.html#4K-display

And the documentation for the class here:

https://developer.android.com/reference/android/view/Display.Mode.html#getPhysicalWidth()

As for ExoPlayer it does not implement this code, as of the current version (1.4.2) but that will probably change. See:

https://github.com/google/ExoPlayer/issues/800

And finally to answer the question, the right way to detect 4K right now is something like this:

/**
 * Used to check if the connected device support UHD (3840x2160)
 *
 * Note that this call will fail to identify UHD devices on api level bellow 23 (M)
 *
 * @return 1 if device is UHD, 0 otherwise
 */
public int isUHD(){
    Display display = getActivity().getWindowManager().getDefaultDisplay();
    Point displaySize = getDisplaySize(display);
    return (displaySize.x >= 3840 && displaySize.y >= 2160) ? 1 : 0;
}

/**
 * Convenience function that forks to the different ways to obatin the Display size across Android versions
 *
 * @param display Display instance to obtain information from
 *
 * @return Point a Point describing the Display size
 */
private static Point getDisplaySize(Display display) {
    Point displaySize = new Point();
    if(Util.SDK_INT >= 23){
        getDisplaySizeV23(display, displaySize);
    }else if(Util.SDK_INT >= 17) {
        getDisplaySizeV17(display, displaySize);
    } else if(Util.SDK_INT >= 16) {
        getDisplaySizeV16(display, displaySize);
    } else {
        getDisplaySizeV9(display, displaySize);
    }
    return displaySize;
}

@TargetApi(23)
private static void getDisplaySizeV23(Display display, Point outSize){
    Display.Mode[] modes = display.getSupportedModes();
    if(modes.length > 0){
        Display.Mode mode = modes[0];
        outSize.x = mode.getPhysicalWidth();
        outSize.y = mode.getPhysicalHeight();
    }
}

@TargetApi(17)
private static void getDisplaySizeV17(Display display, Point outSize) {
    display.getRealSize(outSize);
}

@TargetApi(16)
private static void getDisplaySizeV16(Display display, Point outSize) {
    display.getSize(outSize);
}

private static void getDisplaySizeV9(Display display, Point outSize) {
    outSize.x = display.getWidth();
    outSize.y = display.getHeight();
}

Which will give wrong results on api less than 23.

  • Thanks for talking with ExoPlayer. There's very few Android TV devices on the market, so I think you can safely default to treating any Android TV device as 4K and then exclude the ones that only support 1080p. The only 1080p devices I'm aware of are the Nexus Player and Razer Forge TV. – Cobar Sep 18 '15 at 20:13
  • I haven't done the research to know if what you say is true, but the problem with doing that is that ExoPlayer doesn't actually render the 4K stream even if you specify it as a source. That's why the issue is reported as a bug on the official project. What will happen (unless you override the faulty functions) is that ExoPlayer will shrink it down to 1920. I hope that will change soon. – Juan Carlos Ospina Gonzalez Sep 20 '15 at 11:14
  • For Sony TVs this pasted function still doesn't work. However the one from the linked ExoPlayer commit works - it has a special detection for Sony Bravia. – LachoTomov Dec 27 '17 at 15:15
1

Should be using DisplayCompat.getSupportedModes as the TV may be running in FHD even if it support UHD. It is possible that UHD is only enabled during video playback.

fun isUhdDevice(context: Context) : Boolean {

    val displayManager = DisplayManagerCompat.getInstance(context)
    val defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY)

    defaultDisplay?.let { display ->
        return DisplayCompat.getSupportedModes(context, display).any { it.physicalHeight >= 2160 && it.physicalWidth >= 3840 }
    }
    return false
}
Fung
  • 961
  • 1
  • 8
  • 17