blob: c497cc1e165dcd2c029ef6de03189a8ef47f2fc5 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000011#include "webrtc/voice_engine/output_mixer.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000013#include "webrtc/modules/audio_processing/include/audio_processing.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020014#include "webrtc/rtc_base/format_macros.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010015#include "webrtc/system_wrappers/include/file_wrapper.h"
16#include "webrtc/system_wrappers/include/trace.h"
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000017#include "webrtc/voice_engine/statistics.h"
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000018#include "webrtc/voice_engine/utility.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000019
20namespace webrtc {
niklase@google.com470e71d2011-07-07 08:21:25 +000021namespace voe {
22
23void
pbos@webrtc.org92135212013-05-14 08:31:39 +000024OutputMixer::NewMixedAudio(int32_t id,
niklase@google.com470e71d2011-07-07 08:21:25 +000025 const AudioFrame& generalAudioFrame,
26 const AudioFrame** uniqueAudioFrames,
pbos@webrtc.org92135212013-05-14 08:31:39 +000027 uint32_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +000028{
29 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
30 "OutputMixer::NewMixedAudio(id=%d, size=%u)", id, size);
31
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +000032 _audioFrame.CopyFrom(generalAudioFrame);
andrew@webrtc.org63a50982012-05-02 23:56:37 +000033 _audioFrame.id_ = id;
niklase@google.com470e71d2011-07-07 08:21:25 +000034}
35
pbos@webrtc.org92135212013-05-14 08:31:39 +000036void OutputMixer::PlayNotification(int32_t id, uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000037{
38 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
39 "OutputMixer::PlayNotification(id=%d, durationMs=%d)",
40 id, durationMs);
41 // Not implement yet
42}
43
pbos@webrtc.org92135212013-05-14 08:31:39 +000044void OutputMixer::RecordNotification(int32_t id,
45 uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000046{
47 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
48 "OutputMixer::RecordNotification(id=%d, durationMs=%d)",
49 id, durationMs);
50
51 // Not implement yet
52}
53
pbos@webrtc.org92135212013-05-14 08:31:39 +000054void OutputMixer::PlayFileEnded(int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000055{
56 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
57 "OutputMixer::PlayFileEnded(id=%d)", id);
58
59 // not needed
60}
61
pbos@webrtc.org92135212013-05-14 08:31:39 +000062void OutputMixer::RecordFileEnded(int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000063{
64 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
65 "OutputMixer::RecordFileEnded(id=%d)", id);
66 assert(id == _instanceId);
67
tommi31fc21f2016-01-21 10:37:37 -080068 rtc::CritScope cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +000069 _outputFileRecording = false;
70 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
71 "OutputMixer::RecordFileEnded() =>"
72 "output file recorder module is shutdown");
73}
74
pbos@webrtc.org6141e132013-04-09 10:09:10 +000075int32_t
pbos@webrtc.org92135212013-05-14 08:31:39 +000076OutputMixer::Create(OutputMixer*& mixer, uint32_t instanceId)
niklase@google.com470e71d2011-07-07 08:21:25 +000077{
78 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
79 "OutputMixer::Create(instanceId=%d)", instanceId);
80 mixer = new OutputMixer(instanceId);
81 if (mixer == NULL)
82 {
83 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
84 "OutputMixer::Create() unable to allocate memory for"
85 "mixer");
86 return -1;
87 }
88 return 0;
89}
90
pbos@webrtc.org92135212013-05-14 08:31:39 +000091OutputMixer::OutputMixer(uint32_t instanceId) :
andrew@webrtc.orgc4f129f2011-11-10 03:41:22 +000092 _mixerModule(*AudioConferenceMixer::Create(instanceId)),
xians@google.com22963ab2011-08-03 12:40:23 +000093 _instanceId(instanceId),
xians@google.com22963ab2011-08-03 12:40:23 +000094 _mixingFrequencyHz(8000),
xians@google.com22963ab2011-08-03 12:40:23 +000095 _outputFileRecording(false)
niklase@google.com470e71d2011-07-07 08:21:25 +000096{
97 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
98 "OutputMixer::OutputMixer() - ctor");
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +000099
minyuel0f4b3732015-08-31 16:04:32 +0200100 if (_mixerModule.RegisterMixedStreamCallback(this) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000101 {
102 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
103 "OutputMixer::OutputMixer() failed to register mixer"
104 "callbacks");
105 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000106}
107
108void
109OutputMixer::Destroy(OutputMixer*& mixer)
110{
111 if (mixer)
112 {
113 delete mixer;
114 mixer = NULL;
115 }
116}
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000117
niklase@google.com470e71d2011-07-07 08:21:25 +0000118OutputMixer::~OutputMixer()
119{
120 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
121 "OutputMixer::~OutputMixer() - dtor");
niklase@google.com470e71d2011-07-07 08:21:25 +0000122 {
tommi31fc21f2016-01-21 10:37:37 -0800123 rtc::CritScope cs(&_fileCritSect);
kwiberg5a25d952016-08-17 07:31:12 -0700124 if (output_file_recorder_) {
125 output_file_recorder_->RegisterModuleFileCallback(NULL);
126 output_file_recorder_->StopRecording();
niklase@google.com470e71d2011-07-07 08:21:25 +0000127 }
128 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000129 _mixerModule.UnRegisterMixedStreamCallback();
130 delete &_mixerModule;
niklase@google.com470e71d2011-07-07 08:21:25 +0000131}
132
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000133int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000134OutputMixer::SetEngineInformation(voe::Statistics& engineStatistics)
135{
136 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
137 "OutputMixer::SetEngineInformation()");
138 _engineStatisticsPtr = &engineStatistics;
139 return 0;
140}
141
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000142int32_t
143OutputMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
niklase@google.com470e71d2011-07-07 08:21:25 +0000144{
145 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
146 "OutputMixer::SetAudioProcessingModule("
147 "audioProcessingModule=0x%x)", audioProcessingModule);
148 _audioProcessingModulePtr = audioProcessingModule;
149 return 0;
150}
151
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000152int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000153OutputMixer::SetMixabilityStatus(MixerParticipant& participant,
pbos@webrtc.org92135212013-05-14 08:31:39 +0000154 bool mixable)
niklase@google.com470e71d2011-07-07 08:21:25 +0000155{
minyuel0f4b3732015-08-31 16:04:32 +0200156 return _mixerModule.SetMixabilityStatus(&participant, mixable);
niklase@google.com470e71d2011-07-07 08:21:25 +0000157}
158
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000159int32_t
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000160OutputMixer::SetAnonymousMixabilityStatus(MixerParticipant& participant,
pbos@webrtc.org92135212013-05-14 08:31:39 +0000161 bool mixable)
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000162{
minyuel0f4b3732015-08-31 16:04:32 +0200163 return _mixerModule.SetAnonymousMixabilityStatus(&participant, mixable);
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000164}
165
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000166int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000167OutputMixer::MixActiveChannels()
168{
pbosa26ac922016-02-25 04:50:01 -0800169 _mixerModule.Process();
170 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000171}
172
niklase@google.com470e71d2011-07-07 08:21:25 +0000173int OutputMixer::StartRecordingPlayout(const char* fileName,
174 const CodecInst* codecInst)
175{
176 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
177 "OutputMixer::StartRecordingPlayout(fileName=%s)", fileName);
178
179 if (_outputFileRecording)
180 {
181 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
182 "StartRecordingPlayout() is already recording");
183 return 0;
184 }
185
186 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000187 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000188 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
189
niklas.enbom@webrtc.org40197d72012-03-26 08:45:47 +0000190 if ((codecInst != NULL) &&
191 ((codecInst->channels < 1) || (codecInst->channels > 2)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000192 {
193 _engineStatisticsPtr->SetLastError(
194 VE_BAD_ARGUMENT, kTraceError,
195 "StartRecordingPlayout() invalid compression");
196 return(-1);
197 }
198 if(codecInst == NULL)
199 {
200 format = kFileFormatPcm16kHzFile;
201 codecInst=&dummyCodec;
202 }
203 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
204 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
205 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
206 {
207 format = kFileFormatWavFile;
208 }
209 else
210 {
211 format = kFileFormatCompressedFile;
212 }
213
tommi31fc21f2016-01-21 10:37:37 -0800214 rtc::CritScope cs(&_fileCritSect);
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000215
niklase@google.com470e71d2011-07-07 08:21:25 +0000216 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700217 if (output_file_recorder_) {
218 output_file_recorder_->RegisterModuleFileCallback(NULL);
219 output_file_recorder_.reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000220 }
221
kwiberg5a25d952016-08-17 07:31:12 -0700222 output_file_recorder_ = FileRecorder::CreateFileRecorder(
223 _instanceId, (const FileFormats)format);
224 if (!output_file_recorder_) {
225 _engineStatisticsPtr->SetLastError(
226 VE_INVALID_ARGUMENT, kTraceError,
227 "StartRecordingPlayout() fileRecorder format isnot correct");
228 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000229 }
230
kwiberg5a25d952016-08-17 07:31:12 -0700231 if (output_file_recorder_->StartRecordingAudioFile(
232 fileName, (const CodecInst&)*codecInst, notificationTime) != 0) {
233 _engineStatisticsPtr->SetLastError(
234 VE_BAD_FILE, kTraceError,
235 "StartRecordingAudioFile() failed to start file recording");
236 output_file_recorder_->StopRecording();
237 output_file_recorder_.reset();
238 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000239 }
kwiberg5a25d952016-08-17 07:31:12 -0700240 output_file_recorder_->RegisterModuleFileCallback(this);
niklase@google.com470e71d2011-07-07 08:21:25 +0000241 _outputFileRecording = true;
242
243 return 0;
244}
245
246int OutputMixer::StartRecordingPlayout(OutStream* stream,
247 const CodecInst* codecInst)
248{
249 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
250 "OutputMixer::StartRecordingPlayout()");
251
252 if (_outputFileRecording)
253 {
254 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
255 "StartRecordingPlayout() is already recording");
256 return 0;
257 }
258
259 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000260 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000261 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
262
263 if (codecInst != NULL && codecInst->channels != 1)
264 {
265 _engineStatisticsPtr->SetLastError(
266 VE_BAD_ARGUMENT, kTraceError,
267 "StartRecordingPlayout() invalid compression");
268 return(-1);
269 }
270 if(codecInst == NULL)
271 {
272 format = kFileFormatPcm16kHzFile;
273 codecInst=&dummyCodec;
274 }
275 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
276 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
277 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
278 {
279 format = kFileFormatWavFile;
280 }
281 else
282 {
283 format = kFileFormatCompressedFile;
284 }
285
tommi31fc21f2016-01-21 10:37:37 -0800286 rtc::CritScope cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000287
288 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700289 if (output_file_recorder_) {
290 output_file_recorder_->RegisterModuleFileCallback(NULL);
291 output_file_recorder_.reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000292 }
293
kwiberg5a25d952016-08-17 07:31:12 -0700294 output_file_recorder_ = FileRecorder::CreateFileRecorder(
295 _instanceId, (const FileFormats)format);
296 if (!output_file_recorder_) {
297 _engineStatisticsPtr->SetLastError(
298 VE_INVALID_ARGUMENT, kTraceError,
299 "StartRecordingPlayout() fileRecorder format isnot correct");
300 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000301 }
302
kwiberg4ec01d92016-08-22 08:43:54 -0700303 if (output_file_recorder_->StartRecordingAudioFile(stream, *codecInst,
kwiberg5a25d952016-08-17 07:31:12 -0700304 notificationTime) != 0) {
305 _engineStatisticsPtr->SetLastError(
306 VE_BAD_FILE, kTraceError,
307 "StartRecordingAudioFile() failed to start file recording");
308 output_file_recorder_->StopRecording();
309 output_file_recorder_.reset();
310 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000311 }
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000312
kwiberg5a25d952016-08-17 07:31:12 -0700313 output_file_recorder_->RegisterModuleFileCallback(this);
niklase@google.com470e71d2011-07-07 08:21:25 +0000314 _outputFileRecording = true;
315
316 return 0;
317}
318
319int OutputMixer::StopRecordingPlayout()
320{
321 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
322 "OutputMixer::StopRecordingPlayout()");
323
324 if (!_outputFileRecording)
325 {
326 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
327 "StopRecordingPlayout() file isnot recording");
328 return -1;
329 }
330
tommi31fc21f2016-01-21 10:37:37 -0800331 rtc::CritScope cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000332
kwiberg5a25d952016-08-17 07:31:12 -0700333 if (output_file_recorder_->StopRecording() != 0) {
334 _engineStatisticsPtr->SetLastError(
335 VE_STOP_RECORDING_FAILED, kTraceError,
336 "StopRecording(), could not stop recording");
337 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000338 }
kwiberg5a25d952016-08-17 07:31:12 -0700339 output_file_recorder_->RegisterModuleFileCallback(NULL);
340 output_file_recorder_.reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000341 _outputFileRecording = false;
342
343 return 0;
344}
345
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000346int OutputMixer::GetMixedAudio(int sample_rate_hz,
Peter Kasting69558702016-01-12 16:26:35 -0800347 size_t num_channels,
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000348 AudioFrame* frame) {
Peter Kasting69558702016-01-12 16:26:35 -0800349 WEBRTC_TRACE(
350 kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
351 "OutputMixer::GetMixedAudio(sample_rate_hz=%d, num_channels=%" PRIuS ")",
352 sample_rate_hz, num_channels);
niklase@google.com470e71d2011-07-07 08:21:25 +0000353
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000354 // --- Record playout if enabled
355 {
tommi31fc21f2016-01-21 10:37:37 -0800356 rtc::CritScope cs(&_fileCritSect);
kwiberg5a25d952016-08-17 07:31:12 -0700357 if (_outputFileRecording && output_file_recorder_)
358 output_file_recorder_->RecordAudioToFile(_audioFrame);
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000359 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000360
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000361 frame->num_channels_ = num_channels;
362 frame->sample_rate_hz_ = sample_rate_hz;
363 // TODO(andrew): Ideally the downmixing would occur much earlier, in
364 // AudioCodingModule.
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000365 RemixAndResample(_audioFrame, &resampler_, frame);
366 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000367}
368
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000369int32_t
xians@webrtc.org56925312014-04-14 10:50:37 +0000370OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm)
niklase@google.com470e71d2011-07-07 08:21:25 +0000371{
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000372 if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz)
niklase@google.com470e71d2011-07-07 08:21:25 +0000373 {
374 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
375 "OutputMixer::DoOperationsOnCombinedSignal() => "
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000376 "mixing frequency = %d", _audioFrame.sample_rate_hz_);
377 _mixingFrequencyHz = _audioFrame.sample_rate_hz_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000378 }
379
niklase@google.com470e71d2011-07-07 08:21:25 +0000380 // --- Far-end Voice Quality Enhancement (AudioProcessing Module)
peah66085be2015-12-16 02:02:20 -0800381 if (feed_data_to_apm) {
aluebsda116c42016-03-17 16:43:29 -0700382 if (_audioProcessingModulePtr->ProcessReverseStream(&_audioFrame) != 0) {
peah66085be2015-12-16 02:02:20 -0800383 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
aluebsb0319552016-03-17 20:39:53 -0700384 "AudioProcessingModule::ProcessReverseStream() => error");
nisseeb4ca4e2017-01-12 02:24:27 -0800385 RTC_NOTREACHED();
peah66085be2015-12-16 02:02:20 -0800386 }
387 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000388
niklase@google.com470e71d2011-07-07 08:21:25 +0000389 return 0;
390}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000391} // namespace voe
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000392} // namespace webrtc