Skip to content

Commit

Permalink
Supports AAC codec multi-channels.
Browse files Browse the repository at this point in the history
  • Loading branch information
shogo4405 committed Apr 30, 2024
1 parent 7327e2b commit ffb08ad
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 30 deletions.
11 changes: 8 additions & 3 deletions Sources/Codec/AudioCodecSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public struct AudioCodecSettings: Codable {
case .aac:
return UInt32(MPEG4ObjectID.AAC_LC.rawValue)
case .pcm:
return kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsPacked | kAudioFormatFlagIsFloat
return kAudioFormatFlagIsNonInterleaved |
kAudioFormatFlagIsPacked |
kAudioFormatFlagIsFloat
}
}

Expand Down Expand Up @@ -113,11 +115,14 @@ public struct AudioCodecSettings: Codable {
mBytesPerPacket: bytesPerPacket,
mFramesPerPacket: framesPerPacket,
mBytesPerFrame: bytesPerFrame,
mChannelsPerFrame: min(format.channelCount, AudioCodecSettings.maximumNumberOfChannels),
mChannelsPerFrame: format.channelCount,
mBitsPerChannel: bitsPerChannel,
mReserved: 0
)
return AVAudioFormat(streamDescription: &streamDescription)
return AVAudioFormat(
streamDescription: &streamDescription,
channelLayout: format.channelLayout
)
}
}

Expand Down
129 changes: 104 additions & 25 deletions Sources/ISO/AudioSpecificConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,30 @@ struct AudioSpecificConfig: Equatable {
case celp = 8
case hxvc = 9

init(objectID: MPEG4ObjectID) {
init?(objectID: MPEG4ObjectID?) {
switch objectID {
case .aac_Main:
case .aac_Main?:
self = .aacMain
case .AAC_LC:
case .AAC_LC?:
self = .aacLc
case .AAC_SSR:
case .AAC_SSR?:
self = .aacSsr
case .AAC_LTP:
case .AAC_LTP?:
self = .aacLtp
case .AAC_SBR:
case .AAC_SBR?:
self = .aacSbr
case .aac_Scalable:
case .aac_Scalable?:
self = .aacScalable
case .twinVQ:
case .twinVQ?:
self = .twinqVQ
case .CELP:
case .CELP?:
self = .celp
case .HVXC:
case .HVXC?:
self = .hxvc
case .none:
return nil
@unknown default:
self = .unknown
return nil
}
}
}
Expand Down Expand Up @@ -92,7 +94,7 @@ struct AudioSpecificConfig: Equatable {
}
}

init(sampleRate: Float64) {
init?(sampleRate: Float64) {
switch Int(sampleRate) {
case 96000:
self = .hz96000
Expand Down Expand Up @@ -121,7 +123,7 @@ struct AudioSpecificConfig: Equatable {
case 7350:
self = .hz7350
default:
self = .hz44100
return nil
}
}
}
Expand All @@ -135,17 +137,82 @@ struct AudioSpecificConfig: Equatable {
case frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRight = 5
case frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRightLFE = 6
case frontCenterAndFrontLeftAndFrontRightAndSideLeftAndSideRightAndBackLeftAndBackRightLFE = 7

var channelsCount: UInt32 {
switch self {
case .definedInAOTSpecificConfig:
return 0
case .frontCenter:
return 1
case .frontLeftAndFrontRight:
return 2
case .frontCenterAndFrontLeftAndFrontRight:
return 3
case .frontCenterAndFrontLeftAndFrontRightAndBackCenter:
return 4
case .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRight:
return 5
case .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRightLFE:
return 6
case .frontCenterAndFrontLeftAndFrontRightAndSideLeftAndSideRightAndBackLeftAndBackRightLFE:
return 8
}
}

var audioChannelLayoutTag: AudioChannelLayoutTag? {
switch self {
case .definedInAOTSpecificConfig:
return nil
case .frontCenter:
return nil
case .frontLeftAndFrontRight:
return nil
case .frontCenterAndFrontLeftAndFrontRight:
return kAudioChannelLayoutTag_MPEG_3_0_B
case .frontCenterAndFrontLeftAndFrontRightAndBackCenter:
return kAudioChannelLayoutTag_MPEG_4_0_B
case .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRight:
return kAudioChannelLayoutTag_MPEG_5_0_D
case .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRightLFE:
return kAudioChannelLayoutTag_MPEG_5_1_D
case .frontCenterAndFrontLeftAndFrontRightAndSideLeftAndSideRightAndBackLeftAndBackRightLFE:
return kAudioChannelLayoutTag_MPEG_7_1_B
}
}

init?(channelsCount: UInt32) {
switch channelsCount {
case 0:
self = .definedInAOTSpecificConfig
case 1:
self = .frontCenter
case 2:
self = .frontLeftAndFrontRight
case 3:
self = .frontCenterAndFrontLeftAndFrontRight
case 4:
self = .frontCenterAndFrontLeftAndFrontRightAndBackCenter
case 5:
self = .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRight
case 6:
self = .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRightLFE
case 8:
self = .frontCenterAndFrontLeftAndFrontRightAndSideLeftAndSideRightAndBackLeftAndBackRightLFE
default:
return nil
}
}
}

let type: AudioObjectType
let frequency: SamplingFrequency
let channel: ChannelConfiguration
let channelConfig: ChannelConfiguration
let frameLengthFlag = false

var bytes: [UInt8] {
var bytes = [UInt8](repeating: 0, count: 2)
bytes[0] = type.rawValue << 3 | (frequency.rawValue >> 1)
bytes[1] = (frequency.rawValue & 0x1) << 7 | (channel.rawValue & 0xF) << 3
bytes[1] = (frequency.rawValue & 0x1) << 7 | (channelConfig.rawValue & 0xF) << 3
return bytes
}

Expand All @@ -158,20 +225,26 @@ struct AudioSpecificConfig: Equatable {
}
self.type = type
self.frequency = frequency
self.channel = channel
self.channelConfig = channel
}

