Files
HaishinKit.swift/RTMPHaishinKit/Sources/RTMP/RTMPFoundation.swift
2025-07-13 12:43:53 +09:00

256 lines
5.8 KiB
Swift

import AVFoundation
import HaishinKit
/// The type of flv supports aac packet types.
enum RTMPAACPacketType: UInt8 {
/// The sequence data.
case seq = 0
/// The raw data.
case raw = 1
}
/// The type of flv supports avc packet types.
enum RTMPAVCPacketType: UInt8 {
/// The sequence data.
case seq = 0
/// The NAL unit data.
case nal = 1
/// The end of stream data.
case eos = 2
}
/// The type of flv supports audio codecs.
enum RTMPAudioCodec: UInt8 {
/// The PCM codec.
case pcm = 0
/// The ADPCM codec.
case adpcm = 1
/// The MP3 codec.
case mp3 = 2
/// The PCM little endian codec.
case pcmle = 3
/// The Nellymoser 16kHz codec.
case nellymoser16K = 4
/// The Nellymoser 8kHz codec.
case nellymoser8K = 5
/// The Nellymoser codec.
case nellymoser = 6
/// The G.711 A-law codec.
case g711A = 7
/// The G.711 mu-law codec.
case g711MU = 8
/// The signal FOURCC mode. E-RTMP.
case exheader = 9
/// The AAC codec.
case aac = 10
/// The Speex codec.
case speex = 11
/// The MP3 8kHz codec.
case mp3_8k = 14
/// The Device-specific sound.
case device = 15
/// The undefined codec
case unknown = 0xFF
var isSupported: Bool {
switch self {
case .aac:
return true
default:
return false
}
}
var formatID: AudioFormatID {
switch self {
case .pcm:
return kAudioFormatLinearPCM
case .mp3:
return kAudioFormatMPEGLayer3
case .pcmle:
return kAudioFormatLinearPCM
case .aac:
return kAudioFormatMPEG4AAC
case .mp3_8k:
return kAudioFormatMPEGLayer3
default:
return 0
}
}
var formatFlags: AudioFormatFlags {
switch self {
case .aac:
return AudioFormatFlags(AudioSpecificConfig.AudioObjectType.aacMain.rawValue)
default:
return 0
}
}
var headerSize: Int {
switch self {
case .aac:
return 2
default:
return 1
}
}
func audioStreamBasicDescription(_ payload: Data) -> AudioStreamBasicDescription? {
guard isSupported, !payload.isEmpty else {
return nil
}
guard
let soundRate = RTMPSoundRate(rawValue: (payload[0] & 0b00001100) >> 2),
let soundType = RTMPSoundType(rawValue: (payload[0] & 0b00000001)) else {
return nil
}
return AudioStreamBasicDescription(
mSampleRate: soundRate.floatValue,
mFormatID: formatID,
mFormatFlags: formatFlags,
mBytesPerPacket: 0,
mFramesPerPacket: 1024,
mBytesPerFrame: 0,
mChannelsPerFrame: soundType == .stereo ? 2 : 1,
mBitsPerChannel: 0,
mReserved: 0
)
}
}
/// The type of flv supports video frame types.
enum RTMPFrameType: UInt8 {
/// The keyframe.
case key = 1
/// The inter frame.
case inter = 2
/// The disposable inter frame.
case disposable = 3
/// The generated keydrame.
case generated = 4
/// The video info or command frame.
case command = 5
}
enum RTMPSoundRate: UInt8 {
/// The sound rate of 5,500.0kHz.
case kHz5_5 = 0
/// Ths sound rate of 11,000.0kHz.
case kHz11 = 1
/// The sound rate of 22,050.0kHz.
case kHz22 = 2
/// Ths sound rate of 44,100.0kHz.
case kHz44 = 3
/// The float typed value.
var floatValue: Float64 {
switch self {
case .kHz5_5:
return 5500
case .kHz11:
return 11025
case .kHz22:
return 22050
case .kHz44:
return 44100
}
}
}
/// The type of flv supports audio sound size.
enum RTMPSoundSize: UInt8 {
/// The 8bit sound.
case snd8bit = 0
/// The 16bit sound.
case snd16bit = 1
}
/// The type of flv supports audio sound channel type..
enum RTMPSoundType: UInt8 {
/// The mono sound.
case mono = 0
/// The stereo sound.
case stereo = 1
}
/// The type of flv tag.
enum RTMPTagType: UInt8 {
/// The Audio tag,
case audio = 8
/// The Video tag.
case video = 9
/// The Data tag.
case data = 18
var streamId: UInt16 {
switch self {
case .audio, .video:
return UInt16(rawValue)
case .data:
return 0
}
}
var headerSize: Int {
switch self {
case .audio:
return 2
case .video:
return 5
case .data:
return 0
}
}
}
/// The type of flv supports video codecs.
enum RTMPVideoCodec: UInt8 {
/// The JPEG codec.
case jpeg = 1
/// The Sorenson H263 codec.
case sorensonH263 = 2
/// The Screen video codec.
case screen1 = 3
/// The On2 VP6 codec.
case on2VP6 = 4
/// The On2 VP6 with alpha channel codec.
case on2VP6Alpha = 5
/// The Screen video version2 codec.
case screen2 = 6
/// The AVC codec.
case avc = 7
/// The unknown codec.
case unknown = 0xFF
var isSupported: Bool {
switch self {
case .jpeg:
return false
case .sorensonH263:
return false
case .screen1:
return false
case .on2VP6:
return false
case .on2VP6Alpha:
return false
case .screen2:
return false
case .avc:
return true
case .unknown:
return false
}
}
}
extension CMSampleBuffer {
func getCompositionTime(_ offset: TimeInterval) -> Int32 {
guard decodeTimeStamp.isValid else {
return 0
}
return max(0, Int32(((presentationTimeStamp - decodeTimeStamp).seconds + offset) * 1000))
}
}