【问题标题】:SPS values for H 264 stream in iPhoneiPhone 中 H 264 流的 SPS 值
【发布时间】:2013-02-12 09:41:36
【问题描述】:

谁能给我指点帮助我为 iPhone 获得正确 SPS 和 PPS 值的文档。

【问题讨论】:

  • 你说的是iphone编码的H264视频流吗?

标签: iphone ios ffmpeg avfoundation h.264


【解决方案1】:

使用 VideoToolbox API。参考:https://developer.apple.com/videos/play/wwdc2014/513/

在下方搜索关键字print("SPS isprint("PPS is

//
//  LiveStreamSession.swift
//  LiveStreamKit
//
//  Created by Ya Wang on 6/10/21.
//

import Foundation
import AVFoundation
import VideoToolbox

public class LiveStreamSession {
    
    let compressionSession: VTCompressionSession
        
    var index = -1
    
    var lastInputPTS = CMTime.zero
    
    public init?(width: Int32, height: Int32){
        var compressionSessionOrNil: VTCompressionSession? = nil
        let status = VTCompressionSessionCreate(allocator: kCFAllocatorDefault,
                                                width: width,
                                                height: height,
                                                codecType: kCMVideoCodecType_H264,
                                                encoderSpecification: nil, // let the video toolbox choose a encoder
                                                imageBufferAttributes: nil,
                                                compressedDataAllocator: kCFAllocatorDefault,
                                                outputCallback: nil,
                                                refcon: nil,
                                                compressionSessionOut: &compressionSessionOrNil)
        guard status == noErr,
            let compressionSession = compressionSessionOrNil else {
            return nil
        }
        VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_RealTime, value: kCFBooleanTrue);
        VTCompressionSessionPrepareToEncodeFrames(compressionSession)
        
        self.compressionSession = compressionSession
        
    }
    
    public func pushVideoBuffer(buffer: CMSampleBuffer) {
        // image buffer
        guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
            assertionFailure()
            return
        }
        
        // pts
        let pts = CMSampleBufferGetPresentationTimeStamp(buffer)
        guard CMTIME_IS_VALID(pts) else {
            assertionFailure()
            return
        }
        
        // duration
        var duration = CMSampleBufferGetDuration(buffer);
        if CMTIME_IS_INVALID(duration) && CMTIME_IS_VALID(self.lastInputPTS) {
            duration = CMTimeSubtract(pts, self.lastInputPTS)
        }
                
        index += 1
        self.lastInputPTS = pts
        print("[\(Date())]: pushVideoBuffer \(index)")
        
        let currentIndex = index
        VTCompressionSessionEncodeFrame(compressionSession, imageBuffer: imageBuffer, presentationTimeStamp: pts, duration: duration, frameProperties: nil, infoFlagsOut: nil) {[weak self] status, encodeInfoFlags, sampleBuffer in
            print("[\(Date())]: compressed \(currentIndex)")
            if let sampleBuffer = sampleBuffer {
                self?.didEncodeFrameBuffer(buffer: sampleBuffer, id: currentIndex)
            }
        }
    }
    
    deinit {
        VTCompressionSessionInvalidate(compressionSession)
    }
    
    private func didEncodeFrameBuffer(buffer: CMSampleBuffer, id: Int) {
        guard let attachments = CMSampleBufferGetSampleAttachmentsArray(buffer, createIfNecessary: true)
               else {
            return
        }
        let dic = Unmanaged<CFDictionary>.fromOpaque(CFArrayGetValueAtIndex(attachments, 0)).takeUnretainedValue()
        let keyframe = !CFDictionaryContainsKey(dic, Unmanaged.passRetained(kCMSampleAttachmentKey_NotSync).toOpaque())
//        print("[\(Date())]: didEncodeFrameBuffer \(id) is I frame: \(keyframe)")
        if keyframe,
           let formatDescription = CMSampleBufferGetFormatDescription(buffer) {
            // https://www.slideshare.net/instinctools_EE_Labs/videostream-compression-in-ios
            var number = 0
            CMVideoFormatDescriptionGetH264ParameterSetAtIndex(formatDescription, parameterSetIndex: 0, parameterSetPointerOut: nil, parameterSetSizeOut: nil, parameterSetCountOut: &number, nalUnitHeaderLengthOut: nil)
            // SPS and PPS and so on...
            let parameterSets = NSMutableData()
            for index in 0 ... number - 1 {
                var parameterSetPointer: UnsafePointer<UInt8>?
                var parameterSetLength = 0
                CMVideoFormatDescriptionGetH264ParameterSetAtIndex(formatDescription, parameterSetIndex: index, parameterSetPointerOut: &parameterSetPointer, parameterSetSizeOut: &parameterSetLength, parameterSetCountOut: nil, nalUnitHeaderLengthOut: nil)
//                parameterSets.append(startCode, length: startCodeLength)
                if let parameterSetPointer = parameterSetPointer {
                    parameterSets.append(parameterSetPointer, length: parameterSetLength)
                }
                
                //
                if index == 0 {
                    print("SPS is \(parameterSetPointer) with length \(parameterSetLength)")
                } else if index == 1 {
                    print("PPS is \(parameterSetPointer) with length \(parameterSetLength)")
                }
            }
            print("[\(Date())]: parameterSets \(parameterSets.length)")
        }
    }
}