init(type: AudioObjectType, frequency: SamplingFrequency, channel: ChannelConfiguration) {
self.type = type
self.frequency = frequency
self.channel = channel
self.channelConfig = channel
}

init(formatDescription: CMFormatDescription) {
let asbd: AudioStreamBasicDescription = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription)!.pointee
type = AudioObjectType(objectID: MPEG4ObjectID(rawValue: Int(asbd.mFormatFlags))!)
frequency = SamplingFrequency(sampleRate: asbd.mSampleRate)
channel = ChannelConfiguration(rawValue: UInt8(asbd.mChannelsPerFrame))!
init?(formatDescription: CMFormatDescription) {
guard
let streamDescription = formatDescription.audioStreamBasicDescription,
let type = AudioObjectType(objectID: MPEG4ObjectID(rawValue: Int(streamDescription.mFormatFlags))),
let frequency = SamplingFrequency(sampleRate: streamDescription.mSampleRate),
let channelConfig = ChannelConfiguration(channelsCount: streamDescription.mChannelsPerFrame) else {
return nil
}
self.type = type
self.frequency = frequency
self.channelConfig = channelConfig
}

func makeHeader(_ length: Int) -> [UInt8] {
Expand All @@ -180,8 +253,8 @@ struct AudioSpecificConfig: Equatable {
var adts = [UInt8](repeating: 0x00, count: size)
adts[0] = 0xFF
adts[1] = 0xF9
adts[2] = (type.rawValue - 1) << 6 | (frequency.rawValue << 2) | (channel.rawValue >> 2)
adts[3] = (channel.rawValue & 3) << 6 | UInt8(fullSize >> 11)
adts[2] = (type.rawValue - 1) << 6 | (frequency.rawValue << 2) | (channelConfig.rawValue >> 2)
adts[3] = (channelConfig.rawValue & 3) << 6 | UInt8(fullSize >> 11)
adts[4] = UInt8((fullSize & 0x7FF) >> 3)
adts[5] = ((UInt8(fullSize & 7)) << 5) + 0x1F
adts[6] = 0xFC
Expand All @@ -190,6 +263,12 @@ struct AudioSpecificConfig: Equatable {

func makeAudioFormat() -> AVAudioFormat? {
var audioStreamBasicDescription = makeAudioStreamBasicDescription()
if let audioChannelLayoutTag = channelConfig.audioChannelLayoutTag {
return AVAudioFormat(
streamDescription: &audioStreamBasicDescription,
channelLayout: AVAudioChannelLayout(layoutTag: audioChannelLayoutTag)
)
}
return AVAudioFormat(streamDescription: &audioStreamBasicDescription)
}

Expand All @@ -201,7 +280,7 @@ struct AudioSpecificConfig: Equatable {
mBytesPerPacket: 0,
mFramesPerPacket: frameLengthFlag ? 960 : 1024,
mBytesPerFrame: 0,
mChannelsPerFrame: UInt32(channel.rawValue),
mChannelsPerFrame: channelConfig.channelsCount,
mBitsPerChannel: 0,
mReserved: 0
)
Expand Down
4 changes: 2 additions & 2 deletions Sources/RTMP/RTMPMuxer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ final class RTMPMuxer {
didSet {
switch stream?.readyState {
case .publishing:
guard let audioFormat else {
guard let audioFormat, let config = AudioSpecificConfig(formatDescription: audioFormat.formatDescription) else {
return
}
var buffer = Data([RTMPMuxer.aac, FLVAACPacketType.seq.rawValue])
buffer.append(contentsOf: AudioSpecificConfig(formatDescription: audioFormat.formatDescription).bytes)
buffer.append(contentsOf: config.bytes)
stream?.doOutput(
.zero,
chunkStreamId: FLVTagType.audio.streamId,
Expand Down

0 comments on commit ffb08ad

Please sign in to comment.