peertube-live-streaming/encoder/src/main/java/com/pedro/encoder/utils/CodecUtil.java

297 lines
11 KiB
Java
Raw Normal View History

2021-06-22 16:05:02 +02:00
package com.pedro.encoder.utils;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.os.Build;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by pedro on 14/02/18.
*/
public class CodecUtil {
private static final String TAG = "CodecUtil";
public static final String H264_MIME = "video/avc";
public static final String H265_MIME = "video/hevc";
public static final String AAC_MIME = "audio/mp4a-latm";
public enum Force {
FIRST_COMPATIBLE_FOUND, SOFTWARE, HARDWARE
}
public static List<String> showAllCodecsInfo() {
List<MediaCodecInfo> mediaCodecInfoList = getAllCodecs(false);
List<String> infos = new ArrayList<>();
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfoList) {
String info = "----------------\n";
info += "Name: " + mediaCodecInfo.getName() + "\n";
for (String type : mediaCodecInfo.getSupportedTypes()) {
info += "Type: " + type + "\n";
MediaCodecInfo.CodecCapabilities codecCapabilities =
mediaCodecInfo.getCapabilitiesForType(type);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
info += "Max instances: " + codecCapabilities.getMaxSupportedInstances() + "\n";
}
if (mediaCodecInfo.isEncoder()) {
info += "----- Encoder info -----\n";
MediaCodecInfo.EncoderCapabilities encoderCapabilities = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
encoderCapabilities = codecCapabilities.getEncoderCapabilities();
info += "Complexity range: "
+ encoderCapabilities.getComplexityRange().getLower()
+ " - "
+ encoderCapabilities.getComplexityRange().getUpper()
+ "\n";
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
info += "Quality range: "
+ encoderCapabilities.getQualityRange().getLower()
+ " - "
+ encoderCapabilities.getQualityRange().getUpper()
+ "\n";
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
info += "CBR supported: " + encoderCapabilities.isBitrateModeSupported(
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR) + "\n";
info += "VBR supported: " + encoderCapabilities.isBitrateModeSupported(
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR) + "\n";
info += "CQ supported: " + encoderCapabilities.isBitrateModeSupported(
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ) + "\n";
}
info += "----- -----\n";
} else {
info += "----- Decoder info -----\n";
info += "----- -----\n";
}
if (codecCapabilities.colorFormats != null && codecCapabilities.colorFormats.length > 0) {
info += "----- Video info -----\n";
info += "Supported colors: \n";
for (int color : codecCapabilities.colorFormats) info += color + "\n";
for (MediaCodecInfo.CodecProfileLevel profile : codecCapabilities.profileLevels)
info += "Profile: " + profile.profile + ", level: " + profile.level + "\n";
MediaCodecInfo.VideoCapabilities videoCapabilities = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
videoCapabilities = codecCapabilities.getVideoCapabilities();
info += "Bitrate range: "
+ videoCapabilities.getBitrateRange().getLower()
+ " - "
+ videoCapabilities.getBitrateRange().getUpper()
+ "\n";
info += "Frame rate range: "
+ videoCapabilities.getSupportedFrameRates().getLower()
+ " - "
+ videoCapabilities.getSupportedFrameRates().getUpper()
+ "\n";
info += "Width range: "
+ videoCapabilities.getSupportedWidths().getLower()
+ " - "
+ videoCapabilities.getSupportedWidths().getUpper()
+ "\n";
info += "Height range: "
+ videoCapabilities.getSupportedHeights().getLower()
+ " - "
+ videoCapabilities.getSupportedHeights().getUpper()
+ "\n";
}
info += "----- -----\n";
} else {
info += "----- Audio info -----\n";
for (MediaCodecInfo.CodecProfileLevel profile : codecCapabilities.profileLevels)
info += "Profile: " + profile.profile + ", level: " + profile.level + "\n";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
MediaCodecInfo.AudioCapabilities audioCapabilities =
codecCapabilities.getAudioCapabilities();
info += "Bitrate range: "
+ audioCapabilities.getBitrateRange().getLower()
+ " - "
+ audioCapabilities.getBitrateRange().getUpper()
+ "\n";
info += "Channels supported: " + audioCapabilities.getMaxInputChannelCount() + "\n";
try {
if (audioCapabilities.getSupportedSampleRates() != null
&& audioCapabilities.getSupportedSampleRates().length > 0) {
info += "Supported sample rate: \n";
for (int sr : audioCapabilities.getSupportedSampleRates()) info += sr + "\n";
}
} catch (Exception e) {
}
}
info += "----- -----\n";
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
info += "Max instances: " + codecCapabilities.getMaxSupportedInstances() + "\n";
}
}
info += "----------------\n";
infos.add(info);
}
return infos;
}
public static List<MediaCodecInfo> getAllCodecs(boolean filterBroken) {
List<MediaCodecInfo> mediaCodecInfoList = new ArrayList<>();
if (Build.VERSION.SDK_INT >= 21) {
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
MediaCodecInfo[] mediaCodecInfos = mediaCodecList.getCodecInfos();
mediaCodecInfoList.addAll(Arrays.asList(mediaCodecInfos));
} else {
int count = MediaCodecList.getCodecCount();
for (int i = 0; i < count; i++) {
MediaCodecInfo mci = MediaCodecList.getCodecInfoAt(i);
mediaCodecInfoList.add(mci);
}
}
return filterBroken ? filterBrokenCodecs(mediaCodecInfoList) : mediaCodecInfoList;
}
public static List<MediaCodecInfo> getAllHardwareEncoders(String mime) {
List<MediaCodecInfo> mediaCodecInfoList = getAllEncoders(mime);
List<MediaCodecInfo> mediaCodecInfoHardware = new ArrayList<>();
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfoList) {
if (isHardwareAccelerated(mediaCodecInfo)) {
mediaCodecInfoHardware.add(mediaCodecInfo);
}
}
return mediaCodecInfoHardware;
}
public static List<MediaCodecInfo> getAllHardwareDecoders(String mime) {
List<MediaCodecInfo> mediaCodecInfoList = getAllDecoders(mime);
List<MediaCodecInfo> mediaCodecInfoHardware = new ArrayList<>();
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfoList) {
if (isHardwareAccelerated(mediaCodecInfo)) {
mediaCodecInfoHardware.add(mediaCodecInfo);
}
}
return mediaCodecInfoHardware;
}
public static List<MediaCodecInfo> getAllSoftwareEncoders(String mime) {
List<MediaCodecInfo> mediaCodecInfoList = getAllEncoders(mime);
List<MediaCodecInfo> mediaCodecInfoSoftware = new ArrayList<>();
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfoList) {
if (isSoftwareOnly(mediaCodecInfo)) {
mediaCodecInfoSoftware.add(mediaCodecInfo);
}
}
return mediaCodecInfoSoftware;
}
public static List<MediaCodecInfo> getAllSoftwareDecoders(String mime) {
List<MediaCodecInfo> mediaCodecInfoList = getAllDecoders(mime);
List<MediaCodecInfo> mediaCodecInfoSoftware = new ArrayList<>();
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfoList) {
if (isSoftwareOnly(mediaCodecInfo)) {
mediaCodecInfoSoftware.add(mediaCodecInfo);
}
}
return mediaCodecInfoSoftware;
}
/**
* choose the video encoder by mime.
*/
public static List<MediaCodecInfo> getAllEncoders(String mime) {
List<MediaCodecInfo> mediaCodecInfoList = new ArrayList<>();
List<MediaCodecInfo> mediaCodecInfos = getAllCodecs(true);
for (MediaCodecInfo mci : mediaCodecInfos) {
if (!mci.isEncoder()) {
continue;
}
String[] types = mci.getSupportedTypes();
for (String type : types) {
if (type.equalsIgnoreCase(mime)) {
mediaCodecInfoList.add(mci);
}
}
}
return mediaCodecInfoList;
}
/**
* choose the video encoder by mime.
*/
public static List<MediaCodecInfo> getAllDecoders(String mime) {
List<MediaCodecInfo> mediaCodecInfoList = new ArrayList<>();
List<MediaCodecInfo> mediaCodecInfos = getAllCodecs(true);
for (MediaCodecInfo mci : mediaCodecInfos) {
if (mci.isEncoder()) {
continue;
}
String[] types = mci.getSupportedTypes();
for (String type : types) {
if (type.equalsIgnoreCase(mime)) {
mediaCodecInfoList.add(mci);
}
}
}
return mediaCodecInfoList;
}
/* Adapted from google/ExoPlayer
* https://github.com/google/ExoPlayer/commit/48555550d7fcf6953f2382466818c74092b26355
*/
private static boolean isHardwareAccelerated(MediaCodecInfo codecInfo) {
if (Build.VERSION.SDK_INT >= 29) {
return codecInfo.isHardwareAccelerated();
}
// codecInfo.isHardwareAccelerated() != codecInfo.isSoftwareOnly() is not necessarily true.
// However, we assume this to be true as an approximation.
return !isSoftwareOnly(codecInfo);
}
/* Adapted from google/ExoPlayer
* https://github.com/google/ExoPlayer/commit/48555550d7fcf6953f2382466818c74092b26355
*/
private static boolean isSoftwareOnly(MediaCodecInfo mediaCodecInfo) {
if (Build.VERSION.SDK_INT >= 29) {
return mediaCodecInfo.isSoftwareOnly();
}
String name = mediaCodecInfo.getName().toLowerCase();
if (name.startsWith("arc.")) { // App Runtime for Chrome (ARC) codecs
return false;
}
return name.startsWith("omx.google.")
|| name.startsWith("omx.ffmpeg.")
|| (name.startsWith("omx.sec.") && name.contains(".sw."))
|| name.equals("omx.qcom.video.decoder.hevcswvdec")
|| name.startsWith("c2.android.")
|| name.startsWith("c2.google.")
|| (!name.startsWith("omx.") && !name.startsWith("c2."));
}
/**
* Filter broken codecs by name and device model.
*
* Note:
* There is no way to know broken encoders so we will check by name and device.
* Please add your encoder to this method if you detect one.
*
* @param codecs All device codecs
* @return a list without broken codecs
*/
private static List<MediaCodecInfo> filterBrokenCodecs(List<MediaCodecInfo> codecs) {
List<MediaCodecInfo> listFilter = new ArrayList<>();
for (MediaCodecInfo mediaCodecInfo : codecs) {
if (isValid(mediaCodecInfo.getName())) {
listFilter.add(mediaCodecInfo);
}
}
return listFilter;
}
/**
* For now, none broken codec reported.
*/
private static boolean isValid(String name) {
return true;
}
}