HaishinKit.swift/Sources/RTMP/RTMPMuxer.swift

83 lines
3.6 KiB
Swift

import AVFoundation
protocol RTMPMuxerDelegate: AnyObject {
func metadata(_ metadata: ASObject)
func sampleOutput(audio buffer: Data, withTimestamp: Double, muxer: RTMPMuxer)
func sampleOutput(video buffer: Data, withTimestamp: Double, muxer: RTMPMuxer)
}
// MARK: -
final class RTMPMuxer {
static let aac: UInt8 = FLVAudioCodec.aac.rawValue << 4 | FLVSoundRate.kHz44.rawValue << 2 | FLVSoundSize.snd16bit.rawValue << 1 | FLVSoundType.stereo.rawValue
weak var delegate: RTMPMuxerDelegate?
private var configs: [Int: Data] = [:]
private var audioTimeStamp = CMTime.zero
private var videoTimeStamp = CMTime.zero
func dispose() {
configs.removeAll()
audioTimeStamp = CMTime.zero
videoTimeStamp = CMTime.zero
}
}
extension RTMPMuxer: AudioCodecDelegate {
// MARK: AudioCodecDelegate
func didSetFormatDescription(audio formatDescription: CMFormatDescription?) {
guard let formatDescription = formatDescription else {
return
}
var buffer = Data([RTMPMuxer.aac, FLVAACPacketType.seq.rawValue])
buffer.append(contentsOf: AudioSpecificConfig(formatDescription: formatDescription).bytes)
delegate?.sampleOutput(audio: buffer, withTimestamp: 0, muxer: self)
}
func sampleOutput(audio data: UnsafeMutableAudioBufferListPointer, presentationTimeStamp: CMTime) {
let delta: Double = (audioTimeStamp == CMTime.zero ? 0 : presentationTimeStamp.seconds - audioTimeStamp.seconds) * 1000
guard let bytes = data[0].mData, 0 < data[0].mDataByteSize && 0 <= delta else {
return
}
var buffer = Data([RTMPMuxer.aac, FLVAACPacketType.raw.rawValue])
buffer.append(bytes.assumingMemoryBound(to: UInt8.self), count: Int(data[0].mDataByteSize))
delegate?.sampleOutput(audio: buffer, withTimestamp: delta, muxer: self)
audioTimeStamp = presentationTimeStamp
}
}
extension RTMPMuxer: VideoEncoderDelegate {
// MARK: VideoEncoderDelegate
func didSetFormatDescription(video formatDescription: CMFormatDescription?) {
guard
let formatDescription = formatDescription,
let avcC = AVCConfigurationRecord.getData(formatDescription) else {
return
}
var buffer = Data([FLVFrameType.key.rawValue << 4 | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.seq.rawValue, 0, 0, 0])
buffer.append(avcC)
delegate?.sampleOutput(video: buffer, withTimestamp: 0, muxer: self)
}
func sampleOutput(video sampleBuffer: CMSampleBuffer) {
let keyframe: Bool = !sampleBuffer.isNotSync
var compositionTime: Int32 = 0
let presentationTimeStamp: CMTime = sampleBuffer.presentationTimeStamp
var decodeTimeStamp: CMTime = sampleBuffer.decodeTimeStamp
if decodeTimeStamp == CMTime.invalid {
decodeTimeStamp = presentationTimeStamp
} else {
compositionTime = Int32((presentationTimeStamp.seconds - decodeTimeStamp.seconds) * 1000)
}
let delta: Double = (videoTimeStamp == CMTime.zero ? 0 : decodeTimeStamp.seconds - videoTimeStamp.seconds) * 1000
guard let data = sampleBuffer.dataBuffer?.data, 0 <= delta else {
return
}
var buffer = Data([((keyframe ? FLVFrameType.key.rawValue : FLVFrameType.inter.rawValue) << 4) | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.nal.rawValue])
buffer.append(contentsOf: compositionTime.bigEndian.data[1..<4])
buffer.append(data)
delegate?.sampleOutput(video: buffer, withTimestamp: delta, muxer: self)
videoTimeStamp = decodeTimeStamp
}
}