为什么 ANativeWindow_Buffer.stride 对于不同的设备是不同的

Why ANativeWindow_Buffer.stride is different for different devices

我正在使用 ffmpeg 为 android 编写简单的视频播放器。以下是我遵循的步骤

  1. 从文件中读取 AVFrame
  2. 使用sws_scale
  3. 将AVFrame转换为RGB565格式
  4. 使用 av_image_copy_to_buffer
  5. 获取缓冲区
  6. 通过将缓冲区复制到 ANativeWindow_Buffer
  7. ,将此缓冲区显示到 SurfaceView

大多数视频都可以正常播放,但分辨率低于 window 的视频存在问题。例如,当我在我的 OnePlus 7T (2206x1080) 上播放 656x480 视频时,视频看起来失真了。同一视频在模拟器上播放良好 (2160x1080)。

当我调试整个管道时,我发现在OP7T上,锁定ANativeWindow后,ANativeWindow_Buffer.stride设置为704而不是656。对于所有正常播放的视频,步幅与宽度相同缓冲区。 Android 模拟器的情况不同。

我做了一些试验,尝试将宽度缩放到 600,然后步幅跳到 640,视频失真。当我将宽度缩放到 640 时,视频垂直显示一半正确。

任何人都可以帮助我理解步幅是如何计算的吗?以及 stride 计算错误的原因是什么?

我在这里发现了一个同样的问题: OP 提到视频适用于 640、1280、1920。

似乎因为我的设备是 arm64-v8a,步幅总是对齐到 64。为了克服这个问题,我在锁定 window 并使用 ANative_WindowBuffer 后得到步幅。然后我用这个 windowbuffer.stride 来计算 dst_slice for sws_scale.

AVFrame dummy;

if ((ret = ANativeWindow_lock(window, &windowBuffer, nullptr)) < 0) {
     log_error("cannot lock window: %d", ret);
} else {
     dummy.data[0] = (uint8_t *) windowBuffer.bits;
     dummy.linesize[0] = windowBuffer.stride * 2 // For RGB565;
}

然后:

sws_scale(renderer->sws_ctx,
                        (const uint8_t* const *) frame->data,
                        frame->linesize,
                        0,
                        codecpar->height,
                        dummy.data,
                        dummy.linesize)

这会直接将缩放后的帧数据渲染到window缓冲区。