AVSampleBufferDisplayLayer h264 on a real device intermittent playback

35 Views Asked by At

I get h264 in Data format. The video plays well on the simulator. And on a real device intermittently with a hang. I don't use decompression.

class H264Decoder {
    
    var sampleBufferCallback: ((CMSampleBuffer) -> Void)?

    private var sps: H264UnitModel?
    private var pps: H264UnitModel?
    private var description: CMVideoFormatDescription?
    private var fullBuffer: CMBlockBuffer?
    
    
    func decode(_ h264Units: [H264UnitModel]) {
        fullBuffer = nil
        var isDescript = false
        for h264Unit in h264Units {
            isDescript = false
            if h264Unit.type == .sps || h264Unit.type == .pps {
                description = nil
                createDescription(with: h264Unit)
                isDescript = true
            } else {
                sps = nil
                pps = nil
            }
            
            if !isDescript {
                guard let fullBuffer = createBlockBuffer(with: h264Unit) else {
                    return
                }
                guard let sampleBuffer = createSampleBuffer(with: fullBuffer) else {
                    return
                }
                sampleBufferCallback?(sampleBuffer)
            }
        }
    }
     
    
    // MARK: - create Block Buffer
   
    private func createBlockBuffer(with h264Format: H264UnitModel) -> CMBlockBuffer? {
        let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: h264Format.data.count)
        
        h264Format.data.copyBytes(to: pointer, count: h264Format.data.count)
        var blockBuffer: CMBlockBuffer?
        
        let error = CMBlockBufferCreateWithMemoryBlock(allocator: kCFAllocatorDefault,
                                                       memoryBlock: pointer,
                                                       blockLength: h264Format.data.count,
                                                       blockAllocator: kCFAllocatorDefault,
                                                       customBlockSource: nil,
                                                       offsetToData: 0,
                                                       dataLength: h264Format.data.count,
                                                       flags: .zero,
                                                       blockBufferOut: &blockBuffer)
        
        guard error == kCMBlockBufferNoErr else {
            return nil
        }
        return blockBuffer
    }
    
    
    private func createSampleBuffer(with blockBuffer: CMBlockBuffer) -> CMSampleBuffer? {
        var sampleBuffer: CMSampleBuffer?
        
        var timingInfo = CMSampleTimingInfo()
        timingInfo.decodeTimeStamp = .invalid
        timingInfo.duration = CMTime.invalid
        timingInfo.presentationTimeStamp = .zero
        
        let error = CMSampleBufferCreateReady(allocator: kCFAllocatorDefault,
                                              dataBuffer: blockBuffer,
                                              formatDescription: description,
                                              sampleCount: 1,
                                              sampleTimingEntryCount: 1,
                                              sampleTimingArray: &timingInfo,
                                              sampleSizeEntryCount: 0,
                                              sampleSizeArray: nil,
                                              sampleBufferOut: &sampleBuffer)
        
        guard error == noErr,
              let sampleBuffer = sampleBuffer else {
            return nil
        }
        
        if let attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer,
                                                                     createIfNecessary: true) {
            let dic = unsafeBitCast(CFArrayGetValueAtIndex(attachments, 0),
                                    to: CFMutableDictionary.self)
            
            CFDictionarySetValue(dic,
                                 Unmanaged.passUnretained(kCMSampleAttachmentKey_DisplayImmediately).toOpaque(),
                                 Unmanaged.passUnretained(kCFBooleanTrue).toOpaque())
        }
        return sampleBuffer
    }
    
    
    private func createDescription(with h264Format: H264UnitModel) {
        if h264Format.type == .sps {
            sps = h264Format
        } else if h264Format.type == .pps {
            pps = h264Format
        }
        
        guard let sps = sps,
              let pps = pps else { return }
        
        let spsPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: sps.data.count)
        sps.data.copyBytes(to: spsPointer, count: sps.data.count)
        
        let ppsPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: pps.data.count)
        pps.data.copyBytes(to: ppsPointer, count: pps.data.count)
        
        let parameterSet = [UnsafePointer(spsPointer), UnsafePointer(ppsPointer)]
        let parameterSetSizes = [sps.data.count, pps.data.count]
        
        defer {
            parameterSet.forEach {
                $0.deallocate()
            }
        }
        
        let error = CMVideoFormatDescriptionCreateFromH264ParameterSets(allocator: kCFAllocatorDefault,
                                                            parameterSetCount: 2,
                                                            parameterSetPointers: parameterSet,
                                                            parameterSetSizes: parameterSetSizes,
                                                            nalUnitHeaderLength: 4,
                                                            formatDescriptionOut: &description)
        guard error == noErr else {
            return
        }
    }
}

I suspect I'm missing some key to set up the decoding. No errors are output to the console. Receiving status CMBlockBuffer, CMSampleBuffer, CMVideoFormatDescription equal 0.

0

There are 0 best solutions below