在弄清楚如何使用Apple的硬件加速视频框架来解压缩H.264视频流时,我遇到了很多麻烦.几个星期后,我想出来,想分享一个广泛的例子,因为我找不到一个.
我的目标是提供WWDC '14会议513中介绍的Video Toolbox的全面,有启发性的示例.我的代码将无法编译或运行,因为它需要与基本H.264流集成(如从文件读取视频或从在线等流式传输),并且需要根据具体情况进行调整.
我应该提一下,除了我在谷歌搜索主题时学到的东西,我对视频/解码的经验很少.我不知道有关视频格式,参数结构等的所有细节,所以我只包括我认为你需要知道的内容.
我正在使用XCode 6.2并已部署到运行iOS 8.1和8.2的iOS设备.
由于VideoToolbox不适用于tvOS,我该如何解码视频?
我有一个应用程序,我在内存中有h.264的帧(通过网络流),我之前使用VideoToolbox处理解码.什么是替代品?
所以这是一个更理论化的问题/讨论,因为我无法从网上找到其他SO帖子和来源的明确答案.似乎有很多选择:
如果我想在iOS上为H.264(mov)文件进行硬件解码,我可以简单地使用AVFoundation和AVAssets,还是应该使用VideoToolbox(或任何其他框架).使用这些时,如何在运行项目时分析/基准测试硬件性能? - 只是通过简单地查看XCode中"Debug Navigator"中的CPU使用情况?
简而言之,我基本上是在询问AVFoundation和AVAssets是否执行硬件编码?它们是否足够,我如何对实际性能进行基准测试?
谢谢!
我有CMSampleBufferRef(s),我使用VTDecompressionSessionDecodeFrame解码,在帧的解码完成后导致CVImageBufferRef,所以我的问题是......
在UIView中显示这些CVImageBufferRefs最有效的方法是什么?
我已经成功地CVImageBufferRef转换为CGImageRef和显示那些由设置CGImageRef作为CALayer的内容但随后DecompressionSession已配置@ {(ID)kCVPixelBufferPixelFormatTypeKey:[NSNumber的numberWithInt:kCVPixelFormatType_32BGRA]};
这是示例/代码我如何将CVImageBufferRef转换为CGImageRef(注意:cvpixelbuffer数据必须是32BGRA格式才能使用)
CVPixelBufferLockBaseAddress(cvImageBuffer,0);
// get image properties
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(cvImageBuffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(cvImageBuffer);
size_t width = CVPixelBufferGetWidth(cvImageBuffer);
size_t height = CVPixelBufferGetHeight(cvImageBuffer);
/*Create a CGImageRef from the CVImageBufferRef*/
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef cgContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGImageRef cgImage = CGBitmapContextCreateImage(cgContext);
// release context and colorspace
CGContextRelease(cgContext);
CGColorSpaceRelease(colorSpace);
// now CGImageRef can be displayed either by setting CALayer content
// or by creating a [UIImage withCGImage:geImage] that can …
Run Code Online (Sandbox Code Playgroud) 我有一个项目,我需要从实时网络流解码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);
Run Code Online (Sandbox Code Playgroud)
在回调方......
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);
}
Run Code Online (Sandbox Code Playgroud)
订购时,回调端的时间戳按预期速率单调增加,但缓冲区的顺序不正确.有没有人在这里看到我在哪里犯错误?或者知道如何确定回调端缓冲区的顺序?在这一点上,我已经尝试了我能想到的一切.
我正在尝试使用屏幕上的数据创建 H.264 压缩会话。我创建了一个CGDisplayStreamRef
这样的实例:
displayStream = CGDisplayStreamCreateWithDispatchQueue(0, 100, 100, k32BGRAPixelFormat, nil, self.screenCaptureQueue, ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
//Call encoding session here
});
Run Code Online (Sandbox Code Playgroud)
以下是我目前如何设置编码功能:
- (void) encode:(CMSampleBufferRef )sampleBuffer {
CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
CMTime presentationTimeStamp = CMTimeMake(frameID++, 1000);
VTEncodeInfoFlags flags;
OSStatus statusCode = VTCompressionSessionEncodeFrame(EncodingSession,
imageBuffer,
presentationTimeStamp,
kCMTimeInvalid,
NULL, NULL, &flags);
if (statusCode != noErr) {
NSLog(@"H264: VTCompressionSessionEncodeFrame failed with %d", (int)statusCode);
VTCompressionSessionInvalidate(EncodingSession);
CFRelease(EncodingSession);
EncodingSession = NULL;
return;
}
NSLog(@"H264: VTCompressionSessionEncodeFrame Success");
}
Run Code Online (Sandbox Code Playgroud)
我试图了解如何将屏幕上的数据转换为 ,CMSampleBufferRef
以便我可以正确调用我的编码功能。到目前为止,我无法确定这是否可行,或者我正在尝试做的事情的正确方法。有没有人有什么建议? …
我正在使用AVSampleBufferDisplayLayer
解码和显示从服务器流式传输的H.264视频。当我的应用进入后台并返回到前台时,解码过程就会搞砸并且AVSampleBufferDisplayLayer
失败。我看到的错误是:
H.264 decoding layer has failed: Error Domain=AVFoundationErrorDomain
Code=-11847 "Operation Interrupted" UserInfo=0x17426c500
{NSUnderlyingError=0x17805fe90 "The operation couldn’t be completed.
(OSStatus error -12084.)",
NSLocalizedRecoverySuggestion=Stop other operations and try again.,
NSLocalizedDescription=Operation Interrupted}
Run Code Online (Sandbox Code Playgroud)
还有其他人遇到这样的问题AVSampleBufferDisplayLayer
吗?这是什么意思?
AVSampleBufferDisplayLayer
收到错误后,我尝试销毁并创建一个新错误,但是随后我开始从H.264解码器收到其他错误:
Error Domain=AVFoundationErrorDomain Code=-11821 "Cannot Decode"
UserInfo=0x1740e9700 {AVErrorMediaSubTypeKey=(1635148593),
NSLocalizedFailureReason=The media data could not be decoded. It may be damaged.,
NSUnderlyingError=0x174247680 "The operation couldn’t be completed. (OSStatus error -12909.)",
AVErrorMediaTypeKey=vide,
AVErrorPresentationTimeStampKey=CMTime: {7/30 = 0.233},
NSLocalizedDescription=Cannot Decode}
Run Code Online (Sandbox Code Playgroud)
在AVSampleBufferDisplayLayer
失败之前,我没有收到任何这些错误。
我可以使用视频工具箱框架将从相机设备捕获的视频压缩为h264格式,但是当我尝试在VLC播放器中播放该h264文件时,我无法听到视频的音频.我认为音频压缩也应该在代码中完成.
但是我怎么没找到任何资源?
我编写了一个屏幕录制应用程序,它使用 VideoToolbox 和 AVWriter 写出 H.264 电影文件。与原始屏幕相比,录制文件中的颜色有点暗淡。我知道这是因为 colorProfile 未存储在视频文件中。
这与如何对 AVAssetWriter 输出进行颜色管理密切相关
我创建了一个测试台来在 github ScreenRecordTest上展示这一点
如果您运行此应用程序,您可以使用 CMD-R 开始录制并使用相同的 CMD-R 停止录制(您必须开始和停止录制一次才能获得完整写入的电影文件)。您将在 /tmp/ 文件夹中找到类似以下名称的录音:“/tmp/grab-2018-10-25 09:23:32 +0000.mov”
录制时,应用程序会显示两个实时图像:a)从 CGDisplayStream 获取的帧 - 和 - b)来自压缩器的 cmSampleBuffer。
我发现从 CGDisplayStream 返回的 IOSurface 没有进行颜色管理,因此您会注意到压缩之前已经出现“暗淡”颜色。如果您取消 AppDelegate.swift 中第 89 行的注释
// cgImage = cgImage.copy(colorSpace: screenColorSpace)!
Run Code Online (Sandbox Code Playgroud)
此实时预览将具有正确的颜色。现在这仅用于显示压缩前的 IOSurface。我不知道如何使其他实时预览(压缩后)(AppDelegate 中的第 69 行)显示正确的颜色(例如:如何将 colorProfile 应用于 CMSampleBuffer),或者最重要的是如何使用正确的配置文件,以便在打开 .mov 文件时我在播放时获得正确的颜色。
我正在使用 ffmpeg 4.3.1 将视频从 h264 转换为 h265,最初我很高兴地发现我可以使用我的 Mac 的 GPU 来加速转换,并带有 hevc_videotoolbox 标志。
我的 Mac 硬件是第 10 代 Intel i5 和 AMD Radeon Pro 5300
我正在使用这个命令:
ffmpeg -i input_h264.mp4 -c:v hevc_videotoolbox -b:v 6000K -c:a copy -crf 19 -preset veryslow output_h265.mp4
Run Code Online (Sandbox Code Playgroud)
转换速度从 0.75 倍提高到 4 倍,几乎提高了 500%!
但后来我注意到大文件和稍微模糊的结果。然后我注意到更改 crf 或预设没有区别,ffmpeg 似乎忽略了这些设置。唯一似乎有效的设置是视频比特率 (-b:v)。
所以我开始在谷歌上搜索,看看如何才能获得更好的结果。
但是除了这里和那里的一些帖子之外,我大部分时间都是空白的。
我在哪里可以获得有关如何使用 hevc_videotoolbox 获得更好结果的文档?如何找出哪些设置有效,哪些设置被忽略?
我正在努力将我们的应用程序从一些专有的编解码器移动到iOS本机h264编码器(VideoToolbox.framework)并且有疑问:
是否存在为压缩数据设置比特率或数据速率的方法?
以下是我创建编码器会话的方法:
CFMutableDictionaryRef sessionAttributes = CFDictionaryCreateMutable(
NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
//** bitrate
int fixedBitrate = bitrate; // 2000 * 1024 -> assume 2 Mbits/s
CFNumberRef bitrateNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &fixedBitrate);
CFDictionarySetValue(sessionAttributes, kVTCompressionPropertyKey_AverageBitRate, bitrateNum);
CFRelease(bitrateNum);
CFDictionarySetValue(sessionAttributes, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_High_AutoLevel);
CFDictionarySetValue(sessionAttributes, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);
OSStatus error = VTCompressionSessionCreate(kCFAllocatorDefault,
width,
height,
kCMVideoCodecType_H264,
sessionAttributes,
NULL,
kCFAllocatorDefault,
&EncoderCallback,
this, *outputCallbackRefCon,
&m_EncoderSession);
Run Code Online (Sandbox Code Playgroud)
我玩了很多不同的价值观,kVTCompressionPropertyKey_AverageBitRate
但这对我没什么用,我也试过 kVTCompressionPropertyKey_DataRateLimits
不同的价值但也没有任何运气.
任何想法,建议都是受欢迎的
我正在编写代码来解压本机附件 B H.264 流,我正在完成解析流的过程,从 SPS/PPS NALU 创建 CMVideoFormatDescription,并包装我从流中提取的其他 NALU在 CMSampleBuffers 中。
我在如何处理解码器的 CMBlockBuffer 和 CMSampleBuffer 内存方面遇到了心理障碍。我相信我的问题更多是缺乏对 CF 如何处理内存的透彻理解,所以我的问题更多的是关于这一点,但我希望上下文有帮助。
如果我像这样创建一个 CMBlockBuffer:
CMBlockBufferRef blockBuffer;
OSStatus status = CMBlockBufferCreateWithMemoryBlock(NULL,
memoryBlock,
blockBufferLength,
kCFAllocatorNull,
NULL,
0,
blockBufferLength,
kCMBlockBufferAlwaysCopyDataFlag | kCMBlockBufferAssureMemoryNowFlag,
&blockBuffer);
Run Code Online (Sandbox Code Playgroud)
并将其添加到 CMSampleBuffer 中,如下所示:
CMSampleBufferRef sampleBuffer;
status = CMSampleBufferCreate(kCFAllocatorDefault,
blockBuffer,
true,
NULL,
NULL,
formatDescription,
1,
0,
NULL,
1,
&sampleSize,
&sampleBuffer);
Run Code Online (Sandbox Code Playgroud)
我应该如何处理块缓冲区?SampleBuffer 是否保留块缓冲区的内存,或者我是否需要做一些事情来确保它没有被释放?
另外,关于异步解码过程,是否有一种明智的方法可以知道解码器何时使用 CMSampleBuffer 完成以便我可以处理它?
我的直觉告诉我 CMSampleBuffer 会保留 CMBlockBuffer,而 VTDecodeSession 会保留 CMSampleBuffer 直到它完成解码,但这是一个未记录的领域,我正在寻找一些方向。我得到的结果暗示我的直觉可能是错误的,所以我需要排除内存管理作为一个问题来保持我的理智......
video-toolbox ×12
h.264 ×6
ios ×5
avfoundation ×3
macos ×3
objective-c ×3
core-video ×1
decoding ×1
ffmpeg ×1
hevc ×1
ios8 ×1
iosurface ×1
opengl-es ×1
swift ×1
tvos ×1