blob: 8876e337ab2d1d14c75f5d6ab89ce5ac045d7fd6 [file] [log] [blame]
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/voice_engine/transmit_mixer.h"
12
kwibergb7f89d62016-02-17 10:04:18 -080013#include <memory>
14
aleloi6321b492016-12-05 01:46:09 -080015#include "webrtc/audio/utility/audio_frame_operations.h"
Peter Kastingdce40cf2015-08-24 14:52:23 -070016#include "webrtc/base/format_macros.h"
pbosad856222015-11-27 09:48:36 -080017#include "webrtc/base/logging.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010018#include "webrtc/system_wrappers/include/event_wrapper.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010019#include "webrtc/system_wrappers/include/trace.h"
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000020#include "webrtc/voice_engine/channel.h"
21#include "webrtc/voice_engine/channel_manager.h"
22#include "webrtc/voice_engine/include/voe_external_media.h"
23#include "webrtc/voice_engine/statistics.h"
24#include "webrtc/voice_engine/utility.h"
25#include "webrtc/voice_engine/voe_base_impl.h"
26
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000027namespace webrtc {
28namespace voe {
29
30// TODO(ajm): The thread safety of this is dubious...
31void
32TransmitMixer::OnPeriodicProcess()
33{
34 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
35 "TransmitMixer::OnPeriodicProcess()");
36
37#if defined(WEBRTC_VOICE_ENGINE_TYPING_DETECTION)
solenberg302c9782015-11-24 06:28:22 -080038 bool send_typing_noise_warning = false;
39 bool typing_noise_detected = false;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000040 {
tommi31fc21f2016-01-21 10:37:37 -080041 rtc::CritScope cs(&_critSect);
solenberg302c9782015-11-24 06:28:22 -080042 if (_typingNoiseWarningPending) {
43 send_typing_noise_warning = true;
44 typing_noise_detected = _typingNoiseDetected;
45 _typingNoiseWarningPending = false;
46 }
47 }
48 if (send_typing_noise_warning) {
tommi31fc21f2016-01-21 10:37:37 -080049 rtc::CritScope cs(&_callbackCritSect);
solenberg302c9782015-11-24 06:28:22 -080050 if (_voiceEngineObserverPtr) {
51 if (typing_noise_detected) {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000052 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
53 "TransmitMixer::OnPeriodicProcess() => "
54 "CallbackOnError(VE_TYPING_NOISE_WARNING)");
55 _voiceEngineObserverPtr->CallbackOnError(
56 -1,
57 VE_TYPING_NOISE_WARNING);
58 } else {
59 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
60 "TransmitMixer::OnPeriodicProcess() => "
61 "CallbackOnError(VE_TYPING_NOISE_OFF_WARNING)");
62 _voiceEngineObserverPtr->CallbackOnError(
63 -1,
64 VE_TYPING_NOISE_OFF_WARNING);
65 }
66 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000067 }
68#endif
69
70 bool saturationWarning = false;
71 {
72 // Modify |_saturationWarning| under lock to avoid conflict with write op
73 // in ProcessAudio and also ensure that we don't hold the lock during the
74 // callback.
tommi31fc21f2016-01-21 10:37:37 -080075 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000076 saturationWarning = _saturationWarning;
77 if (_saturationWarning)
78 _saturationWarning = false;
79 }
80
81 if (saturationWarning)
82 {
tommi31fc21f2016-01-21 10:37:37 -080083 rtc::CritScope cs(&_callbackCritSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000084 if (_voiceEngineObserverPtr)
85 {
86 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
87 "TransmitMixer::OnPeriodicProcess() =>"
88 " CallbackOnError(VE_SATURATION_WARNING)");
89 _voiceEngineObserverPtr->CallbackOnError(-1, VE_SATURATION_WARNING);
90 }
91 }
92}
93
94
95void TransmitMixer::PlayNotification(int32_t id,
96 uint32_t durationMs)
97{
98 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
99 "TransmitMixer::PlayNotification(id=%d, durationMs=%d)",
100 id, durationMs);
101
102 // Not implement yet
103}
104
105void TransmitMixer::RecordNotification(int32_t id,
106 uint32_t durationMs)
107{
108 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
109 "TransmitMixer::RecordNotification(id=%d, durationMs=%d)",
110 id, durationMs);
111
112 // Not implement yet
113}
114
115void TransmitMixer::PlayFileEnded(int32_t id)
116{
117 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
118 "TransmitMixer::PlayFileEnded(id=%d)", id);
119
120 assert(id == _filePlayerId);
121
tommi31fc21f2016-01-21 10:37:37 -0800122 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000123
124 _filePlaying = false;
125 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
126 "TransmitMixer::PlayFileEnded() =>"
127 "file player module is shutdown");
128}
129
130void
131TransmitMixer::RecordFileEnded(int32_t id)
132{
133 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
134 "TransmitMixer::RecordFileEnded(id=%d)", id);
135
136 if (id == _fileRecorderId)
137 {
tommi31fc21f2016-01-21 10:37:37 -0800138 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000139 _fileRecording = false;
140 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
141 "TransmitMixer::RecordFileEnded() => fileRecorder module"
142 "is shutdown");
143 } else if (id == _fileCallRecorderId)
144 {
tommi31fc21f2016-01-21 10:37:37 -0800145 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000146 _fileCallRecording = false;
147 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
148 "TransmitMixer::RecordFileEnded() => fileCallRecorder"
149 "module is shutdown");
150 }
151}
152
153int32_t
154TransmitMixer::Create(TransmitMixer*& mixer, uint32_t instanceId)
155{
156 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
157 "TransmitMixer::Create(instanceId=%d)", instanceId);
158 mixer = new TransmitMixer(instanceId);
159 if (mixer == NULL)
160 {
161 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
162 "TransmitMixer::Create() unable to allocate memory"
163 "for mixer");
164 return -1;
165 }
166 return 0;
167}
168
169void
170TransmitMixer::Destroy(TransmitMixer*& mixer)
171{
172 if (mixer)
173 {
174 delete mixer;
175 mixer = NULL;
176 }
177}
178
179TransmitMixer::TransmitMixer(uint32_t instanceId) :
180 _engineStatisticsPtr(NULL),
181 _channelManagerPtr(NULL),
182 audioproc_(NULL),
183 _voiceEngineObserverPtr(NULL),
184 _processThreadPtr(NULL),
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000185 // Avoid conflict with other channels by adding 1024 - 1026,
186 // won't use as much as 1024 channels.
187 _filePlayerId(instanceId + 1024),
188 _fileRecorderId(instanceId + 1025),
189 _fileCallRecorderId(instanceId + 1026),
190 _filePlaying(false),
191 _fileRecording(false),
192 _fileCallRecording(false),
193 _audioLevel(),
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000194#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
195 _typingNoiseWarningPending(false),
196 _typingNoiseDetected(false),
197#endif
198 _saturationWarning(false),
199 _instanceId(instanceId),
200 _mixFileWithMicrophone(false),
201 _captureLevel(0),
202 external_postproc_ptr_(NULL),
203 external_preproc_ptr_(NULL),
204 _mute(false),
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000205 stereo_codec_(false),
206 swap_stereo_channels_(false)
207{
208 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
209 "TransmitMixer::TransmitMixer() - ctor");
210}
211
212TransmitMixer::~TransmitMixer()
213{
214 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
215 "TransmitMixer::~TransmitMixer() - dtor");
216 _monitorModule.DeRegisterObserver();
217 if (_processThreadPtr)
218 {
219 _processThreadPtr->DeRegisterModule(&_monitorModule);
220 }
221 DeRegisterExternalMediaProcessing(kRecordingAllChannelsMixed);
222 DeRegisterExternalMediaProcessing(kRecordingPreprocessing);
223 {
tommi31fc21f2016-01-21 10:37:37 -0800224 rtc::CritScope cs(&_critSect);
kwiberg5a25d952016-08-17 07:31:12 -0700225 if (file_recorder_) {
226 file_recorder_->RegisterModuleFileCallback(NULL);
227 file_recorder_->StopRecording();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000228 }
kwiberg5a25d952016-08-17 07:31:12 -0700229 if (file_call_recorder_) {
230 file_call_recorder_->RegisterModuleFileCallback(NULL);
231 file_call_recorder_->StopRecording();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000232 }
kwiberg5a25d952016-08-17 07:31:12 -0700233 if (file_player_) {
234 file_player_->RegisterModuleFileCallback(NULL);
235 file_player_->StopPlayingFile();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000236 }
237 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000238}
239
240int32_t
241TransmitMixer::SetEngineInformation(ProcessThread& processThread,
242 Statistics& engineStatistics,
243 ChannelManager& channelManager)
244{
245 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
246 "TransmitMixer::SetEngineInformation()");
247
248 _processThreadPtr = &processThread;
249 _engineStatisticsPtr = &engineStatistics;
250 _channelManagerPtr = &channelManager;
251
tommi@webrtc.org3985f012015-02-27 13:36:34 +0000252 _processThreadPtr->RegisterModule(&_monitorModule);
253 _monitorModule.RegisterObserver(*this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000254
255 return 0;
256}
257
258int32_t
259TransmitMixer::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
260{
261 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
262 "TransmitMixer::RegisterVoiceEngineObserver()");
tommi31fc21f2016-01-21 10:37:37 -0800263 rtc::CritScope cs(&_callbackCritSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000264
265 if (_voiceEngineObserverPtr)
266 {
267 _engineStatisticsPtr->SetLastError(
268 VE_INVALID_OPERATION, kTraceError,
269 "RegisterVoiceEngineObserver() observer already enabled");
270 return -1;
271 }
272 _voiceEngineObserverPtr = &observer;
273 return 0;
274}
275
276int32_t
277TransmitMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
278{
279 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
280 "TransmitMixer::SetAudioProcessingModule("
281 "audioProcessingModule=0x%x)",
282 audioProcessingModule);
283 audioproc_ = audioProcessingModule;
284 return 0;
285}
286
Peter Kasting69558702016-01-12 16:26:35 -0800287void TransmitMixer::GetSendCodecInfo(int* max_sample_rate,
288 size_t* max_channels) {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000289 *max_sample_rate = 8000;
290 *max_channels = 1;
291 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
292 it.Increment()) {
293 Channel* channel = it.GetChannel();
294 if (channel->Sending()) {
295 CodecInst codec;
296 channel->GetSendCodec(codec);
297 *max_sample_rate = std::max(*max_sample_rate, codec.plfreq);
298 *max_channels = std::max(*max_channels, codec.channels);
299 }
300 }
301}
302
303int32_t
304TransmitMixer::PrepareDemux(const void* audioSamples,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700305 size_t nSamples,
Peter Kasting69558702016-01-12 16:26:35 -0800306 size_t nChannels,
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000307 uint32_t samplesPerSec,
308 uint16_t totalDelayMS,
309 int32_t clockDrift,
310 uint16_t currentMicLevel,
311 bool keyPressed)
312{
313 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
Peter Kastingdce40cf2015-08-24 14:52:23 -0700314 "TransmitMixer::PrepareDemux(nSamples=%" PRIuS ", "
Peter Kasting69558702016-01-12 16:26:35 -0800315 "nChannels=%" PRIuS ", samplesPerSec=%u, totalDelayMS=%u, "
Peter Kastingdce40cf2015-08-24 14:52:23 -0700316 "clockDrift=%d, currentMicLevel=%u)",
317 nSamples, nChannels, samplesPerSec, totalDelayMS, clockDrift,
318 currentMicLevel);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000319
320 // --- Resample input audio and create/store the initial audio frame
321 GenerateAudioFrame(static_cast<const int16_t*>(audioSamples),
322 nSamples,
323 nChannels,
324 samplesPerSec);
325
326 {
tommi31fc21f2016-01-21 10:37:37 -0800327 rtc::CritScope cs(&_callbackCritSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000328 if (external_preproc_ptr_) {
329 external_preproc_ptr_->Process(-1, kRecordingPreprocessing,
330 _audioFrame.data_,
331 _audioFrame.samples_per_channel_,
332 _audioFrame.sample_rate_hz_,
333 _audioFrame.num_channels_ == 2);
334 }
335 }
336
337 // --- Near-end audio processing.
338 ProcessAudio(totalDelayMS, clockDrift, currentMicLevel, keyPressed);
339
340 if (swap_stereo_channels_ && stereo_codec_)
341 // Only bother swapping if we're using a stereo codec.
342 AudioFrameOperations::SwapStereoChannels(&_audioFrame);
343
344 // --- Annoying typing detection (utilizes the APM/VAD decision)
345#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
346 TypingDetection(keyPressed);
347#endif
348
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000349 // --- Mute signal
solenberg1c2af8e2016-03-24 10:36:00 -0700350 AudioFrameOperations::Mute(&_audioFrame, _mute, _mute);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000351
352 // --- Mix with file (does not affect the mixing frequency)
353 if (_filePlaying)
354 {
355 MixOrReplaceAudioWithFile(_audioFrame.sample_rate_hz_);
356 }
357
358 // --- Record to file
359 bool file_recording = false;
360 {
tommi31fc21f2016-01-21 10:37:37 -0800361 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000362 file_recording = _fileRecording;
363 }
364 if (file_recording)
365 {
366 RecordAudioToFile(_audioFrame.sample_rate_hz_);
367 }
368
369 {
tommi31fc21f2016-01-21 10:37:37 -0800370 rtc::CritScope cs(&_callbackCritSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000371 if (external_postproc_ptr_) {
372 external_postproc_ptr_->Process(-1, kRecordingAllChannelsMixed,
373 _audioFrame.data_,
374 _audioFrame.samples_per_channel_,
375 _audioFrame.sample_rate_hz_,
376 _audioFrame.num_channels_ == 2);
377 }
378 }
379
380 // --- Measure audio level of speech after all processing.
381 _audioLevel.ComputeLevel(_audioFrame);
382 return 0;
383}
384
385int32_t
386TransmitMixer::DemuxAndMix()
387{
388 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
389 "TransmitMixer::DemuxAndMix()");
390
391 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
392 it.Increment())
393 {
394 Channel* channelPtr = it.GetChannel();
henrika@webrtc.org66803482014-04-17 10:45:01 +0000395 if (channelPtr->Sending())
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000396 {
397 // Demultiplex makes a copy of its input.
398 channelPtr->Demultiplex(_audioFrame);
399 channelPtr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
400 }
401 }
402 return 0;
403}
404
405void TransmitMixer::DemuxAndMix(const int voe_channels[],
Peter Kasting69558702016-01-12 16:26:35 -0800406 size_t number_of_voe_channels) {
407 for (size_t i = 0; i < number_of_voe_channels; ++i) {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000408 voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]);
409 voe::Channel* channel_ptr = ch.channel();
410 if (channel_ptr) {
henrika@webrtc.org66803482014-04-17 10:45:01 +0000411 if (channel_ptr->Sending()) {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000412 // Demultiplex makes a copy of its input.
413 channel_ptr->Demultiplex(_audioFrame);
414 channel_ptr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
415 }
416 }
417 }
418}
419
420int32_t
421TransmitMixer::EncodeAndSend()
422{
423 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
424 "TransmitMixer::EncodeAndSend()");
425
426 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
427 it.Increment())
428 {
429 Channel* channelPtr = it.GetChannel();
henrika@webrtc.org66803482014-04-17 10:45:01 +0000430 if (channelPtr->Sending())
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000431 {
432 channelPtr->EncodeAndSend();
433 }
434 }
435 return 0;
436}
437
438void TransmitMixer::EncodeAndSend(const int voe_channels[],
Peter Kasting69558702016-01-12 16:26:35 -0800439 size_t number_of_voe_channels) {
440 for (size_t i = 0; i < number_of_voe_channels; ++i) {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000441 voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]);
442 voe::Channel* channel_ptr = ch.channel();
henrika@webrtc.org66803482014-04-17 10:45:01 +0000443 if (channel_ptr && channel_ptr->Sending())
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000444 channel_ptr->EncodeAndSend();
445 }
446}
447
448uint32_t TransmitMixer::CaptureLevel() const
449{
450 return _captureLevel;
451}
452
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000453int32_t
454TransmitMixer::StopSend()
455{
456 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
457 "TransmitMixer::StopSend()");
458 _audioLevel.Clear();
459 return 0;
460}
461
462int TransmitMixer::StartPlayingFileAsMicrophone(const char* fileName,
463 bool loop,
464 FileFormats format,
465 int startPosition,
466 float volumeScaling,
467 int stopPosition,
468 const CodecInst* codecInst)
469{
470 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
471 "TransmitMixer::StartPlayingFileAsMicrophone("
472 "fileNameUTF8[]=%s,loop=%d, format=%d, volumeScaling=%5.3f,"
473 " startPosition=%d, stopPosition=%d)", fileName, loop,
474 format, volumeScaling, startPosition, stopPosition);
475
476 if (_filePlaying)
477 {
478 _engineStatisticsPtr->SetLastError(
479 VE_ALREADY_PLAYING, kTraceWarning,
480 "StartPlayingFileAsMicrophone() is already playing");
481 return 0;
482 }
483
tommi31fc21f2016-01-21 10:37:37 -0800484 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000485
486 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700487 if (file_player_) {
488 file_player_->RegisterModuleFileCallback(NULL);
489 file_player_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000490 }
491
492 // Dynamically create the instance
kwiberg5a25d952016-08-17 07:31:12 -0700493 file_player_ =
kwiberg5b356f42016-09-08 04:32:33 -0700494 FilePlayer::CreateFilePlayer(_filePlayerId, (const FileFormats)format);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000495
kwiberg5a25d952016-08-17 07:31:12 -0700496 if (!file_player_) {
497 _engineStatisticsPtr->SetLastError(
498 VE_INVALID_ARGUMENT, kTraceError,
499 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
500 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000501 }
502
503 const uint32_t notificationTime(0);
504
kwiberg5a25d952016-08-17 07:31:12 -0700505 if (file_player_->StartPlayingFile(
506 fileName, loop, startPosition, volumeScaling, notificationTime,
507 stopPosition, (const CodecInst*)codecInst) != 0) {
508 _engineStatisticsPtr->SetLastError(
509 VE_BAD_FILE, kTraceError,
510 "StartPlayingFile() failed to start file playout");
511 file_player_->StopPlayingFile();
512 file_player_.reset();
513 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000514 }
515
kwiberg5a25d952016-08-17 07:31:12 -0700516 file_player_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000517 _filePlaying = true;
518
519 return 0;
520}
521
522int TransmitMixer::StartPlayingFileAsMicrophone(InStream* stream,
523 FileFormats format,
524 int startPosition,
525 float volumeScaling,
526 int stopPosition,
527 const CodecInst* codecInst)
528{
529 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
530 "TransmitMixer::StartPlayingFileAsMicrophone(format=%d,"
531 " volumeScaling=%5.3f, startPosition=%d, stopPosition=%d)",
532 format, volumeScaling, startPosition, stopPosition);
533
534 if (stream == NULL)
535 {
536 _engineStatisticsPtr->SetLastError(
537 VE_BAD_FILE, kTraceError,
538 "StartPlayingFileAsMicrophone() NULL as input stream");
539 return -1;
540 }
541
542 if (_filePlaying)
543 {
544 _engineStatisticsPtr->SetLastError(
545 VE_ALREADY_PLAYING, kTraceWarning,
546 "StartPlayingFileAsMicrophone() is already playing");
547 return 0;
548 }
549
tommi31fc21f2016-01-21 10:37:37 -0800550 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000551
552 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700553 if (file_player_) {
554 file_player_->RegisterModuleFileCallback(NULL);
555 file_player_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000556 }
557
558 // Dynamically create the instance
kwiberg5a25d952016-08-17 07:31:12 -0700559 file_player_ =
kwiberg5b356f42016-09-08 04:32:33 -0700560 FilePlayer::CreateFilePlayer(_filePlayerId, (const FileFormats)format);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000561
kwiberg5a25d952016-08-17 07:31:12 -0700562 if (!file_player_) {
563 _engineStatisticsPtr->SetLastError(
564 VE_INVALID_ARGUMENT, kTraceWarning,
565 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
566 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000567 }
568
569 const uint32_t notificationTime(0);
570
kwiberg4ec01d92016-08-22 08:43:54 -0700571 if (file_player_->StartPlayingFile(stream, startPosition, volumeScaling,
572 notificationTime, stopPosition,
573 (const CodecInst*)codecInst) != 0) {
kwiberg5a25d952016-08-17 07:31:12 -0700574 _engineStatisticsPtr->SetLastError(
575 VE_BAD_FILE, kTraceError,
576 "StartPlayingFile() failed to start file playout");
577 file_player_->StopPlayingFile();
578 file_player_.reset();
579 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000580 }
kwiberg5a25d952016-08-17 07:31:12 -0700581 file_player_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000582 _filePlaying = true;
583
584 return 0;
585}
586
587int TransmitMixer::StopPlayingFileAsMicrophone()
588{
589 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
590 "TransmitMixer::StopPlayingFileAsMicrophone()");
591
592 if (!_filePlaying)
593 {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000594 return 0;
595 }
596
tommi31fc21f2016-01-21 10:37:37 -0800597 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000598
kwiberg5a25d952016-08-17 07:31:12 -0700599 if (file_player_->StopPlayingFile() != 0) {
600 _engineStatisticsPtr->SetLastError(
601 VE_CANNOT_STOP_PLAYOUT, kTraceError,
602 "StopPlayingFile() couldnot stop playing file");
603 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000604 }
605
kwiberg5a25d952016-08-17 07:31:12 -0700606 file_player_->RegisterModuleFileCallback(NULL);
607 file_player_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000608 _filePlaying = false;
609
610 return 0;
611}
612
613int TransmitMixer::IsPlayingFileAsMicrophone() const
614{
615 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
616 "TransmitMixer::IsPlayingFileAsMicrophone()");
617 return _filePlaying;
618}
619
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000620int TransmitMixer::StartRecordingMicrophone(const char* fileName,
621 const CodecInst* codecInst)
622{
623 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
624 "TransmitMixer::StartRecordingMicrophone(fileName=%s)",
625 fileName);
626
tommi31fc21f2016-01-21 10:37:37 -0800627 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000628
629 if (_fileRecording)
630 {
631 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
632 "StartRecordingMicrophone() is already recording");
633 return 0;
634 }
635
636 FileFormats format;
637 const uint32_t notificationTime(0); // Not supported in VoE
638 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
639
Peter Kasting69558702016-01-12 16:26:35 -0800640 if (codecInst != NULL && codecInst->channels > 2)
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000641 {
642 _engineStatisticsPtr->SetLastError(
643 VE_BAD_ARGUMENT, kTraceError,
644 "StartRecordingMicrophone() invalid compression");
645 return (-1);
646 }
647 if (codecInst == NULL)
648 {
649 format = kFileFormatPcm16kHzFile;
650 codecInst = &dummyCodec;
651 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
652 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
653 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
654 {
655 format = kFileFormatWavFile;
656 } else
657 {
658 format = kFileFormatCompressedFile;
659 }
660
661 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700662 if (file_recorder_) {
663 file_recorder_->RegisterModuleFileCallback(NULL);
664 file_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000665 }
666
kwiberg5a25d952016-08-17 07:31:12 -0700667 file_recorder_ = FileRecorder::CreateFileRecorder(
668 _fileRecorderId, (const FileFormats)format);
669 if (!file_recorder_) {
670 _engineStatisticsPtr->SetLastError(
671 VE_INVALID_ARGUMENT, kTraceError,
672 "StartRecordingMicrophone() fileRecorder format isnot correct");
673 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000674 }
675
kwiberg5a25d952016-08-17 07:31:12 -0700676 if (file_recorder_->StartRecordingAudioFile(
677 fileName, (const CodecInst&)*codecInst, notificationTime) != 0) {
678 _engineStatisticsPtr->SetLastError(
679 VE_BAD_FILE, kTraceError,
680 "StartRecordingAudioFile() failed to start file recording");
681 file_recorder_->StopRecording();
682 file_recorder_.reset();
683 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000684 }
kwiberg5a25d952016-08-17 07:31:12 -0700685 file_recorder_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000686 _fileRecording = true;
687
688 return 0;
689}
690
691int TransmitMixer::StartRecordingMicrophone(OutStream* stream,
692 const CodecInst* codecInst)
693{
694 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
695 "TransmitMixer::StartRecordingMicrophone()");
696
tommi31fc21f2016-01-21 10:37:37 -0800697 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000698
699 if (_fileRecording)
700 {
701 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
702 "StartRecordingMicrophone() is already recording");
703 return 0;
704 }
705
706 FileFormats format;
707 const uint32_t notificationTime(0); // Not supported in VoE
708 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
709
710 if (codecInst != NULL && codecInst->channels != 1)
711 {
712 _engineStatisticsPtr->SetLastError(
713 VE_BAD_ARGUMENT, kTraceError,
714 "StartRecordingMicrophone() invalid compression");
715 return (-1);
716 }
717 if (codecInst == NULL)
718 {
719 format = kFileFormatPcm16kHzFile;
720 codecInst = &dummyCodec;
721 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
722 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
723 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
724 {
725 format = kFileFormatWavFile;
726 } else
727 {
728 format = kFileFormatCompressedFile;
729 }
730
731 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700732 if (file_recorder_) {
733 file_recorder_->RegisterModuleFileCallback(NULL);
734 file_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000735 }
736
kwiberg5a25d952016-08-17 07:31:12 -0700737 file_recorder_ = FileRecorder::CreateFileRecorder(
738 _fileRecorderId, (const FileFormats)format);
739 if (!file_recorder_) {
740 _engineStatisticsPtr->SetLastError(
741 VE_INVALID_ARGUMENT, kTraceError,
742 "StartRecordingMicrophone() fileRecorder format isnot correct");
743 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000744 }
745
kwiberg4ec01d92016-08-22 08:43:54 -0700746 if (file_recorder_->StartRecordingAudioFile(stream, *codecInst,
kwiberg5a25d952016-08-17 07:31:12 -0700747 notificationTime) != 0) {
748 _engineStatisticsPtr->SetLastError(
749 VE_BAD_FILE, kTraceError,
750 "StartRecordingAudioFile() failed to start file recording");
751 file_recorder_->StopRecording();
752 file_recorder_.reset();
753 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000754 }
755
kwiberg5a25d952016-08-17 07:31:12 -0700756 file_recorder_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000757 _fileRecording = true;
758
759 return 0;
760}
761
762
763int TransmitMixer::StopRecordingMicrophone()
764{
765 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
766 "TransmitMixer::StopRecordingMicrophone()");
767
tommi31fc21f2016-01-21 10:37:37 -0800768 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000769
770 if (!_fileRecording)
771 {
772 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
773 "StopRecordingMicrophone() isnot recording");
774 return 0;
775 }
776
kwiberg5a25d952016-08-17 07:31:12 -0700777 if (file_recorder_->StopRecording() != 0) {
778 _engineStatisticsPtr->SetLastError(
779 VE_STOP_RECORDING_FAILED, kTraceError,
780 "StopRecording(), could not stop recording");
781 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000782 }
kwiberg5a25d952016-08-17 07:31:12 -0700783 file_recorder_->RegisterModuleFileCallback(NULL);
784 file_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000785 _fileRecording = false;
786
787 return 0;
788}
789
790int TransmitMixer::StartRecordingCall(const char* fileName,
791 const CodecInst* codecInst)
792{
793 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
794 "TransmitMixer::StartRecordingCall(fileName=%s)", fileName);
795
796 if (_fileCallRecording)
797 {
798 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
799 "StartRecordingCall() is already recording");
800 return 0;
801 }
802
803 FileFormats format;
804 const uint32_t notificationTime(0); // Not supported in VoE
805 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
806
807 if (codecInst != NULL && codecInst->channels != 1)
808 {
809 _engineStatisticsPtr->SetLastError(
810 VE_BAD_ARGUMENT, kTraceError,
811 "StartRecordingCall() invalid compression");
812 return (-1);
813 }
814 if (codecInst == NULL)
815 {
816 format = kFileFormatPcm16kHzFile;
817 codecInst = &dummyCodec;
818 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
819 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
820 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
821 {
822 format = kFileFormatWavFile;
823 } else
824 {
825 format = kFileFormatCompressedFile;
826 }
827
tommi31fc21f2016-01-21 10:37:37 -0800828 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000829
830 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700831 if (file_call_recorder_) {
832 file_call_recorder_->RegisterModuleFileCallback(NULL);
833 file_call_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000834 }
835
kwiberg5a25d952016-08-17 07:31:12 -0700836 file_call_recorder_ = FileRecorder::CreateFileRecorder(
837 _fileCallRecorderId, (const FileFormats)format);
838 if (!file_call_recorder_) {
839 _engineStatisticsPtr->SetLastError(
840 VE_INVALID_ARGUMENT, kTraceError,
841 "StartRecordingCall() fileRecorder format isnot correct");
842 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000843 }
844
kwiberg5a25d952016-08-17 07:31:12 -0700845 if (file_call_recorder_->StartRecordingAudioFile(
846 fileName, (const CodecInst&)*codecInst, notificationTime) != 0) {
847 _engineStatisticsPtr->SetLastError(
848 VE_BAD_FILE, kTraceError,
849 "StartRecordingAudioFile() failed to start file recording");
850 file_call_recorder_->StopRecording();
851 file_call_recorder_.reset();
852 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000853 }
kwiberg5a25d952016-08-17 07:31:12 -0700854 file_call_recorder_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000855 _fileCallRecording = true;
856
857 return 0;
858}
859
860int TransmitMixer::StartRecordingCall(OutStream* stream,
861 const CodecInst* codecInst)
862{
863 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
864 "TransmitMixer::StartRecordingCall()");
865
866 if (_fileCallRecording)
867 {
868 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
869 "StartRecordingCall() is already recording");
870 return 0;
871 }
872
873 FileFormats format;
874 const uint32_t notificationTime(0); // Not supported in VoE
875 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
876
877 if (codecInst != NULL && codecInst->channels != 1)
878 {
879 _engineStatisticsPtr->SetLastError(
880 VE_BAD_ARGUMENT, kTraceError,
881 "StartRecordingCall() invalid compression");
882 return (-1);
883 }
884 if (codecInst == NULL)
885 {
886 format = kFileFormatPcm16kHzFile;
887 codecInst = &dummyCodec;
888 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
889 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
890 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
891 {
892 format = kFileFormatWavFile;
893 } else
894 {
895 format = kFileFormatCompressedFile;
896 }
897
tommi31fc21f2016-01-21 10:37:37 -0800898 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000899
900 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700901 if (file_call_recorder_) {
902 file_call_recorder_->RegisterModuleFileCallback(NULL);
903 file_call_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000904 }
905
kwiberg5a25d952016-08-17 07:31:12 -0700906 file_call_recorder_ = FileRecorder::CreateFileRecorder(
907 _fileCallRecorderId, (const FileFormats)format);
908 if (!file_call_recorder_) {
909 _engineStatisticsPtr->SetLastError(
910 VE_INVALID_ARGUMENT, kTraceError,
911 "StartRecordingCall() fileRecorder format isnot correct");
912 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000913 }
914
kwiberg4ec01d92016-08-22 08:43:54 -0700915 if (file_call_recorder_->StartRecordingAudioFile(stream, *codecInst,
kwiberg5a25d952016-08-17 07:31:12 -0700916 notificationTime) != 0) {
917 _engineStatisticsPtr->SetLastError(
918 VE_BAD_FILE, kTraceError,
919 "StartRecordingAudioFile() failed to start file recording");
920 file_call_recorder_->StopRecording();
921 file_call_recorder_.reset();
922 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000923 }
924
kwiberg5a25d952016-08-17 07:31:12 -0700925 file_call_recorder_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000926 _fileCallRecording = true;
927
928 return 0;
929}
930
931int TransmitMixer::StopRecordingCall()
932{
933 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
934 "TransmitMixer::StopRecordingCall()");
935
936 if (!_fileCallRecording)
937 {
938 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
939 "StopRecordingCall() file isnot recording");
940 return -1;
941 }
942
tommi31fc21f2016-01-21 10:37:37 -0800943 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000944
kwiberg5a25d952016-08-17 07:31:12 -0700945 if (file_call_recorder_->StopRecording() != 0) {
946 _engineStatisticsPtr->SetLastError(
947 VE_STOP_RECORDING_FAILED, kTraceError,
948 "StopRecording(), could not stop recording");
949 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000950 }
951
kwiberg5a25d952016-08-17 07:31:12 -0700952 file_call_recorder_->RegisterModuleFileCallback(NULL);
953 file_call_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000954 _fileCallRecording = false;
955
956 return 0;
957}
958
959void
960TransmitMixer::SetMixWithMicStatus(bool mix)
961{
962 _mixFileWithMicrophone = mix;
963}
964
965int TransmitMixer::RegisterExternalMediaProcessing(
966 VoEMediaProcess* object,
967 ProcessingTypes type) {
968 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
969 "TransmitMixer::RegisterExternalMediaProcessing()");
970
tommi31fc21f2016-01-21 10:37:37 -0800971 rtc::CritScope cs(&_callbackCritSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000972 if (!object) {
973 return -1;
974 }
975
976 // Store the callback object according to the processing type.
977 if (type == kRecordingAllChannelsMixed) {
978 external_postproc_ptr_ = object;
979 } else if (type == kRecordingPreprocessing) {
980 external_preproc_ptr_ = object;
981 } else {
982 return -1;
983 }
984 return 0;
985}
986
987int TransmitMixer::DeRegisterExternalMediaProcessing(ProcessingTypes type) {
988 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
989 "TransmitMixer::DeRegisterExternalMediaProcessing()");
990
tommi31fc21f2016-01-21 10:37:37 -0800991 rtc::CritScope cs(&_callbackCritSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000992 if (type == kRecordingAllChannelsMixed) {
993 external_postproc_ptr_ = NULL;
994 } else if (type == kRecordingPreprocessing) {
995 external_preproc_ptr_ = NULL;
996 } else {
997 return -1;
998 }
999 return 0;
1000}
1001
1002int
1003TransmitMixer::SetMute(bool enable)
1004{
1005 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1006 "TransmitMixer::SetMute(enable=%d)", enable);
1007 _mute = enable;
1008 return 0;
1009}
1010
1011bool
1012TransmitMixer::Mute() const
1013{
1014 return _mute;
1015}
1016
1017int8_t TransmitMixer::AudioLevel() const
1018{
1019 // Speech + file level [0,9]
1020 return _audioLevel.Level();
1021}
1022
1023int16_t TransmitMixer::AudioLevelFullRange() const
1024{
1025 // Speech + file level [0,32767]
1026 return _audioLevel.LevelFullRange();
1027}
1028
1029bool TransmitMixer::IsRecordingCall()
1030{
1031 return _fileCallRecording;
1032}
1033
1034bool TransmitMixer::IsRecordingMic()
1035{
tommi31fc21f2016-01-21 10:37:37 -08001036 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001037 return _fileRecording;
1038}
1039
1040void TransmitMixer::GenerateAudioFrame(const int16_t* audio,
Peter Kastingdce40cf2015-08-24 14:52:23 -07001041 size_t samples_per_channel,
Peter Kasting69558702016-01-12 16:26:35 -08001042 size_t num_channels,
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001043 int sample_rate_hz) {
1044 int codec_rate;
Peter Kasting69558702016-01-12 16:26:35 -08001045 size_t num_codec_channels;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001046 GetSendCodecInfo(&codec_rate, &num_codec_channels);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001047 stereo_codec_ = num_codec_channels == 2;
1048
Alejandro Luebscdfe20b2015-09-23 12:49:12 -07001049 // We want to process at the lowest rate possible without losing information.
1050 // Choose the lowest native rate at least equal to the input and codec rates.
1051 const int min_processing_rate = std::min(sample_rate_hz, codec_rate);
1052 for (size_t i = 0; i < AudioProcessing::kNumNativeSampleRates; ++i) {
1053 _audioFrame.sample_rate_hz_ = AudioProcessing::kNativeSampleRatesHz[i];
1054 if (_audioFrame.sample_rate_hz_ >= min_processing_rate) {
1055 break;
1056 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001057 }
Alejandro Luebscdfe20b2015-09-23 12:49:12 -07001058 _audioFrame.num_channels_ = std::min(num_channels, num_codec_channels);
1059 RemixAndResample(audio, samples_per_channel, num_channels, sample_rate_hz,
1060 &resampler_, &_audioFrame);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001061}
1062
1063int32_t TransmitMixer::RecordAudioToFile(
1064 uint32_t mixingFrequency)
1065{
tommi31fc21f2016-01-21 10:37:37 -08001066 rtc::CritScope cs(&_critSect);
kwiberg5a25d952016-08-17 07:31:12 -07001067 if (!file_recorder_) {
1068 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1069 "TransmitMixer::RecordAudioToFile() filerecorder doesnot"
1070 "exist");
1071 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001072 }
1073
kwiberg5a25d952016-08-17 07:31:12 -07001074 if (file_recorder_->RecordAudioToFile(_audioFrame) != 0) {
1075 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1076 "TransmitMixer::RecordAudioToFile() file recording"
1077 "failed");
1078 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001079 }
1080
1081 return 0;
1082}
1083
1084int32_t TransmitMixer::MixOrReplaceAudioWithFile(
1085 int mixingFrequency)
1086{
kwibergb7f89d62016-02-17 10:04:18 -08001087 std::unique_ptr<int16_t[]> fileBuffer(new int16_t[640]);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001088
Peter Kastingdce40cf2015-08-24 14:52:23 -07001089 size_t fileSamples(0);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001090 {
tommi31fc21f2016-01-21 10:37:37 -08001091 rtc::CritScope cs(&_critSect);
kwiberg5a25d952016-08-17 07:31:12 -07001092 if (!file_player_) {
1093 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1094 "TransmitMixer::MixOrReplaceAudioWithFile()"
1095 "fileplayer doesnot exist");
1096 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001097 }
1098
kwiberg4ec01d92016-08-22 08:43:54 -07001099 if (file_player_->Get10msAudioFromFile(fileBuffer.get(), &fileSamples,
kwiberg5a25d952016-08-17 07:31:12 -07001100 mixingFrequency) == -1) {
1101 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1102 "TransmitMixer::MixOrReplaceAudioWithFile() file"
1103 " mixing failed");
1104 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001105 }
1106 }
1107
1108 assert(_audioFrame.samples_per_channel_ == fileSamples);
1109
1110 if (_mixFileWithMicrophone)
1111 {
1112 // Currently file stream is always mono.
1113 // TODO(xians): Change the code when FilePlayer supports real stereo.
1114 MixWithSat(_audioFrame.data_,
1115 _audioFrame.num_channels_,
1116 fileBuffer.get(),
1117 1,
1118 fileSamples);
1119 } else
1120 {
1121 // Replace ACM audio with file.
1122 // Currently file stream is always mono.
1123 // TODO(xians): Change the code when FilePlayer supports real stereo.
1124 _audioFrame.UpdateFrame(-1,
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +00001125 0xFFFFFFFF,
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001126 fileBuffer.get(),
1127 fileSamples,
1128 mixingFrequency,
1129 AudioFrame::kNormalSpeech,
1130 AudioFrame::kVadUnknown,
1131 1);
1132 }
1133 return 0;
1134}
1135
1136void TransmitMixer::ProcessAudio(int delay_ms, int clock_drift,
1137 int current_mic_level, bool key_pressed) {
1138 if (audioproc_->set_stream_delay_ms(delay_ms) != 0) {
pbosad856222015-11-27 09:48:36 -08001139 // Silently ignore this failure to avoid flooding the logs.
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001140 }
1141
1142 GainControl* agc = audioproc_->gain_control();
1143 if (agc->set_stream_analog_level(current_mic_level) != 0) {
pbosad856222015-11-27 09:48:36 -08001144 LOG(LS_ERROR) << "set_stream_analog_level failed: current_mic_level = "
1145 << current_mic_level;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001146 assert(false);
1147 }
1148
1149 EchoCancellation* aec = audioproc_->echo_cancellation();
1150 if (aec->is_drift_compensation_enabled()) {
1151 aec->set_stream_drift_samples(clock_drift);
1152 }
1153
1154 audioproc_->set_stream_key_pressed(key_pressed);
1155
1156 int err = audioproc_->ProcessStream(&_audioFrame);
1157 if (err != 0) {
1158 LOG(LS_ERROR) << "ProcessStream() error: " << err;
1159 assert(false);
1160 }
1161
1162 // Store new capture level. Only updated when analog AGC is enabled.
1163 _captureLevel = agc->stream_analog_level();
1164
tommi31fc21f2016-01-21 10:37:37 -08001165 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001166 // Triggers a callback in OnPeriodicProcess().
1167 _saturationWarning |= agc->stream_is_saturated();
1168}
1169
1170#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1171void TransmitMixer::TypingDetection(bool keyPressed)
1172{
1173 // We let the VAD determine if we're using this feature or not.
1174 if (_audioFrame.vad_activity_ == AudioFrame::kVadUnknown) {
1175 return;
1176 }
1177
1178 bool vadActive = _audioFrame.vad_activity_ == AudioFrame::kVadActive;
1179 if (_typingDetection.Process(keyPressed, vadActive)) {
tommi31fc21f2016-01-21 10:37:37 -08001180 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001181 _typingNoiseWarningPending = true;
1182 _typingNoiseDetected = true;
1183 } else {
tommi31fc21f2016-01-21 10:37:37 -08001184 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001185 // If there is already a warning pending, do not change the state.
1186 // Otherwise set a warning pending if last callback was for noise detected.
1187 if (!_typingNoiseWarningPending && _typingNoiseDetected) {
1188 _typingNoiseWarningPending = true;
1189 _typingNoiseDetected = false;
1190 }
1191 }
1192}
1193#endif
1194
1195int TransmitMixer::GetMixingFrequency()
1196{
1197 assert(_audioFrame.sample_rate_hz_ != 0);
1198 return _audioFrame.sample_rate_hz_;
1199}
1200
1201#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1202int TransmitMixer::TimeSinceLastTyping(int &seconds)
1203{
1204 // We check in VoEAudioProcessingImpl that this is only called when
1205 // typing detection is active.
1206 seconds = _typingDetection.TimeSinceLastDetectionInSeconds();
1207 return 0;
1208}
1209#endif
1210
1211#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1212int TransmitMixer::SetTypingDetectionParameters(int timeWindow,
1213 int costPerTyping,
1214 int reportingThreshold,
1215 int penaltyDecay,
1216 int typeEventDelay)
1217{
1218 _typingDetection.SetParameters(timeWindow,
1219 costPerTyping,
1220 reportingThreshold,
1221 penaltyDecay,
1222 typeEventDelay,
1223 0);
1224 return 0;
1225}
1226#endif
1227
1228void TransmitMixer::EnableStereoChannelSwapping(bool enable) {
1229 swap_stereo_channels_ = enable;
1230}
1231
1232bool TransmitMixer::IsStereoChannelSwappingEnabled() {
1233 return swap_stereo_channels_;
1234}
1235
1236} // namespace voe
1237} // namespace webrtc