2026-05-31 20:09:02 +08:00
|
|
|
#include "codec.h"
|
|
|
|
|
|
2026-09-18 20:24:43 +08:00
|
|
|
#include "common/android/api.h"
|
2026-05-31 20:09:02 +08:00
|
|
|
#include "common/strutil.h"
|
|
|
|
|
#include <media/NdkMediaCodec.h>
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
|
const char* mime;
|
|
|
|
|
FFCodecType type;
|
|
|
|
|
} FF_CODEC_MIME_TO_TYPE[] = {
|
|
|
|
|
{ "video/3gpp", FF_CODEC_TYPE_H263 },
|
|
|
|
|
{ "video/h263", FF_CODEC_TYPE_H263 },
|
|
|
|
|
{ "video/mjpeg", FF_CODEC_TYPE_MJPEG },
|
|
|
|
|
{ "video/mpeg2", FF_CODEC_TYPE_MPEG2 },
|
|
|
|
|
{ "video/mp2v-es", FF_CODEC_TYPE_MPEG2 },
|
|
|
|
|
{ "video/mp4v-es", FF_CODEC_TYPE_DIVX_XVID },
|
|
|
|
|
{ "video/avc", FF_CODEC_TYPE_H264 },
|
|
|
|
|
{ "video/hevc", FF_CODEC_TYPE_HEVC },
|
|
|
|
|
{ "video/x-vnd.on2.vp8", FF_CODEC_TYPE_VP8 },
|
|
|
|
|
{ "video/x-vnd.on2.vp9", FF_CODEC_TYPE_VP9 },
|
|
|
|
|
{ "video/av01", FF_CODEC_TYPE_AV1 },
|
|
|
|
|
{ "video/vvc", FF_CODEC_TYPE_VVC },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool ffCodecIsLikelySoftware(const char* codecName) {
|
|
|
|
|
if (!codecName) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ffStrStartsWith(codecName, "OMX.google.") ||
|
|
|
|
|
ffStrStartsWith(codecName, "c2.android.") ||
|
|
|
|
|
ffStrStartsWith(codecName, "OMX.ffmpeg.") ||
|
|
|
|
|
ffStrStartsWith(codecName, "OMX.PV.");
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-18 01:10:18 +08:00
|
|
|
// Only the name query is newer than the API level this build targets: AMediaCodec_getName and
|
|
|
|
|
// AMediaCodec_releaseName are API 28, while creating and deleting a codec is API 21.
|
|
|
|
|
FF_REQUIRES_API(28) static bool ffCodecIsHardwareAccelerated(AMediaCodec* codec) {
|
2026-05-31 20:09:02 +08:00
|
|
|
if (!codec) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
char* codecName = nullptr;
|
2026-09-18 01:10:18 +08:00
|
|
|
media_status_t status = AMediaCodec_getName(codec, &codecName);
|
2026-05-31 20:09:02 +08:00
|
|
|
if (status != AMEDIA_OK || !codecName) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isHardware = !ffCodecIsLikelySoftware(codecName);
|
2026-09-18 01:10:18 +08:00
|
|
|
AMediaCodec_releaseName(codec, codecName);
|
2026-05-31 20:09:02 +08:00
|
|
|
return isHardware;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-18 01:10:18 +08:00
|
|
|
FF_REQUIRES_API(28) static const char* ffDetectCodecNativeImpl(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/) {
|
2026-05-31 20:09:02 +08:00
|
|
|
FFCodecType decoders = FF_CODEC_TYPE_NONE;
|
|
|
|
|
FFCodecType encoders = FF_CODEC_TYPE_NONE;
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < ARRAY_SIZE(FF_CODEC_MIME_TO_TYPE); ++i) {
|
|
|
|
|
const char* mime = FF_CODEC_MIME_TO_TYPE[i].mime;
|
|
|
|
|
FFCodecType type = FF_CODEC_MIME_TO_TYPE[i].type;
|
|
|
|
|
|
2026-06-01 19:16:53 +08:00
|
|
|
if ((options->showType & FF_CODEC_SHOW_TYPE_DECODER) && !(decoders & type)) {
|
2026-09-18 01:10:18 +08:00
|
|
|
AMediaCodec* decoder = AMediaCodec_createDecoderByType(mime);
|
2026-06-01 19:16:53 +08:00
|
|
|
if (decoder) {
|
2026-09-18 01:10:18 +08:00
|
|
|
if (ffCodecIsHardwareAccelerated(decoder)) {
|
2026-06-01 19:16:53 +08:00
|
|
|
decoders |= type;
|
|
|
|
|
}
|
2026-09-18 01:10:18 +08:00
|
|
|
AMediaCodec_delete(decoder);
|
2026-05-31 20:09:02 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-01 19:16:53 +08:00
|
|
|
if ((options->showType & FF_CODEC_SHOW_TYPE_ENCODER) && !(encoders & type)) {
|
2026-09-18 01:10:18 +08:00
|
|
|
AMediaCodec* encoder = AMediaCodec_createEncoderByType(mime);
|
2026-06-01 19:16:53 +08:00
|
|
|
if (encoder) {
|
2026-09-18 01:10:18 +08:00
|
|
|
if (ffCodecIsHardwareAccelerated(encoder)) {
|
2026-06-01 19:16:53 +08:00
|
|
|
encoders |= type;
|
|
|
|
|
}
|
2026-09-18 01:10:18 +08:00
|
|
|
AMediaCodec_delete(encoder);
|
2026-05-31 20:09:02 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (decoders != FF_CODEC_TYPE_NONE || encoders != FF_CODEC_TYPE_NONE) {
|
|
|
|
|
FFCodecResult* item = FF_LIST_ADD(FFCodecResult, *result);
|
|
|
|
|
ffStrbufInitStatic(&item->gpu, "Default");
|
|
|
|
|
item->decoders = decoders;
|
|
|
|
|
item->encoders = encoders;
|
|
|
|
|
item->platformApi = "AMediaCodec";
|
|
|
|
|
}
|
2026-05-31 23:31:45 +08:00
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
return nullptr;
|
2026-05-31 20:09:02 +08:00
|
|
|
}
|
2026-09-18 01:10:18 +08:00
|
|
|
|
|
|
|
|
const char* ffDetectCodecNative(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/) {
|
|
|
|
|
if (FF_API_AT_LEAST(28)) {
|
|
|
|
|
return ffDetectCodecNativeImpl(options, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reading the codec name is the only way to tell a hardware codec from a software one. Without
|
|
|
|
|
// it there is nothing to report, and listing the codecs as if they were accelerated would be a
|
|
|
|
|
// guess, so say why instead.
|
|
|
|
|
return "AMediaCodec_getName() requires Android 9 (API 28)";
|
|
|
|
|
}
|