AVPlayerItemVideoOutput copyPixelBufferForItemTime 在 iOS 上为特定视频提供了不正确的 CVPixelBufferRef

AVPlayerItemVideoOutput copyPixelBufferForItemTime gives incorrect CVPixelBufferRef on iOS for particular video

您是否遇到过同一视频 copyPixelBufferForItemTime 在 iOS 上不正确的问题?

我有AVPlayerItemVideoOutput,链接到合适的AVPlayerItem。 我调用 copyPixelBufferForItemTime,接收 CVPixelBufferRef,然后从中检索 OpenGL 纹理。

CVPixelBufferRef pb = [_playerVideoOutput copyPixelBufferForItemTime:currentTime itemTimeForDisplay:nil];

为此 sample video CVPixelBufferRef 存在错误:

int bpr = (int)CVPixelBufferGetBytesPerRow(pb);
int width_real = (int)CVPixelBufferGetWidth(pb);
int width_working = (int)CVPixelBufferGetBytesPerRow(pb)/4;

Mac 输出:
bpr = 2400
width_real = 596
width_working = 600

iOS 输出:
bpr = 2432
width_real = 596
width_working = 608

它在 iOS 上的呈现方式:

在 Mac 上的呈现方式:

CVPixelBufferGetPixelFormatType returns BGRA 在两个平台上。

编辑 在 iOS 上创建纹理时,我通过 CVPixelBufferGetBaseAddress 从像素缓冲区读取数据并使用提供的大小 CVPixelBufferGetWidth/CVPixelBufferGetHeight:

- (GLuint)createTextureFromMovieFrame:(CVPixelBufferRef)movieFrame
{
    int bufferWidth = (int) CVPixelBufferGetWidth(movieFrame);
    int bufferHeight = (int) CVPixelBufferGetHeight(movieFrame);

    // Upload to texture
    CVPixelBufferLockBaseAddress(movieFrame, 0);

    CVOpenGLTextureRef texture=0;
    GLuint tex = 0;

#if TARGET_OS_IOS==1
    void * data = CVPixelBufferGetBaseAddress(movieFrame);
    CVReturn err = 0;
    tex = algotest::MyGL::createRGBATexture(bufferWidth, bufferHeight, data, algotest::MyGL::KLinear);

#else
    CVReturn err = CVOpenGLTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                          getGlobalTextureCache(), movieFrame, 0, &texture);
#endif

    CVPixelBufferUnlockBaseAddress(movieFrame, 0);

    return tex;
}

所以width_working只是为了调试。由于它不匹配 width_real,并且既不传递 width_working 也不传递 width_real 都不起作用,我认为这是像素缓冲区的错误。

像素缓冲区在 iOS 和 mac 上都有每行填充像素,大概是出于对齐的原因。不同之处在于 mac CVOpenGLTextureCacheCreateTextureFromImage 函数可以理解这一点,而 iOS createRGBATexture 函数不能,因为没有字节每行参数。

您可以在宽度中包含填充像素,然后再将它们剪掉:

tex = algotest::MyGL::createRGBATexture(CVPixelBufferGetBytesPerRow(movieFrame)/4, bufferHeight, data, algotest::MyGL::KLinear);

或者您可以使用 CVOpenGLESTextureCache,相当于 CVOpenGLTextureCache 的 iOS,并将 createRGBATexture() 替换为 CVOpenGLESTextureCacheCreateTextureFromImage()。那么您的 mac 和 iOS 代码将是相似的并且 iOS 代码甚至可能 运行 更快,因为 iOS 上的纹理缓存可以避免纹理数据的冗余复制。