【讨论】:

    【解决方案2】:

    我相信您知道,但您只能将 H264 编码的视频保存到 iOS 上的文件(.mp4、.mov)中。尚无法从代码中访问编码的视频帧。因此,如果要创建具有编码视频的 mp4 文件,则需要使用 AVAssetWriter。苹果有一个很好的example code 来说明如何做到这一点。

    我不知道有什么地方发布了不同的 SPS/PPS;因为它们会根据您的压缩设置、图像大小以及您是以纵向还是横向模式编码视频而有所不同。您可以使用上面的示例代码(RosyWriter)使用您的编码预设生成一些小的 .mp4 文件;然后我会使用十六进制编辑器手动查找 SPS/PPS。请注意,作为更大的 mp4 信息结构的一部分,SPS/PPS 将在您的 H264 流之后指向文件的末尾。您可以在网上找到有关其结构的更多信息。

    这是我发现对我的项目有用的一些 SPS/PPS。其中一些可能对您有用,但如果不是,您始终可以使用您的 H264 编码预设生成 mp4 并找到必要的 SPS/PPS。我的视频是使用 AVVideoProfileLevelH264Baseline30 编码的,这里是我需要的不同视频尺寸的 SPS/PPS:

    SPS:

    // For AVCaptureSessionPresetLow(144x192) AVCaptureSessionLandscape on Iphone4S, Iphone5
    char iphone_sps[] = {0x67, 0x4D, 0x00, 0x0C, 0xAB, 0x41, 0x82, 0x74, 0xD4, 0x04, 0x04, 0x18, 0x08};
    
    // For AVCaptureSessionPresetLow(144x192), AVCaptureVideoOrientationPortrait on all Ipads
    char ipad_sps[] = {0x67, 0x4D, 0x00, 0x0C, 0xAB, 0x41, 0x23, 0x34, 0xD4, 0x04, 0x04, 0x18, 0x08};
    
    // Iphone 4G AVCaptureSessionPresetLow (144x192), AVCaptureVideoOrientationPortrait
    char iphone4g_sps[] = {0x67, 0x42, 0x00, 0x1E, 0x8D, 0x68, 0x24, 0x66, 0x9A, 0x83, 0x00, 0x83, 0x01};
    
    // For AVCaptureSessionPreset352x288 (352x288), AVCaptureVideoOrientationLandscape 
    char iphone_sps[] = {0x67, 0x42, 0x00, 0x1E, 0xAB, 0x40, 0xB0, 0x4B, 0x4D, 0x40, 0x40, 0x41, 0x80, 0x80};
    
    // For AVCaptureSessionPreset352x288 (352x288), AVCaptureVideoOrientationPortrait
    char ipad_sps[] = {0x67, 0x42, 0x00, 0x1E, 0xAB, 0x40, 0xB0, 0x4B, 0x4D, 0x40, 0x40, 0x41, 0x80, 0x80};
    

    PPS:

    char pps[] =  {0x28, 0xCE, 0x3C, 0x80};
    char iphone4g_pps[] = {0x68, 0xCE, 0x09, 0xC8};
    

    【讨论】:

    • 就像我在回答中描述的那样,SPS/PPS 在我通过 avfoundation/avassetwriter 类生成的仅视频 mp4 文件中
    【解决方案3】:

    您可以将单个帧编码为文件,然后从该文件中提取 sps 和 pps。我有一个例子说明如何在http://www.gdcl.co.uk/2013/02/20/iOS-Video-Encoding.html

    做到这一点

    【讨论】:

    • 你是我的救星,@Geraint!多年来,我一直在寻找一个合适的帖子,你的帖子甚至有很好的示例代码和非常简洁的项目符号列表!
    • 3年后的iOs 9还是这样吗?这似乎几乎令人难以置信。我可以通过 udp 套接字接收 h264 数据并在硬件中轻松解码,但如果我想从相机发送 h264 数据,我需要先将其写入文件然后从文件中读取?那么,在没有延迟的情况下结束相机数据的唯一方法是使用 FFMpeg 在软件中对其进行编码?
    • 不,从 ios 8 开始不再需要。您可以直接使用 VideoToolbox 编码器和解码器 api。
    【解决方案4】:

    问题有点不清楚……

    Picture Parameter Set 在标准的最新ITU-T release 章节 7.3.2.2 中有描述

    Sequence Parameter Set在第 7.3.2.1 章中有描述。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-23
      • 2012-03-25
      • 1970-01-01
      • 2013-05-10
      • 2011-12-16
      • 2011-10-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多