使用 NDK MediaCodec 访问解码视频的裁剪边界
Access crop bounds for decoded video using NDK MediaCodec
我正在尝试通过 NDK 的 MediaCodec API 读取裁剪边界。
我正在读取以下值:
AMediaFormat* fmt = initialized();
if (AMediaFormat_getInt32(fmt, "crop-left", &cropLeft)) {
LOGI("crop-left: %d", cropLeft);
}
但是,我无法读取我尝试过的各种视频的 cropLeft 值。我尝试在第一帧之后以及检测到格式更改后读取它。
我在 vlc code 基地看到过类似的代码。
寻找这里可能存在的问题。
你没有在代码中说明 initialized()
是什么。请记住,实际输出 MediaFormat
不会立即可用,但当您从解码器返回 INFO_OUTPUT_FORMAT_CHANGED
时,您应该使用 MediaCodec.getOutputFormat()
获取它。您不能预先获取 MediaFormat
对象并在获取 return 值时从旧对象读取,您需要再次调用 getOutputFormat()
。
目前还无法读取裁剪值。
在幕后,AMediaFormat 包含一个消息结构 (AMessage),它由 stagefright 发送,带有所有裁剪值。 crop 值是特殊类型的 Rect,除了已经在 AMediaFormat 中定义的方法(int32、int64、string、buffer)之外,它还需要一个新的读取方法。
不幸的是,AMediaFormat_toString 方法甚至不处理 Rect 类型。我们可以看到一个输出 "crop: unknown(9)" 而不是裁剪值。
更新:
这是我在 Android 7.0 的 Galaxy S6 上使用的一个丑陋的 hack。
它不能用于生产代码。它仅用于测试和演示。它会在其他 Android 版本和供应商手机上崩溃。
预计结构会在其他 Android 版本中发生变化,因此需要修复偏移量、填充和其他值。
不管怎样,AMediaFormat 是一个结构,它有一个指向 AMessage 的指针。预计这是在前 4 个字节中。 AMessage 是 RefBase 的一个子 class,使用 mainoffset[24],我们将一些字节向前推进到 AMessage class 中的 Items 数组。我们期望 Items 结构的大小为 32 字节,并且它有一个指向 "crop" 字符串的 char 指针。当我们找到裁剪项目时,我们就可以读出裁剪值。
AMediaFormat* format = AMediaCodec_getOutputFormat(_codec);
typedef struct {
char mainoffset[24];
struct {
char itemoffset[8];
int32_t cropLeft;
int32_t cropTop;
int32_t cropRight;
int32_t cropBottom;
unsigned char* name;
char padding[4];
}items[64];
}AMessage;
AMessage* amessage = *((AMessage**)format);
int n = 0;
while ( amessage->items[n].name ) {
if ( !strcmp((const char*)amessage->items[n].name, "crop") ) {
int32_t width = amessage->items[n].cropRight - amessage->items[n].cropLeft + 1;
int32_t height = amessage->items[n].cropBottom - amessage->items[n].cropTop + 1;
__android_log_print(ANDROID_LOG_VERBOSE, "FORMAT", "width=%d height=%d", width, height);
break;
}
n++;
}
我正在尝试通过 NDK 的 MediaCodec API 读取裁剪边界。
我正在读取以下值:
AMediaFormat* fmt = initialized();
if (AMediaFormat_getInt32(fmt, "crop-left", &cropLeft)) {
LOGI("crop-left: %d", cropLeft);
}
但是,我无法读取我尝试过的各种视频的 cropLeft 值。我尝试在第一帧之后以及检测到格式更改后读取它。
我在 vlc code 基地看到过类似的代码。
寻找这里可能存在的问题。
你没有在代码中说明 initialized()
是什么。请记住,实际输出 MediaFormat
不会立即可用,但当您从解码器返回 INFO_OUTPUT_FORMAT_CHANGED
时,您应该使用 MediaCodec.getOutputFormat()
获取它。您不能预先获取 MediaFormat
对象并在获取 return 值时从旧对象读取,您需要再次调用 getOutputFormat()
。
目前还无法读取裁剪值。 在幕后,AMediaFormat 包含一个消息结构 (AMessage),它由 stagefright 发送,带有所有裁剪值。 crop 值是特殊类型的 Rect,除了已经在 AMediaFormat 中定义的方法(int32、int64、string、buffer)之外,它还需要一个新的读取方法。 不幸的是,AMediaFormat_toString 方法甚至不处理 Rect 类型。我们可以看到一个输出 "crop: unknown(9)" 而不是裁剪值。
更新:
这是我在 Android 7.0 的 Galaxy S6 上使用的一个丑陋的 hack。 它不能用于生产代码。它仅用于测试和演示。它会在其他 Android 版本和供应商手机上崩溃。 预计结构会在其他 Android 版本中发生变化,因此需要修复偏移量、填充和其他值。 不管怎样,AMediaFormat 是一个结构,它有一个指向 AMessage 的指针。预计这是在前 4 个字节中。 AMessage 是 RefBase 的一个子 class,使用 mainoffset[24],我们将一些字节向前推进到 AMessage class 中的 Items 数组。我们期望 Items 结构的大小为 32 字节,并且它有一个指向 "crop" 字符串的 char 指针。当我们找到裁剪项目时,我们就可以读出裁剪值。
AMediaFormat* format = AMediaCodec_getOutputFormat(_codec);
typedef struct {
char mainoffset[24];
struct {
char itemoffset[8];
int32_t cropLeft;
int32_t cropTop;
int32_t cropRight;
int32_t cropBottom;
unsigned char* name;
char padding[4];
}items[64];
}AMessage;
AMessage* amessage = *((AMessage**)format);
int n = 0;
while ( amessage->items[n].name ) {
if ( !strcmp((const char*)amessage->items[n].name, "crop") ) {
int32_t width = amessage->items[n].cropRight - amessage->items[n].cropLeft + 1;
int32_t height = amessage->items[n].cropBottom - amessage->items[n].cropTop + 1;
__android_log_print(ANDROID_LOG_VERBOSE, "FORMAT", "width=%d height=%d", width, height);
break;
}
n++;
}