【发布时间】:2016-01-19 14:33:45
【问题描述】:
我有一个项目,我需要从实时网络流中解码 h264 视频,最终得到可以在 iOS 设备上的另一个框架 (Unity3D) 中显示的纹理。我可以使用 VTDecompressionSession 成功解码视频,然后使用 CVMetalTextureCacheCreateTextureFromImage(或 OpenGL 变体)获取纹理。当我使用低延迟编码器并且图像缓冲区按显示顺序出现时效果很好,但是,当我使用常规编码器时,图像缓冲区不会按显示顺序出现并且重新排序图像缓冲区显然要困难得多我期待。
第一次尝试是使用 kVTDecodeFrame_EnableAsynchronousDecompression 和 kVTDecodeFrame_EnableTemporalProcessing 设置 VTDecodeFrameFlags... 但是,事实证明 VTDecompressionSession 可以选择忽略该标志并做任何它想做的事情...在我的情况下,它选择忽略该标志并且仍然以编码器顺序(不是显示顺序)输出缓冲区。基本没用。
下一个尝试是将图像缓冲区与演示时间戳相关联,然后将它们放入一个向量中,这样我就可以在创建纹理时获取所需的图像缓冲区。问题似乎是进入与时间戳相关联的 VTDecompressionSession 的图像缓冲区不再是出来的缓冲区,本质上使时间戳无用。
比如进入解码器...
VTDecodeFrameFlags flags = kVTDecodeFrame_EnableAsynchronousDecompression;
VTDecodeInfoFlags flagOut;
// Presentation time stamp to be passed with the buffer
NSNumber *nsPts = [NSNumber numberWithDouble:pts];
VTDecompressionSessionDecodeFrame(_decompressionSession, sampleBuffer, flags,
(void*)CFBridgingRetain(nsPts), &flagOut);
在回调方面...
void decompressionSessionDecodeFrameCallback(void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef imageBuffer, CMTime presentationTimeStamp, CMTime presentationDuration)
{
// The presentation time stamp...
// No longer seems to be associated with the buffer that it went in with!
NSNumber* pts = CFBridgingRelease(sourceFrameRefCon);
}
排序时,回调端的时间戳以预期速率单调增加,但缓冲区的顺序不正确。有谁看到我在这里犯了错误?或者知道如何确定回调端缓冲区的顺序?在这一点上,我几乎尝试了所有我能想到的。
【问题讨论】:
-
你解决了吗?它让我头疼。
-
我还没有解决这个问题。我敢肯定,如果没有重新排序,包含 B 帧的视频会按照我的预期以高-低-中顺序播放。但是,很明显,视频帧虽然具有高-低-中顺序,但不再与回调中到达的高-低-中presentationTimeStamps 相关联。这会破坏排序,最终会出现奇怪的帧播放顺序。至少我知道我不是唯一一个……
-
我正在查看 XBMC 的实现,他们在回调中有一条注释,内容为“有时帧按解码顺序”以及他们用来重新排序它们的优先级队列。我必须说我对 Video Toolbox API 印象不深,它的文档记录很差,而且这个错误非常糟糕。
-
我假设您使用的是 h264 编码...您使用自己的编码器还是 Apple 编码器?我刚刚使用带有 Apple 编码器的 VTDecompressionSession 编写了一个测试应用程序,它完美地对帧进行了排序。似乎 VTDecompressionSession 不喜欢我正在使用的编码器。 :-/
-
这取决于我认为你有多少 B 和 P 帧。我的内容是由几个不同的编码器生成的,而且看起来确实比其他编码器更糟糕 - 但我认为这是 B/P 帧比。
标签: ios core-video video-toolbox