首页 > 解决方案 > 为什么不同设备的 ANativeWindow_Buffer.stride 不同

问题描述

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

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

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

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

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

谁能帮我理解,步幅是如何计算的?错误计算步幅的原因是什么?

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

标签: androidffmpeglibavandroid-ffmpeg

解决方案


似乎因为我的设备是 arm64-v8a,所以步幅总是对齐到 64。为了克服这个问题,我在锁定窗口并使用ANative_WindowBuffer. 然后我用它windowbuffer.stride来计算 dst_slice 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)

这将直接将缩放的帧数据渲染到窗口缓冲区。


推荐阅读