#include "media/base/android/media_codec_util.h"
#include <stddef.h>
#include <vector>
#include "base/android/android_info.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "media/base/android/media_codec_bridge.h"
#include "media/base/video_codecs.h"
#include "third_party/re2/src/re2/re2.h"
#include "url/gurl.h"
#include "media/base/android/media_jni_headers/CodecProfileLevelList_jni.h"
#include "media/base/android/media_jni_headers/MediaCodecUtil_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertUTF8ToJavaString;
using base::android::ScopedJavaLocalRef;
namespace media {
namespace {
constexpr char kMp3MimeType[] = "audio/mpeg";
constexpr char kAacMimeType[] = "audio/mp4a-latm";
constexpr char kOpusMimeType[] = "audio/opus";
constexpr char kVorbisMimeType[] = "audio/vorbis";
constexpr char kFLACMimeType[] = "audio/flac";
constexpr char kAc3MimeType[] = "audio/ac3";
constexpr char kEac3MimeType[] = "audio/eac3";
constexpr char kBitstreamAudioMimeType[] = "audio/raw";
constexpr char kAvcMimeType[] = "video/avc";
constexpr char kDolbyVisionMimeType[] = "video/dolby-vision";
constexpr char kHevcMimeType[] = "video/hevc";
constexpr char kVp8MimeType[] = "video/x-vnd.on2.vp8";
constexpr char kVp9MimeType[] = "video/x-vnd.on2.vp9";
constexpr char kAv1MimeType[] = "video/av01";
constexpr char kDtsMimeType[] = "audio/vnd.dts";
constexpr char kDtseMimeType[] = "audio/vnd.dts;profile=lbr";
constexpr char kDtsxP2MimeType[] = "audio/vnd.dts.uhd;profile=p2";
}
static CodecProfileLevel MediaCodecProfileLevelToChromiumProfileLevel(
JNIEnv* env,
const base::android::JavaRef<jobject>& j_codec_profile_level) {
VideoCodec codec = static_cast<VideoCodec>(
Java_CodecProfileLevelAdapter_getCodec(env, j_codec_profile_level));
VideoCodecProfile profile = static_cast<VideoCodecProfile>(
Java_CodecProfileLevelAdapter_getProfile(env, j_codec_profile_level));
auto level = static_cast<VideoCodecLevel>(
Java_CodecProfileLevelAdapter_getLevel(env, j_codec_profile_level));
return {codec, profile, level};
}
static bool IsDecoderSupportedByDevice(std::string_view android_mime_type) {
if (android_mime_type == kVp8MimeType) {
std::string hardware = base::SysInfo::GetAndroidBuildID();
if (hardware.starts_with("mt") &&
base::android::android_info::sdk_int() <
base::android::android_info::SDK_VERSION_T) {
return hardware.starts_with("mt5599") || hardware.starts_with("mt5895") ||
hardware.starts_with("mt8768") || hardware.starts_with("mt8696") ||
hardware.starts_with("mt5887");
}
}
return true;
}
static jboolean JNI_MediaCodecUtil_IsDecoderSupportedForDevice(
JNIEnv* env,
std::string& mime_type) {
return IsDecoderSupportedByDevice(mime_type);
}
static bool CanDecodeInternal(const std::string& mime, bool is_secure) {
if (mime.empty())
return false;
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime);
return Java_MediaCodecUtil_canDecode(env, j_mime, is_secure);
}
std::string MediaCodecUtil::CodecToAndroidMimeType(AudioCodec codec) {
return CodecToAndroidMimeType(codec, kUnknownSampleFormat);
}
std::string MediaCodecUtil::CodecToAndroidMimeType(AudioCodec codec,
SampleFormat sample_format) {
if (sample_format == kSampleFormatDts ||
sample_format == kSampleFormatDtsxP2 ||
sample_format == kSampleFormatAc3 || sample_format == kSampleFormatEac3 ||
sample_format == kSampleFormatMpegHAudio) {
return kBitstreamAudioMimeType;
}
switch (codec) {
case AudioCodec::kMP3:
return kMp3MimeType;
case AudioCodec::kVorbis:
return kVorbisMimeType;
case AudioCodec::kFLAC:
return kFLACMimeType;
case AudioCodec::kOpus:
return kOpusMimeType;
case AudioCodec::kAAC:
return kAacMimeType;
case AudioCodec::kAC3:
return kAc3MimeType;
case AudioCodec::kEAC3:
return kEac3MimeType;
case AudioCodec::kDTS:
return kDtsMimeType;
case AudioCodec::kDTSE:
return kDtseMimeType;
case AudioCodec::kDTSXP2:
return kDtsxP2MimeType;
default:
return std::string();
}
}
std::string MediaCodecUtil::CodecToAndroidMimeType(VideoCodec codec) {
switch (codec) {
case VideoCodec::kH264:
return kAvcMimeType;
case VideoCodec::kHEVC:
return kHevcMimeType;
case VideoCodec::kVP8:
return kVp8MimeType;
case VideoCodec::kVP9:
return kVp9MimeType;
case VideoCodec::kDolbyVision:
return kDolbyVisionMimeType;
case VideoCodec::kAV1:
return kAv1MimeType;
default:
return std::string();
}
}
bool MediaCodecUtil::IsVp8DecoderAvailable() {
return IsDecoderSupportedByDevice(kVp8MimeType);
}
std::optional<gfx::Size> MediaCodecUtil::LookupCodedSizeAlignment(
std::string_view name,
std::optional<int> host_sdk_int) {
struct CodecAlignment {
const char* name_regex;
gfx::Size alignment;
int sdk_int = base::android::android_info::SDK_VERSION_Q;
};
using base::android::android_info::SDK_VERSION_Q;
using base::android::android_info::SDK_VERSION_R;
using base::android::android_info::SDK_VERSION_Sv2;
using base::android::android_info::SDK_VERSION_U;
constexpr CodecAlignment kCodecAlignmentMap[] = {
{"c2.android.avc", gfx::Size(128, 2), SDK_VERSION_Sv2},
{"c2.android.avc", gfx::Size(32, 2), SDK_VERSION_R},
{"c2.android.avc", gfx::Size(64, 2)},
{"c2.android.hevc", gfx::Size(128, 2), SDK_VERSION_Sv2},
{"c2.android.hevc", gfx::Size(32, 2), SDK_VERSION_R},
{"c2.android.hevc", gfx::Size(64, 2)},
{"c2.android.(vp8|vp9|av1)", gfx::Size(16, 2)},
{"omx.google.(h264|hevc|vp8|vp9)", gfx::Size(2, 2)},
{"c2.google.av1", gfx::Size(64, 16), SDK_VERSION_U},
{"c2.google.av1", gfx::Size(64, 8)},
{"c2.qti.(avc|vp8)", gfx::Size(16, 16)},
{"c2.qti.(hevc|vp9)", gfx::Size(8, 8)},
{"omx.qcom.video.decoder.avc", gfx::Size(16, 16)},
{"omx.qcom.video.decoder.hevc", gfx::Size(8, 8)},
{"omx.qcom.video.decoder.vp8", gfx::Size(16, 16), SDK_VERSION_R},
{"omx.qcom.video.decoder.vp8", gfx::Size(1, 1)},
{"omx.qcom.video.decoder.vp9", gfx::Size(8, 8), SDK_VERSION_R},
{"omx.qcom.video.decoder.vp9", gfx::Size(1, 1)},
{"(omx|c2).exynos.h264", gfx::Size(16, 16)},
{"(omx|c2).exynos.hevc", gfx::Size(8, 8)},
{"(omx|c2).exynos.(vp8|vp9)", gfx::Size(1, 1)},
{"omx.sprd.(h264|vpx)", gfx::Size(16, 16)},
{"omx.sprd.(hevc|vp9)", gfx::Size(64, 64)},
};
const auto lower_name = base::ToLowerASCII(name);
const auto sdk_int =
host_sdk_int.value_or(base::android::android_info::sdk_int());
for (const auto& entry : kCodecAlignmentMap) {
if (sdk_int >= entry.sdk_int &&
RE2::PartialMatch(lower_name, entry.name_regex)) {
return entry.alignment;
}
}
return std::nullopt;
}
bool MediaCodecUtil::CanDecode(VideoCodec codec, bool is_secure) {
return CanDecodeInternal(CodecToAndroidMimeType(codec), is_secure);
}
bool MediaCodecUtil::CanDecode(AudioCodec codec) {
return CanDecodeInternal(CodecToAndroidMimeType(codec), false);
}
void MediaCodecUtil::AddSupportedCodecProfileLevels(
std::vector<CodecProfileLevel>* result) {
DCHECK(result);
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobjectArray> j_codec_profile_levels(
Java_MediaCodecUtil_getSupportedCodecProfileLevels(env));
for (auto java_codec_profile_level :
j_codec_profile_levels.ReadElements<jobject>()) {
result->push_back(MediaCodecProfileLevelToChromiumProfileLevel(
env, java_codec_profile_level));
}
}
bool MediaCodecUtil::IsKnownUnaccelerated(VideoCodec codec,
MediaCodecDirection direction) {
auto* env = AttachCurrentThread();
auto j_mime = ConvertUTF8ToJavaString(env, CodecToAndroidMimeType(codec));
auto j_codec_name = Java_MediaCodecUtil_getDefaultCodecName(
env, j_mime, static_cast<int>(direction), false,
true);
auto codec_name =
base::android::ConvertJavaStringToUTF8(env, j_codec_name.obj());
DVLOG(1) << __func__ << "Default hardware codec for " << GetCodecName(codec)
<< " : " << codec_name
<< ", direction: " << static_cast<int>(direction);
return codec_name.empty();
}
}
DEFINE_JNI(CodecProfileLevelList)
DEFINE_JNI(MediaCodecUtil)