8
8
* be found in the AUTHORS file in the root of the source tree.
9
9
*/
10
10
11
+ #import < AVFoundation/AVFoundation.h>
12
+ #import < os/lock.h>
13
+
11
14
#import " RTCAudioTrack+Private.h"
12
15
16
+ #import " RTCAudioRenderer.h"
13
17
#import " RTCAudioSource+Private.h"
14
18
#import " RTCMediaStreamTrack+Private.h"
15
19
#import " RTCPeerConnectionFactory+Private.h"
16
20
#import " helpers/NSString+StdString.h"
17
21
18
22
#include " rtc_base/checks.h"
19
23
20
- @implementation RTC_OBJC_TYPE (RTCAudioTrack)
24
+ namespace webrtc {
25
+ /* *
26
+ * Captures audio data and converts to CMSampleBuffers
27
+ */
28
+ class AudioSinkConverter : public rtc ::RefCountInterface, public webrtc::AudioTrackSinkInterface {
29
+ private:
30
+ os_unfair_lock *lock_;
31
+ __weak RTCAudioTrack *audio_track_;
32
+ int64_t total_frames_ = 0 ;
33
+ bool attached_ = false ;
34
+
35
+ public:
36
+ AudioSinkConverter (RTCAudioTrack *audioTrack, os_unfair_lock *lock) {
37
+ RTC_LOG (LS_INFO) << " RTCAudioTrack.AudioSinkConverter init" ;
38
+ audio_track_ = audioTrack;
39
+ lock_ = lock;
40
+ }
41
+
42
+ ~AudioSinkConverter () {
43
+ //
44
+ RTC_LOG (LS_INFO) << " RTCAudioTrack.AudioSinkConverter dealloc" ;
45
+ }
46
+
47
+ // Must be called while locked
48
+ void TryAttach () {
49
+ if (attached_) {
50
+ // Already attached
51
+ return ;
52
+ }
53
+ RTC_LOG (LS_INFO) << " RTCAudioTrack attaching sink..." ;
54
+ // Reset for creating CMSampleTimingInfo correctly
55
+ audio_track_.nativeAudioTrack ->AddSink (this );
56
+ total_frames_ = 0 ;
57
+ attached_ = true ;
58
+ }
59
+
60
+ // Must be called while locked
61
+ void TryDetach () {
62
+ if (!attached_) {
63
+ // Already detached
64
+ return ;
65
+ }
66
+ RTC_LOG (LS_INFO) << " RTCAudioTrack detaching sink..." ;
67
+ audio_track_.nativeAudioTrack ->RemoveSink (this );
68
+ attached_ = false ;
69
+ }
70
+
71
+ void OnData (const void *audio_data,
72
+ int bits_per_sample,
73
+ int sample_rate,
74
+ size_t number_of_channels,
75
+ size_t number_of_frames,
76
+ absl::optional<int64_t > absolute_capture_timestamp_ms) override {
77
+ RTC_LOG (LS_INFO) << " RTCAudioTrack.AudioSinkConverter OnData bits_per_sample: "
78
+ << bits_per_sample << " sample_rate: " << sample_rate
79
+ << " number_of_channels: " << number_of_channels
80
+ << " number_of_frames: " << number_of_frames
81
+ << " absolute_capture_timestamp_ms: "
82
+ << (absolute_capture_timestamp_ms ? absolute_capture_timestamp_ms.value () : 0 );
83
+
84
+ bool is_locked = os_unfair_lock_trylock (lock_);
85
+ if (!is_locked) {
86
+ RTC_LOG (LS_INFO) << " RTCAudioTrack.AudioSinkConverter OnData already locked, skipping..." ;
87
+ return ;
88
+ }
89
+ bool is_attached = attached_;
90
+ os_unfair_lock_unlock (lock_);
91
+
92
+ if (!is_attached) {
93
+ RTC_LOG (LS_INFO) << " RTCAudioTrack.AudioSinkConverter OnData already detached, skipping..." ;
94
+ return ;
95
+ }
96
+
97
+ /*
98
+ * Convert to CMSampleBuffer
99
+ */
100
+
101
+ if (!(number_of_channels == 1 || number_of_channels == 2 )) {
102
+ NSLog (@" RTCAudioTrack: Only mono or stereo is supported currently. numberOfChannels: %zu " ,
103
+ number_of_channels);
104
+ return ;
105
+ }
106
+
107
+ OSStatus status;
108
+
109
+ AudioChannelLayout acl;
110
+ bzero (&acl, sizeof (acl));
111
+ acl.mChannelLayoutTag =
112
+ number_of_channels == 2 ? kAudioChannelLayoutTag_Stereo : kAudioChannelLayoutTag_Mono ;
113
+
114
+ AudioStreamBasicDescription sd;
115
+ sd.mSampleRate = sample_rate;
116
+ sd.mFormatID = kAudioFormatLinearPCM ;
117
+ sd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked ;
118
+ sd.mFramesPerPacket = 1 ;
119
+ sd.mChannelsPerFrame = number_of_channels;
120
+ sd.mBitsPerChannel = bits_per_sample; /* 16 */
121
+ sd.mBytesPerFrame = sd.mChannelsPerFrame * (sd.mBitsPerChannel / 8 );
122
+ sd.mBytesPerPacket = sd.mBytesPerFrame ;
123
+
124
+ CMSampleTimingInfo timing = {
125
+ CMTimeMake (1 , sample_rate),
126
+ CMTimeMake (total_frames_, sample_rate),
127
+ kCMTimeInvalid ,
128
+ };
129
+
130
+ total_frames_ += number_of_frames; // update the total
131
+
132
+ CMFormatDescriptionRef format = NULL ;
133
+ status = CMAudioFormatDescriptionCreate (
134
+ kCFAllocatorDefault , &sd, sizeof (acl), &acl, 0 , NULL , NULL , &format);
135
+
136
+ if (status != 0 ) {
137
+ NSLog (@" RTCAudioTrack: Failed to create audio format description" );
138
+ return ;
139
+ }
140
+
141
+ CMSampleBufferRef buffer;
142
+ status = CMSampleBufferCreate (kCFAllocatorDefault ,
143
+ NULL ,
144
+ false ,
145
+ NULL ,
146
+ NULL ,
147
+ format,
148
+ (CMItemCount)number_of_frames,
149
+ 1 ,
150
+ &timing,
151
+ 0 ,
152
+ NULL ,
153
+ &buffer);
154
+ if (status != 0 ) {
155
+ NSLog (@" RTCAudioTrack: Failed to allocate sample buffer" );
156
+ return ;
157
+ }
158
+
159
+ AudioBufferList bufferList;
160
+ bufferList.mNumberBuffers = 1 ;
161
+ bufferList.mBuffers [0 ].mNumberChannels = sd.mChannelsPerFrame ;
162
+ bufferList.mBuffers [0 ].mDataByteSize = (UInt32)(number_of_frames * sd.mBytesPerFrame );
163
+ bufferList.mBuffers [0 ].mData = (void *)audio_data;
164
+ status = CMSampleBufferSetDataBufferFromAudioBufferList (
165
+ buffer, kCFAllocatorDefault , kCFAllocatorDefault , 0 , &bufferList);
166
+ if (status != 0 ) {
167
+ NSLog (@" RTCAudioTrack: Failed to convert audio buffer list into sample buffer" );
168
+ return ;
169
+ }
170
+
171
+ // Report back to RTCAudioTrack
172
+ [audio_track_ didCaptureSampleBuffer: buffer];
173
+
174
+ CFRelease (buffer);
175
+ }
176
+ };
177
+ } // namespace webrtc
178
+
179
+ @implementation RTC_OBJC_TYPE (RTCAudioTrack) {
180
+ rtc::scoped_refptr<webrtc::AudioSinkConverter> _audioConverter;
181
+ // Stores weak references to renderers
182
+ NSHashTable *_renderers;
183
+ os_unfair_lock _lock;
184
+ }
21
185
22
186
@synthesize source = _source;
23
187
@@ -43,7 +207,21 @@ - (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)facto
43
207
NSParameterAssert (factory);
44
208
NSParameterAssert (nativeTrack);
45
209
NSParameterAssert (type == RTCMediaStreamTrackTypeAudio);
46
- return [super initWithFactory: factory nativeTrack: nativeTrack type: type];
210
+ if (self = [super initWithFactory: factory nativeTrack: nativeTrack type: type]) {
211
+ RTC_LOG (LS_INFO) << " RTCAudioTrack init" ;
212
+ _renderers = [NSHashTable weakObjectsHashTable ];
213
+ _audioConverter = new rtc::RefCountedObject<webrtc::AudioSinkConverter>(self, &_lock);
214
+ }
215
+
216
+ return self;
217
+ }
218
+
219
+ - (void )dealloc {
220
+ os_unfair_lock_lock (&_lock);
221
+ _audioConverter->TryDetach ();
222
+ os_unfair_lock_unlock (&_lock);
223
+
224
+ RTC_LOG (LS_INFO) << " RTCAudioTrack dealloc" ;
47
225
}
48
226
49
227
- (RTC_OBJC_TYPE(RTCAudioSource) *)source {
@@ -57,11 +235,44 @@ - (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)facto
57
235
return _source;
58
236
}
59
237
238
+ - (void )addRenderer : (id <RTC_OBJC_TYPE(RTCAudioRenderer)>)renderer {
239
+ os_unfair_lock_lock (&_lock);
240
+ [_renderers addObject: renderer];
241
+ _audioConverter->TryAttach ();
242
+ os_unfair_lock_unlock (&_lock);
243
+ }
244
+
245
+ - (void )removeRenderer : (id <RTC_OBJC_TYPE(RTCAudioRenderer)>)renderer {
246
+ os_unfair_lock_lock (&_lock);
247
+ [_renderers removeObject: renderer];
248
+ NSUInteger renderersCount = _renderers.allObjects .count ;
249
+
250
+ if (renderersCount == 0 ) {
251
+ // Detach if no more renderers...
252
+ _audioConverter->TryDetach ();
253
+ }
254
+ os_unfair_lock_unlock (&_lock);
255
+ }
256
+
60
257
#pragma mark - Private
61
258
62
259
- (rtc::scoped_refptr<webrtc::AudioTrackInterface>)nativeAudioTrack {
63
260
return rtc::scoped_refptr<webrtc::AudioTrackInterface>(
64
261
static_cast <webrtc::AudioTrackInterface *>(self.nativeTrack .get ()));
65
262
}
66
263
264
+ - (void )didCaptureSampleBuffer : (CMSampleBufferRef)sampleBuffer {
265
+ bool is_locked = os_unfair_lock_trylock (&_lock);
266
+ if (!is_locked) {
267
+ RTC_LOG (LS_INFO) << " RTCAudioTrack didCaptureSampleBuffer already locked, skipping..." ;
268
+ return ;
269
+ }
270
+ NSArray *renderers = [_renderers allObjects ];
271
+ os_unfair_lock_unlock (&_lock);
272
+
273
+ for (id <RTCAudioRenderer> renderer in renderers) {
274
+ [renderer renderSampleBuffer: sampleBuffer];
275
+ }
276
+ }
277
+
67
278
@end
0 commit comments