package com.brentvatne.react; import android.annotation.SuppressLint; import android.media.MediaCodecInfo; import android.media.MediaCodecList; import android.media.MediaDrm; import android.media.MediaFormat; import android.media.UnsupportedSchemeException; import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import java.util.UUID; @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public class VideoDecoderPropertiesModule extends ReactContextBaseJavaModule { ReactApplicationContext reactContext; @NonNull @Override public String getName() { return "VideoDecoderProperties"; } @SuppressLint("ObsoleteSdkInt") @ReactMethod public void getWidevineLevel(Promise p) { int widevineLevel = 0; if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { p.resolve(widevineLevel); return; } final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL); final String WIDEVINE_SECURITY_LEVEL_1 = "L1"; final String WIDEVINE_SECURITY_LEVEL_2 = "L2"; final String WIDEVINE_SECURITY_LEVEL_3 = "L3"; final String SECURITY_LEVEL_PROPERTY = "securityLevel"; String securityProperty = null; try { MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID); securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY); } catch (UnsupportedSchemeException e) { e.printStackTrace(); } if (securityProperty == null) { p.resolve(widevineLevel); return; } switch (securityProperty) { case WIDEVINE_SECURITY_LEVEL_1: { widevineLevel = 1; break; } case WIDEVINE_SECURITY_LEVEL_2: { widevineLevel = 2; break; } case WIDEVINE_SECURITY_LEVEL_3: { widevineLevel = 3; break; } default: { // widevineLevel 0 break; } } p.resolve(widevineLevel); } @SuppressLint("ObsoleteSdkInt") @ReactMethod public void isCodecSupported(String mimeType, int width, int height, Promise p) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { p.resolve("unsupported"); return; } MediaCodecList mRegularCodecs = new MediaCodecList(MediaCodecList.REGULAR_CODECS); MediaFormat format = MediaFormat.createVideoFormat(mimeType, width, height); String codecName = mRegularCodecs.findDecoderForFormat(format); if (codecName == null) { p.resolve("unsupported"); return; } // Fallback for android < 10 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { p.resolve("software"); return; } boolean isHardwareAccelerated = false; for (MediaCodecInfo codecInfo : mRegularCodecs.getCodecInfos()) { if (codecInfo.getName().equalsIgnoreCase(codecName)) { isHardwareAccelerated = codecInfo.isHardwareAccelerated(); break; } } p.resolve(isHardwareAccelerated ? "software" : "hardware"); } @ReactMethod public void isHEVCSupported(Promise p) { isCodecSupported("video/hevc", 1920, 1080, p); } public VideoDecoderPropertiesModule(ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; } }