Skip to content

Commit

Permalink
Merge pull request #20 from pedroSG94/feature/g711-codec
Browse files Browse the repository at this point in the history
adding g711 codec
  • Loading branch information
pedroSG94 committed May 25, 2024
2 parents e7a560f + 454e6b9 commit f3e8b3f
Show file tree
Hide file tree
Showing 24 changed files with 222 additions and 155 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "RootEncoderTests"
BuildableName = "RootEncoderTests"
BlueprintName = "RootEncoderTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "044138302501A02300732969"
BuildableName = "app.app"
BlueprintName = "app"
ReferencedContainer = "container:../app.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "044138302501A02300732969"
BuildableName = "app.app"
BlueprintName = "app"
ReferencedContainer = "container:../app.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
101 changes: 84 additions & 17 deletions RootEncoder/Sources/RootEncoder/encoder/audio/AudioEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,29 @@ public class AudioEncoder {
private var initTs: Int64 = 0
private let thread = DispatchQueue(label: "AudioEncoder")
private let syncQueue = SynchronizedQueue<AVAudioPCMBuffer>(label: "AudioEncodeQueue", size: 60)
public var codec = AudioCodec.AAC
private var codec = AudioCodec.AAC

public init(callback: GetAacData) {
self.callback = callback
}

public func setCodec(codec: AudioCodec) {
self.codec = codec
}

public func prepareAudio(inputFormat: AVAudioFormat, sampleRate: Double, channels: UInt32, bitrate: Int) -> Bool {
guard let outputFormat = getAACFormat(sampleRate: sampleRate, channels: channels) else {
if (codec == AudioCodec.G711 && (sampleRate != 8000 || channels != 1)) {
print("G711 only support samplerate 8000 and mono channel")
return false
}
let format: AVAudioFormat? = if codec == AudioCodec.AAC {
getAACFormat(sampleRate: sampleRate, channels: channels)
} else if codec == AudioCodec.G711 {
getG711AFormat(sampleRate: sampleRate, channels: channels)
} else {
nil
}
guard let outputFormat = format else {
return false
}
guard let converter = AVAudioConverter(from: inputFormat, to: outputFormat) else {
Expand Down Expand Up @@ -52,30 +67,51 @@ public class AudioEncoder {
let b = self.syncQueue.dequeue()
if let b = b {
var error: NSError? = nil
guard let aacBuffer = self.convertToAAC(buffer: b, error: &error) else {
continue
}
if error != nil {
print("Encode error: \(error.debugDescription)")
} else {
let data = Array<UInt8>(UnsafeBufferPointer<UInt8>(start: aacBuffer.data.assumingMemoryBound(to: UInt8.self), count: Int(aacBuffer.byteLength)))
guard let packetDescriptions = aacBuffer.packetDescriptions else {
if self.codec == AudioCodec.AAC {
guard let aacBuffer = self.convertToAAC(buffer: b, error: &error) else {
continue
}
if error != nil {
print("Encode error: \(error.debugDescription)")
} else {
let data = Array<UInt8>(UnsafeBufferPointer<UInt8>(start: aacBuffer.data.assumingMemoryBound(to: UInt8.self), count: Int(aacBuffer.byteLength)))
guard let packetDescriptions = aacBuffer.packetDescriptions else {
continue
}
for i in 0..<aacBuffer.packetCount {
let info = packetDescriptions[Int(i)]
var mBuffer = Array<UInt8>(repeating: 0, count: Int(info.mDataByteSize))
mBuffer[0...mBuffer.count - 1] = data[Int(info.mStartOffset)...Int(info.mStartOffset) + Int(info.mDataByteSize - 1)]
let end = Date().millisecondsSince1970
let elapsedNanoSeconds = (end - self.initTs) * 1000

var frame = Frame()
frame.buffer = mBuffer
frame.length = UInt32(mBuffer.count)
frame.timeStamp = UInt64(elapsedNanoSeconds)
self.callback?.getAacData(frame: frame)
}
}
} else if self.codec == AudioCodec.G711 {
guard let g711Buffer = self.convertToG711(buffer: b, error: &error) else {
continue
}
for i in 0..<aacBuffer.packetCount {
let info = packetDescriptions[Int(i)]
var mBuffer = Array<UInt8>(repeating: 0, count: Int(info.mDataByteSize))
mBuffer[0...mBuffer.count - 1] = data[Int(info.mStartOffset)...Int(info.mStartOffset) + Int(info.mDataByteSize - 1)]
if error != nil {
print("Encode error: \(error.debugDescription)")
} else {
let data = g711Buffer.audioBufferToBytes()
let end = Date().millisecondsSince1970
let elapsedNanoSeconds = (end - self.initTs) * 1000

var frame = Frame()
frame.buffer = mBuffer
frame.length = UInt32(mBuffer.count)
frame.buffer = data
frame.length = UInt32(data.count)
frame.timeStamp = UInt64(elapsedNanoSeconds)
self.callback?.getAacData(frame: frame)

}
}

}
}
}
Expand All @@ -97,6 +133,16 @@ public class AudioEncoder {
return outBuffer
}

private func convertToG711(buffer: AVAudioPCMBuffer, error: inout NSError?) -> AVAudioPCMBuffer? {
guard let outputFormat = outputFormat else {
return nil
}
let outBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate))!
outBuffer.frameLength = outBuffer.frameCapacity
self.convert(sourceBuffer: buffer, destinationBuffer: outBuffer, error: &error)
return outBuffer
}

private func convert(sourceBuffer: AVAudioPCMBuffer, destinationBuffer: AVAudioBuffer, error: NSErrorPointer) {
if (running) {
sourceBuffer.frameLength = sourceBuffer.frameCapacity
Expand All @@ -118,7 +164,28 @@ public class AudioEncoder {
}

private func getAACFormat(sampleRate: Double, channels: UInt32) -> AVAudioFormat? {
var description = AudioStreamBasicDescription(mSampleRate: sampleRate, mFormatID: kAudioFormatMPEG4AAC, mFormatFlags: 0, mBytesPerPacket: 0, mFramesPerPacket: 0, mBytesPerFrame: 0, mChannelsPerFrame: channels, mBitsPerChannel: 0, mReserved: 0)
var description = AudioStreamBasicDescription(mSampleRate: sampleRate,
mFormatID: kAudioFormatMPEG4AAC,
mFormatFlags: 0,
mBytesPerPacket: 0,
mFramesPerPacket: 0,
mBytesPerFrame: 0,
mChannelsPerFrame: channels,
mBitsPerChannel: 0,
mReserved: 0)
return AVAudioFormat(streamDescription: &description)
}

private func getG711AFormat(sampleRate: Double, channels: UInt32) -> AVAudioFormat? {
var description = AudioStreamBasicDescription(mSampleRate: sampleRate,
mFormatID: kAudioFormatALaw,
mFormatFlags: AudioFormatFlags(kAudioFormatALaw),
mBytesPerPacket: 1,
mFramesPerPacket: 1,
mBytesPerFrame: 1,
mChannelsPerFrame: channels,
mBitsPerChannel: 8,
mReserved: 0)
return AVAudioFormat(streamDescription: &description)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import AVFoundation

public class MicrophoneManager {

private let thread = DispatchQueue.global()
private let thread = DispatchQueue(label: "MicrophoneManager")
private let audioEngine = AVAudioEngine()
private var inputNode: AVAudioInputNode?
private var inputFormat: AVAudioFormat?
Expand Down
13 changes: 13 additions & 0 deletions RootEncoder/Sources/RootEncoder/encoder/utils/EncoderUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ public extension AVAudioPCMBuffer {
)
return sampleBuffer
}

func audioBufferToBytes() -> [UInt8] {
let srcLeft = self.audioBufferList.pointee.mBuffers.mData!
let bytesPerFrame = self.format.streamDescription.pointee.mBytesPerFrame
let numBytes = Int(bytesPerFrame * self.frameLength)
var audioByteArray = [UInt8](repeating: 0, count: numBytes)
srcLeft.withMemoryRebound(to: UInt8.self, capacity: numBytes) { srcByteData in
audioByteArray.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: srcByteData, count: numBytes)
}
}
return audioByteArray
}
}

extension AVAudioTime {
Expand Down
10 changes: 7 additions & 3 deletions RootEncoder/Sources/RootEncoder/encoder/video/VideoEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ public class VideoEncoder {
codecType: codec.value, encoderSpecification: nil, imageBufferAttributes: nil,
compressedDataAllocator: nil, outputCallback: videoCallback, refcon: Unmanaged.passUnretained(self).toOpaque(),
compressionSessionOut: &session)
print("using codec \(codec)")
self.resolution = resolution
self.fps = fps
self.bitrate = bitrate
Expand Down Expand Up @@ -100,8 +99,13 @@ public class VideoEncoder {
}
}

public func setCodec(codec: CodecUtil) {
self.codec = codec
public func setCodec(codec: VideoCodec) {
switch codec {
case .H264:
self.codec = CodecUtil.H264
case .H265:
self.codec = CodecUtil.H265
}
}

public func forceKeyFrame() {
Expand Down
12 changes: 2 additions & 10 deletions RootEncoder/Sources/RootEncoder/library/base/CameraBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,21 +164,13 @@ public class CameraBase: GetMicrophoneData, GetCameraData, GetAacData, GetH264Da
public func setVideoCodec(codec: VideoCodec) {
setVideoCodecImp(codec: codec)
recordController.setVideoCodec(codec: codec)
let type = switch codec {
case .H264:
CodecUtil.H264
case .H265:
CodecUtil.H265
@unknown default:
CodecUtil.H264
}
videoEncoder.setCodec(codec: type)
videoEncoder.setCodec(codec: codec)
}

public func setAudioCodec(codec: AudioCodec) {
setAudioCodecImp(codec: codec)
recordController.setAudioCodec(codec: codec)
audioEncoder.codec = codec
audioEncoder.setCodec(codec: codec)
}

public func setVideoCodecImp(codec: VideoCodec) {}
Expand Down
12 changes: 2 additions & 10 deletions RootEncoder/Sources/RootEncoder/library/base/DisplayBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,13 @@ public class DisplayBase: GetMicrophoneData, GetCameraData, GetAacData, GetH264D
public func setVideoCodec(codec: VideoCodec) {
setVideoCodecImp(codec: codec)
recordController.setVideoCodec(codec: codec)
let type = switch codec {
case .H264:
CodecUtil.H264
case .H265:
CodecUtil.H265
@unknown default:
CodecUtil.H264
}
videoEncoder.setCodec(codec: type)
videoEncoder.setCodec(codec: codec)
}

public func setAudioCodec(codec: AudioCodec) {
setAudioCodecImp(codec: codec)
recordController.setAudioCodec(codec: codec)
audioEncoder.codec = codec
audioEncoder.setCodec(codec: codec)
}

public func setVideoCodecImp(codec: VideoCodec) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ public class DisplayRtmp: DisplayBase {
}

public func setAuth(user: String, password: String) {
//client.setAuth(user: user, password: password)
}

public func setCodec(codec: CodecUtil) {
videoEncoder.setCodec(codec: codec)
client.setAuth(user: user, password: password)
}

public func reTry(delay: Int, reason: String, backUrl: String? = nil) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ public class DisplayRtsp: DisplayBase {
client.setAuth(user: user, password: password)
}

public func setCodec(codec: CodecUtil) {
videoEncoder.setCodec(codec: codec)
}

public func reTry(delay: Int, reason: String, backUrl: String? = nil) -> Bool {
let result = client.shouldRetry(reason: reason)
if (result) {
Expand Down
4 changes: 0 additions & 4 deletions RootEncoder/Sources/RootEncoder/library/rtsp/RtspCamera.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ public class RtspCamera: CameraBase {
client.setAuth(user: user, password: password)
}

public func setCodec(codec: CodecUtil) {
videoEncoder.setCodec(codec: codec)
}

public func reTry(delay: Int, reason: String, backUrl: String? = nil) -> Bool {
let result = client.shouldRetry(reason: reason)
if (result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,6 @@ public class RecordController {
AVVideoCodecType.h264
case VideoCodec.H265:
AVVideoCodecType.hevc
default:
AVVideoCodecType.h264 //TODO throw error
}
}

Expand All @@ -157,8 +155,6 @@ public class RecordController {
kAudioFormatMPEG4AAC
case AudioCodec.G711:
kAudioFormatALaw
default:
kAudioFormatLinearPCM // TODO throw error
}
}

Expand Down
4 changes: 2 additions & 2 deletions RootEncoder/Sources/RootEncoder/rtmp/rtmp/RtmpClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class RtmpClient {
}
if (!self.isStreaming || isRetry) {
self.isStreaming = true
thread = Task {
thread = Task(priority: .high) {
guard let url = url else {
self.connectChecker.onConnectionFailed(reason: "Endpoint malformed, should be: rtmp://ip:port/appname/streamname")
return
Expand Down Expand Up @@ -292,7 +292,7 @@ public class RtmpClient {
connectChecker.onAuthError()
}
default:
connectChecker.onConnectionFailed(reason: description)
connectChecker.onConnectionFailed(reason: description)
}
case "onStatus":
let code = ((command.data[3] as! AmfObject).getProperty(name: "code") as! AmfString).value
Expand Down
Loading

0 comments on commit f3e8b3f

Please sign in to comment.