【问题标题】:Decoding h264 in iOS 8 with video tool box使用视频工具箱在 iOS 8 中解码 h264
【发布时间】:2014-09-24 08:30:07
【问题描述】:

需要解码 h264 流并获取像素缓冲区

我知道在 iOS 8 上可以使用视频工具箱

1.如何将h264流转成CMSampleBufferRef

2.如何使用视频工具箱进行解码?

【问题讨论】:

  • 您阅读文档了吗?你看过关于这个主题的 WWDC 视频吗?你为什么尝试?
  • 我对视频和视频流媒体世界了如指掌。是的,我看过 WWDC 2014 的视频,但现在还不够,因为我错过了一些细节,这对你来说可能很明显:)
  • 哦,对我来说很明显你缺少细节。事实上,你的问题甚至没有足够的细节来回答它。你的 h264 流是什么格式的?是附件B吗?你有指向 SPS/PPS 的指针吗?
  • 我通过 udp 从外部设备获取 h264 到 iphone,需要将像素缓冲区发送到我用于视频会议的 sdk
  • 我有来自 udp 套接字的 avcc 流

标签: video-streaming ios8 h.264


【解决方案1】:

我假设你得到的是附件 B 格式的流,如果它已经是 AVCC 格式(读取 MP4),那么你可以使用 AssetReader 而不需要做太多。

对于附件 B 流(这就是人们通常所说的原始 h264 流)。

  1. 提取 SPS/PPS NAL 单元并从中创建参数集。您会定期收到它们。它们包含有关如何解码帧的信息。

  2. 使用持续时间(您可以从解析 SPS 的 VUI 部分中获取)和演示时间戳和解码时间戳创建 TimingInfo 数组。如果以 MPEG2 TS 形式接收流,则从 PESr 获取时间戳。如果不只是根据您的计算提供缺失的信息。

  3. 将 VLC NAL 单元包装在 CMBlockBuffer 中。您可以将多个放入其中。如果您通过 RTP 接收流,这可能会导致 NAL 单元碎片化,请确保每个 NAL 单元都是完整的。

  4. 在 CMBlockbuffer 中包装 NAL 单元时,将 3 或 4 字节的起始码替换为长度标头。

  5. 向 CMSampleBufferCreate 提供信息,您可以在 VTDecompressionSession 中解码帧

WWDC 提供了一个预设,可以更详细地解释这些步骤,并提供示例代码。

【讨论】:

  • 从文档中 AssetReader 无法帮助处理网络流。希望我错了。 AVAssetRedaer 使用 AVAsset 初始化。没有找到使它与流一起使用的方法。看到一些关于将流写入文件并使用 AVAssetReader 读取文件的帖子,但效果不佳,我需要良好的性能和低电池消耗,所以...
  • 正确,AssetReader 无法处理附件 B 流。您必须转换为 AVCC 格式(基本上是 MP4 容器)。这与上面基本相同,但不是解码,而是将其写入文件。但这只有在您想保留收到的流并希望以后使用 IOS/OSX 以简单的方式多次播放时才有意义。
  • 我已经成功解码了 h264 流,现在我想将流显示到 iPhone 的屏幕上。你知道吗?
  • 我使用CADisplayLink 让我的演示者处理VTDecompressionSession 的解码图片队列。在每次垂直同步时,我都会使用 OpenGLES 将新帧渲染到图层。我这样做,所以我可以进行颜色校正、处理 OSD 等。如果这种方法对您有意义,取决于您的代码以及您最终想要实现的目标。 Video Toolbox 在这里提供了很多东西。如果你在这里分享你的一些代码并解释你接下来想要实现的目标,那么你会更容易指出一些可以为你完成工作的类/方法来显示现在在 CVPixelBuffers 中的内容
  • 试图将 CVImageBufferRef 转换为 CGImageRef 并将其显示在 UIImageView 中。我在 CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);错误是 - CGBitmapContextCreate:无效数据字节/行:对于 8 个整数位/组件、3 个组件、kCGImageAlphaPremultipliedFirst,应至少为 1280
【解决方案2】:

试试这个工作代码。将编码的 CMSampleBufferRef 提供给 sampleBuffer。

if(!decompressionSession)
    {
    CMFormatDescriptionRef formatDescription=CMSampleBufferGetFormatDescription(sampleBuffer);
    decompressionSession = NULL;
    VTDecompressionOutputCallbackRecord callBackRecord;
    callBackRecord.decompressionOutputCallback=didDecompress;
    callBackRecord.decompressionOutputRefCon = (__bridge void *)self;
    OSStatus status1= VTDecompressionSessionCreate(kCFAllocatorDefault, formatDescription, NULL, NULL, &callBackRecord, &decompressionSession);
    }
    else
    {
        VTDecodeFrameFlags flags = kVTDecodeFrame_EnableAsynchronousDecompression;
        VTDecodeInfoFlags flagOut;
        VTDecompressionSessionDecodeFrame(decompressionSession, sampleBuffer, flags, NULL, &flagOut);
        VTDecompressionSessionWaitForAsynchronousFrames(decompressionSession);

    }



Decompression all back

static void didDecompress( void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef imageBuffer, CMTime presentationTimeStamp, CMTime presentationDuration ){
 if(status==noErr)
    {
        NSLog(@"SUCCESS PROCEED FROM HERE !!!!");
    }
}

// 请记住,您在编码时提供了正确的演示时间。这里我为您提供编码详细信息..

//ENCODING-------------------ENCODING---------------ENCODING
if(!_compression_session)
{
  NSDictionary* pixelBufferOptions = @{
                                          (NSString*) kCVPixelBufferWidthKey : @(widthOFCaptureImage),
                                          (NSString*) kCVPixelBufferHeightKey : @(heightOFCaptureImage),
                                          (NSString*) kCVPixelBufferOpenGLESCompatibilityKey : @YES,
                                          (NSString*) kCVPixelBufferIOSurfacePropertiesKey : @{}};
_compression_session=NULL;
 CFMutableDictionaryRef encoderSpecifications = NULL;
  err = VTCompressionSessionCreate(
                                     kCFAllocatorDefault,
                                     widthOFCaptureImage,
                                     heightOFCaptureImage,
                                     kCMVideoCodecType_H264,
                                     encoderSpecifications,
                                     (__bridge CFDictionaryRef)pixelBufferOptions,
                                     NULL,
                                     compressionCallback,
                                     (__bridge void *)self,
                                     &_compression_session);
}
else
{


 CMTime presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBufferIs);
  CVPixelBufferRef pixelbufferPassing= CMSampleBufferGetImageBuffer(sampleBufferIs);
          OSStatus status1= VTCompressionSessionEncodeFrame(_compression_session, pixelbufferPassing, presentationTimeStamp, kCMTimeInvalid, NULL, NULL, NULL);
 VTCompressionSessionEndPass(_compression_session, NO, NULL);
}

//编码回调------------------------------------------

  static void compressionCallback(void *outputCallbackRefCon,
                             void *sourceFrameRefCon,
                             OSStatus status,
                             VTEncodeInfoFlags infoFlags,
                             CMSampleBufferRef sampleBuffer ){
    }

//最好的祝愿:)编码愉快:)

【讨论】:

  • 请不要只转储代码而不解释如何以及为什么。
猜你喜欢
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
  • 2018-06-22
  • 2012-02-11
  • 2012-08-03
  • 2012-09-05
  • 2022-01-11
  • 1970-01-01
相关资源
最近更新 更多