Skip to content

Commit 59d6989

Browse files
xuchdeidxianing
authored andcommitted
NEW: CustomPcmAudioSource
1 parent c37ec61 commit 59d6989

8 files changed

Lines changed: 543 additions & 1 deletion

File tree

iOS/APIExample.xcodeproj/project.pbxproj

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 52;
6+
objectVersion = 51;
77
objects = {
88

99
/* Begin PBXBuildFile section */
@@ -101,6 +101,10 @@
101101
576BB8EE259B00E100323D43 /* CreateDataStream.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 576BB8F0259B00E100323D43 /* CreateDataStream.storyboard */; };
102102
576EA57A25ADC4A1000B3D79 /* VideoChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576EA57925ADC4A1000B3D79 /* VideoChat.swift */; };
103103
576EA58525AED471000B3D79 /* VideoChat.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 576EA58725AED471000B3D79 /* VideoChat.storyboard */; };
104+
5771635E264536BA0072DE96 /* CustomPcmAudioSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5771635D264536BA0072DE96 /* CustomPcmAudioSource.swift */; };
105+
57716360264539FF0072DE96 /* AgoraPcmSourcePush.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5771635F264539FF0072DE96 /* AgoraPcmSourcePush.swift */; };
106+
5771636426453F080072DE96 /* CustomPcmAudioSource.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5771636626453F080072DE96 /* CustomPcmAudioSource.storyboard */; };
107+
577163692645406F0072DE96 /* output.raw in Resources */ = {isa = PBXBuildFile; fileRef = 5771636326453E8E0072DE96 /* output.raw */; };
104108
578AA65C259A05B200D7CAD9 /* CreateDataStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 578AA65B259A05B200D7CAD9 /* CreateDataStream.swift */; };
105109
57B7FC83259C313200407BE1 /* RawAudioData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57B7FC82259C313200407BE1 /* RawAudioData.swift */; };
106110
57B7FC89259C599100407BE1 /* RawAudioData.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 57B7FC8B259C599100407BE1 /* RawAudioData.storyboard */; };
@@ -299,6 +303,11 @@
299303
576EA57925ADC4A1000B3D79 /* VideoChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoChat.swift; sourceTree = "<group>"; };
300304
576EA58625AED471000B3D79 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/VideoChat.storyboard; sourceTree = "<group>"; };
301305
576EA59F25AEE8BC000B3D79 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/VideoChat.strings"; sourceTree = "<group>"; };
306+
5771635D264536BA0072DE96 /* CustomPcmAudioSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPcmAudioSource.swift; sourceTree = "<group>"; };
307+
5771635F264539FF0072DE96 /* AgoraPcmSourcePush.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgoraPcmSourcePush.swift; sourceTree = "<group>"; };
308+
5771636326453E8E0072DE96 /* output.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = output.raw; sourceTree = "<group>"; };
309+
5771636526453F080072DE96 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/CustomPcmAudioSource.storyboard; sourceTree = "<group>"; };
310+
5771636826453F0E0072DE96 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/CustomPcmAudioSource.strings"; sourceTree = "<group>"; };
302311
578AA65B259A05B200D7CAD9 /* CreateDataStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateDataStream.swift; sourceTree = "<group>"; };
303312
57B7FC82259C313200407BE1 /* RawAudioData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawAudioData.swift; sourceTree = "<group>"; };
304313
57B7FC8A259C599100407BE1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/RawAudioData.storyboard; sourceTree = "<group>"; };
@@ -671,6 +680,7 @@
671680
03BEED09251C4446005E78F4 /* Resources */ = {
672681
isa = PBXGroup;
673682
children = (
683+
5771636326453E8E0072DE96 /* output.raw */,
674684
7FBE1D532576A904005A8619 /* model.bundle */,
675685
7FBE1D502576A904005A8619 /* pvc_jnqd.bundle */,
676686
7FBE1D522576A904005A8619 /* pvc_kernels.metallib */,
@@ -755,6 +765,7 @@
755765
03DF1D8924CFC29700DF7151 /* UIColor+CSRGB.m */,
756766
03DF1D8E24CFC29700DF7151 /* UIView+CSshortFrame.h */,
757767
03DF1D8A24CFC29700DF7151 /* UIView+CSshortFrame.m */,
768+
5771635F264539FF0072DE96 /* AgoraPcmSourcePush.swift */,
758769
);
759770
path = ExternalAudio;
760771
sourceTree = "<group>";
@@ -777,6 +788,15 @@
777788
path = VideoChat;
778789
sourceTree = "<group>";
779790
};
791+
5771635C264536750072DE96 /* CustomPcmAudioSource */ = {
792+
isa = PBXGroup;
793+
children = (
794+
5771635D264536BA0072DE96 /* CustomPcmAudioSource.swift */,
795+
5771636626453F080072DE96 /* CustomPcmAudioSource.storyboard */,
796+
);
797+
path = CustomPcmAudioSource;
798+
sourceTree = "<group>";
799+
};
780800
578AA65A259A056D00D7CAD9 /* CreateDataStream */ = {
781801
isa = PBXGroup;
782802
children = (
@@ -807,6 +827,7 @@
807827
A75A56D724A0603000D0089E /* Advanced */ = {
808828
isa = PBXGroup;
809829
children = (
830+
5771635C264536750072DE96 /* CustomPcmAudioSource */,
810831
576EA57825ADC482000B3D79 /* VideoChat */,
811832
57B7FC81259C305B00407BE1 /* RawAudioData */,
812833
578AA65A259A056D00D7CAD9 /* CreateDataStream */,
@@ -954,10 +975,12 @@
954975
isa = PBXResourcesBuildPhase;
955976
buildActionMask = 2147483647;
956977
files = (
978+
577163692645406F0072DE96 /* output.raw in Resources */,
957979
033A9F8E252D8FF300BC26E1 /* JoinMultiChannel.storyboard in Resources */,
958980
03BEED0B251C4446005E78F4 /* audiomixing.mp3 in Resources */,
959981
03FB5B3625642E7C00F04ED0 /* LiveStreaming.storyboard in Resources */,
960982
03B12DAC251127DC00E55818 /* VideoViewMetal.xib in Resources */,
983+
5771636426453F080072DE96 /* CustomPcmAudioSource.storyboard in Resources */,
961984
033A9F89252D8B6C00BC26E1 /* PrecallTest.storyboard in Resources */,
962985
033A9F61252D8B0A00BC26E1 /* VideoMetadata.storyboard in Resources */,
963986
033A9F3F252D89BC00BC26E1 /* RTMPStreaming.storyboard in Resources */,
@@ -1104,6 +1127,7 @@
11041127
03B12DA8251125A500E55818 /* VideoView.swift in Sources */,
11051128
0339D6D424E91BAA008739CD /* QuickSwitchChannelVCItem.swift in Sources */,
11061129
036C42B624D2A3C600A59000 /* AgoraMetalShader.metal in Sources */,
1130+
57716360264539FF0072DE96 /* AgoraPcmSourcePush.swift in Sources */,
11071131
7F76DCA92571794C00E8B7BC /* SettingsCells.swift in Sources */,
11081132
033A9EE5252D5C6900BC26E1 /* VideoMetadata.swift in Sources */,
11091133
036C42BF24D5853200A59000 /* AgoraMediaRawData.m in Sources */,
@@ -1119,6 +1143,7 @@
11191143
03BEED08251C35E7005E78F4 /* AudioMixing.swift in Sources */,
11201144
03DF1D9424CFC29700DF7151 /* AudioController.m in Sources */,
11211145
0339BE86251EF728007D4FDD /* AudioCircularBuffer.cc in Sources */,
1146+
5771635E264536BA0072DE96 /* CustomPcmAudioSource.swift in Sources */,
11221147
036C42AC24D292A700A59000 /* AgoraCameraSourceMediaIO.swift in Sources */,
11231148
03DF1D9024CFC29700DF7151 /* AudioWriteToFile.m in Sources */,
11241149
0339BE6D251DEAFC007D4FDD /* PrecallTest.swift in Sources */,
@@ -1381,6 +1406,15 @@
13811406
name = VideoChat.storyboard;
13821407
sourceTree = "<group>";
13831408
};
1409+
5771636626453F080072DE96 /* CustomPcmAudioSource.storyboard */ = {
1410+
isa = PBXVariantGroup;
1411+
children = (
1412+
5771636526453F080072DE96 /* Base */,
1413+
5771636826453F0E0072DE96 /* zh-Hans */,
1414+
);
1415+
name = CustomPcmAudioSource.storyboard;
1416+
sourceTree = "<group>";
1417+
};
13841418
57B7FC8B259C599100407BE1 /* RawAudioData.storyboard */ = {
13851419
isa = PBXVariantGroup;
13861420
children = (
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// AgoraPcmSourcePush.swift
3+
// APIExample
4+
//
5+
// Created by XC on 2021/5/7.
6+
// Copyright © 2021 Agora Corp. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
protocol AgoraPcmSourcePushDelegate {
12+
func onAudioFrame(data: UnsafeMutablePointer<UInt8>, samples: UInt) -> Void
13+
func onStop()
14+
}
15+
16+
class AgoraPcmSourcePush: NSObject {
17+
fileprivate var delegate: AgoraPcmSourcePushDelegate?
18+
private let filePath: String
19+
private let sampleRate: Int
20+
private let channelsPerFrame: Int
21+
private let bitPerSample: Int
22+
private let samples: Int
23+
24+
private var state: State = .Stop
25+
private let playerQueue: DispatchQueue
26+
27+
enum State {
28+
case Play
29+
case Stop
30+
}
31+
32+
init(delegate: AgoraPcmSourcePushDelegate?, filePath: String, sampleRate: Int, channelsPerFrame: Int, bitPerSample: Int, samples: Int) {
33+
self.delegate = delegate
34+
self.filePath = filePath
35+
self.sampleRate = sampleRate
36+
self.channelsPerFrame = channelsPerFrame
37+
self.bitPerSample = bitPerSample
38+
self.samples = samples
39+
playerQueue = DispatchQueue(label: "player")
40+
}
41+
42+
func start() {
43+
if state == .Stop {
44+
state = .Play
45+
play()
46+
}
47+
}
48+
49+
func stop() {
50+
DispatchQueue.main.async {
51+
if self.state == .Play {
52+
self.state = .Stop
53+
}
54+
self.delegate?.onStop()
55+
}
56+
}
57+
58+
private func play() {
59+
guard let input = InputStream(fileAtPath: self.filePath) else { return }
60+
input.open()
61+
let bufferSize = self.samples * self.bitPerSample / 8 * self.channelsPerFrame
62+
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
63+
let timeInterval = TimeInterval(self.samples) / TimeInterval(self.sampleRate)
64+
dispatchTimer(timeInterval: timeInterval, handler: { (timer: DispatchSourceTimer?) in
65+
if (timer != nil) {
66+
input.read(buffer, maxLength: bufferSize)
67+
self.delegate?.onAudioFrame(data: buffer, samples: UInt(self.samples))
68+
} else {
69+
buffer.deallocate()
70+
input.close()
71+
self.stop()
72+
}
73+
}, needRepeat: {
74+
return input.hasBytesAvailable && self.state == .Play
75+
})
76+
}
77+
78+
private func dispatchTimer(timeInterval: Double, handler: @escaping (DispatchSourceTimer?) -> Void, needRepeat: @escaping () -> Bool) {
79+
let timer = DispatchSource.makeTimerSource(flags: [], queue: playerQueue)
80+
timer.schedule(deadline: .now(), repeating: timeInterval)
81+
timer.setEventHandler {
82+
self.playerQueue.async {
83+
if needRepeat() {
84+
handler(timer)
85+
} else {
86+
timer.cancel()
87+
handler(nil)
88+
}
89+
}
90+
}
91+
timer.resume()
92+
}
93+
}

0 commit comments

Comments
 (0)