简体   繁体   English

iPhone 中 H 264 stream 的 SPS 值

[英]SPS values for H 264 stream in iPhone

Can someone point me to documentation that will help me get correct SPS and PPS values for iPhone.有人能给我指点帮助我获得正确的 iPhone SPS 和 PPS 值的文档吗?

Question is a bit unclear... 问题有点不清楚...

Picture Parameter Set is described in the latest ITU-T release of the standard in chapter 7.3.2.2 Picture Parameter Set在最新版的ITU-T版本 7.3.2.2中进行了描述

Sequence Parameter Set is described in chapter 7.3.2.1. Sequence Parameter Set在第7.3.2.1节中介绍。

I am sure you know, but you can only save H264 encoded video into a file(.mp4, .mov) on iOS. 我相信您知道,但是您只能将H264编码的视频保存到iOS上的文件(.mp4,.mov)中。 There is no access to encoded video frames from code yet. 目前尚无法从代码访问编码的视频帧。 So if you want to create an mp4 file that has encoded video, you need to use AVAssetWriter. 因此,如果要创建已对视频进行编码的mp4文件,则需要使用AVAssetWriter。 Apple has a good example code on how to do this. Apple有一个很好的示例代码来说明如何执行此操作。

I don't know of any place where different SPS/PPS are published; 我不知道在哪个地方发布了不同的SPS / PPS。 since they vary based on your compression settings, image size, and whether you are encoding video in portrait or landscape mode. 因为它们会根据您的压缩设置,图像大小以及是以纵向还是横向模式编码视频而有所不同。 You can use above example code(RosyWriter) to generate some small .mp4 files with your encoding presets; 您可以使用上面的示例代码(RosyWriter)来生成一些带有编码预设的.mp4小文件; and then I would use a hex editor to find the SPS/PPS manually. 然后我将使用十六进制编辑器手动查找SPS / PPS。 Note that SPS/PPS will towards on the end of the file after your H264 stream as part of a larger mp4 info structure. 请注意,作为较大mp4信息结构的一部分,SPS / PPS将在您的H264流之后移向文件结尾。 You can find more info on its structure online. 您可以在线找到有关其结构的更多信息。

Here is some SPS/PPS that I found useful for my project. 这是我发现对我的项目有用的一些SPS / PPS。 Some of them might work for you, but if not you can always generate an mp4 with your H264 encoding presets and find necessary SPS/PPS. 其中一些可能对您有用,但如果不能,则始终可以使用H264编码预设生成mp4并找到必要的SPS / PPS。 My video was encoded using AVVideoProfileLevelH264Baseline30, and here are the SPS/PPS for different video sizes I needed: 我的视频是使用AVVideoProfileLevelH264Baseline30编码的,以下是我需要的不同视频尺寸的SPS / PPS:

SPS: 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: PPS:

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

You can encode a single frame to a file and then extract the sps and pps from that file. 您可以将单个帧编码为文件,然后从该文件中提取sps和pps。 I have an example that shows how to do exactly that at http://www.gdcl.co.uk/2013/02/20/iOS-Video-Encoding.html http://www.gdcl.co.uk/2013/02/20/iOS-Video-Encoding.html上 ,我有一个示例演示了如何完全做到这一点

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

Search keyword print("SPS is and print("PPS is below.搜索关键字print("SPS is and print("PPS is below.

//
//  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)")
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM