使用 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++;
}