1/*
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/voe_file_impl.h"
12
13#include "webrtc/modules/media_file/interface/media_file.h"
14#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
15#include "webrtc/system_wrappers/interface/file_wrapper.h"
16#include "webrtc/system_wrappers/interface/trace.h"
17#include "webrtc/voice_engine/channel.h"
18#include "webrtc/voice_engine/include/voe_errors.h"
19#include "webrtc/voice_engine/output_mixer.h"
20#include "webrtc/voice_engine/transmit_mixer.h"
21#include "webrtc/voice_engine/voice_engine_impl.h"
22
23namespace webrtc {
24
25VoEFile* VoEFile::GetInterface(VoiceEngine* voiceEngine)
26{
27#ifndef WEBRTC_VOICE_ENGINE_FILE_API
28    return NULL;
29#else
30    if (NULL == voiceEngine)
31    {
32        return NULL;
33    }
34    VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
35    s->AddRef();
36    return s;
37#endif
38}
39
40#ifdef WEBRTC_VOICE_ENGINE_FILE_API
41
42VoEFileImpl::VoEFileImpl(voe::SharedData* shared) : _shared(shared)
43{
44    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
45                 "VoEFileImpl::VoEFileImpl() - ctor");
46}
47
48VoEFileImpl::~VoEFileImpl()
49{
50    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
51                 "VoEFileImpl::~VoEFileImpl() - dtor");
52}
53
54int VoEFileImpl::StartPlayingFileLocally(
55    int channel,
56    const char fileNameUTF8[1024],
57    bool loop, FileFormats format,
58    float volumeScaling,
59    int startPointMs,
60    int stopPointMs)
61{
62    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
63                 "StartPlayingFileLocally(channel=%d, fileNameUTF8[]=%s, "
64                 "loop=%d, format=%d, volumeScaling=%5.3f, startPointMs=%d,"
65                 " stopPointMs=%d)",
66                 channel, fileNameUTF8, loop, format, volumeScaling,
67                 startPointMs, stopPointMs);
68    assert(1024 == FileWrapper::kMaxFileNameSize);
69    if (!_shared->statistics().Initialized())
70    {
71        _shared->SetLastError(VE_NOT_INITED, kTraceError);
72        return -1;
73    }
74    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
75    voe::Channel* channelPtr = ch.channel();
76    if (channelPtr == NULL)
77    {
78        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
79            "StartPlayingFileLocally() failed to locate channel");
80        return -1;
81    }
82
83    return channelPtr->StartPlayingFileLocally(fileNameUTF8,
84                                               loop,
85                                               format,
86                                               startPointMs,
87                                               volumeScaling,
88                                               stopPointMs,
89                                               NULL);
90}
91
92int VoEFileImpl::StartPlayingFileLocally(int channel,
93                                         InStream* stream,
94                                         FileFormats format,
95                                         float volumeScaling,
96                                         int startPointMs,
97                                         int stopPointMs)
98{
99    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
100                 "StartPlayingFileLocally(channel=%d, stream, format=%d, "
101                 "volumeScaling=%5.3f, startPointMs=%d, stopPointMs=%d)",
102                 channel, format, volumeScaling, startPointMs, stopPointMs);
103
104    if (!_shared->statistics().Initialized())
105    {
106        _shared->SetLastError(VE_NOT_INITED, kTraceError);
107        return -1;
108    }
109
110    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
111    voe::Channel* channelPtr = ch.channel();
112    if (channelPtr == NULL)
113    {
114        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
115            "StartPlayingFileLocally() failed to locate channel");
116        return -1;
117    }
118
119    return channelPtr->StartPlayingFileLocally(stream,
120                                               format,
121                                               startPointMs,
122                                               volumeScaling,
123                                               stopPointMs,
124                                               NULL);
125}
126
127int VoEFileImpl::StopPlayingFileLocally(int channel)
128{
129    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
130                 "StopPlayingFileLocally()");
131    if (!_shared->statistics().Initialized())
132    {
133        _shared->SetLastError(VE_NOT_INITED, kTraceError);
134        return -1;
135    }
136    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
137    voe::Channel* channelPtr = ch.channel();
138    if (channelPtr == NULL)
139    {
140        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
141            "StopPlayingFileLocally() failed to locate channel");
142        return -1;
143    }
144    return channelPtr->StopPlayingFileLocally();
145}
146
147int VoEFileImpl::IsPlayingFileLocally(int channel)
148{
149    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
150                 "IsPlayingFileLocally(channel=%d)", channel);
151    if (!_shared->statistics().Initialized())
152    {
153        _shared->SetLastError(VE_NOT_INITED, kTraceError);
154        return -1;
155    }
156    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
157    voe::Channel* channelPtr = ch.channel();
158    if (channelPtr == NULL)
159    {
160        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
161            "StopPlayingFileLocally() failed to locate channel");
162        return -1;
163    }
164    return channelPtr->IsPlayingFileLocally();
165}
166
167int VoEFileImpl::StartPlayingFileAsMicrophone(int channel,
168                                              const char fileNameUTF8[1024],
169                                              bool loop,
170                                              bool mixWithMicrophone,
171                                              FileFormats format,
172                                              float volumeScaling)
173{
174    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
175                 "StartPlayingFileAsMicrophone(channel=%d, fileNameUTF8=%s, "
176                 "loop=%d, mixWithMicrophone=%d, format=%d, "
177                 "volumeScaling=%5.3f)",
178                 channel, fileNameUTF8, loop, mixWithMicrophone, format,
179                 volumeScaling);
180    assert(1024 == FileWrapper::kMaxFileNameSize);
181    if (!_shared->statistics().Initialized())
182    {
183        _shared->SetLastError(VE_NOT_INITED, kTraceError);
184        return -1;
185    }
186
187    const uint32_t startPointMs(0);
188    const uint32_t stopPointMs(0);
189
190    if (channel == -1)
191    {
192        int res = _shared->transmit_mixer()->StartPlayingFileAsMicrophone(
193            fileNameUTF8,
194            loop,
195            format,
196            startPointMs,
197            volumeScaling,
198            stopPointMs,
199            NULL);
200        if (res)
201        {
202            WEBRTC_TRACE(kTraceError, kTraceVoice,
203                VoEId(_shared->instance_id(), -1),
204                "StartPlayingFileAsMicrophone() failed to start playing file");
205            return(-1);
206        }
207        else
208        {
209            _shared->transmit_mixer()->SetMixWithMicStatus(mixWithMicrophone);
210            return(0);
211        }
212    }
213    else
214    {
215        // Add file after demultiplexing <=> affects one channel only
216        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
217        voe::Channel* channelPtr = ch.channel();
218        if (channelPtr == NULL)
219        {
220            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
221                "StartPlayingFileAsMicrophone() failed to locate channel");
222            return -1;
223        }
224
225        int res = channelPtr->StartPlayingFileAsMicrophone(fileNameUTF8,
226                                                           loop,
227                                                           format,
228                                                           startPointMs,
229                                                           volumeScaling,
230                                                           stopPointMs,
231                                                           NULL);
232        if (res)
233        {
234            WEBRTC_TRACE(kTraceError, kTraceVoice,
235                VoEId(_shared->instance_id(), -1),
236                "StartPlayingFileAsMicrophone() failed to start playing file");
237            return -1;
238        }
239        else
240        {
241            channelPtr->SetMixWithMicStatus(mixWithMicrophone);
242            return 0;
243        }
244    }
245}
246
247int VoEFileImpl::StartPlayingFileAsMicrophone(int channel,
248                                              InStream* stream,
249                                              bool mixWithMicrophone,
250                                              FileFormats format,
251                                              float volumeScaling)
252{
253    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
254                 "StartPlayingFileAsMicrophone(channel=%d, stream,"
255                 " mixWithMicrophone=%d, format=%d, volumeScaling=%5.3f)",
256                 channel, mixWithMicrophone, format, volumeScaling);
257
258    if (!_shared->statistics().Initialized())
259    {
260        _shared->SetLastError(VE_NOT_INITED, kTraceError);
261        return -1;
262    }
263
264    const uint32_t startPointMs(0);
265    const uint32_t stopPointMs(0);
266
267    if (channel == -1)
268    {
269        int res = _shared->transmit_mixer()->StartPlayingFileAsMicrophone(
270            stream,
271            format,
272            startPointMs,
273            volumeScaling,
274            stopPointMs,
275            NULL);
276        if (res)
277        {
278            WEBRTC_TRACE(kTraceError, kTraceVoice,
279                VoEId(_shared->instance_id(), -1),
280                "StartPlayingFileAsMicrophone() failed to start "
281                "playing stream");
282            return(-1);
283        }
284        else
285        {
286            _shared->transmit_mixer()->SetMixWithMicStatus(mixWithMicrophone);
287            return(0);
288        }
289    }
290    else
291    {
292        // Add file after demultiplexing <=> affects one channel only
293        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
294        voe::Channel* channelPtr = ch.channel();
295        if (channelPtr == NULL)
296        {
297            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
298                "StartPlayingFileAsMicrophone() failed to locate channel");
299            return -1;
300        }
301
302        int res = channelPtr->StartPlayingFileAsMicrophone(
303            stream, format, startPointMs, volumeScaling, stopPointMs, NULL);
304        if (res)
305        {
306            WEBRTC_TRACE(kTraceError, kTraceVoice,
307                VoEId(_shared->instance_id(), -1),
308                "StartPlayingFileAsMicrophone() failed to start "
309                "playing stream");
310            return -1;
311        }
312        else
313        {
314            channelPtr->SetMixWithMicStatus(mixWithMicrophone);
315            return 0;
316        }
317    }
318}
319
320int VoEFileImpl::StopPlayingFileAsMicrophone(int channel)
321{
322    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
323                 "StopPlayingFileAsMicrophone(channel=%d)", channel);
324    if (!_shared->statistics().Initialized())
325    {
326        _shared->SetLastError(VE_NOT_INITED, kTraceError);
327        return -1;
328    }
329    if (channel == -1)
330    {
331        // Stop adding file before demultiplexing <=> affects all channels
332        return _shared->transmit_mixer()->StopPlayingFileAsMicrophone();
333    }
334    else
335    {
336        // Stop adding file after demultiplexing <=> affects one channel only
337        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
338        voe::Channel* channelPtr = ch.channel();
339        if (channelPtr == NULL)
340        {
341            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
342                "StopPlayingFileAsMicrophone() failed to locate channel");
343            return -1;
344        }
345        return channelPtr->StopPlayingFileAsMicrophone();
346    }
347}
348
349int VoEFileImpl::IsPlayingFileAsMicrophone(int channel)
350{
351    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
352                 "IsPlayingFileAsMicrophone(channel=%d)", channel);
353    if (!_shared->statistics().Initialized())
354    {
355        _shared->SetLastError(VE_NOT_INITED, kTraceError);
356        return -1;
357    }
358    if (channel == -1)
359    {
360        return _shared->transmit_mixer()->IsPlayingFileAsMicrophone();
361    }
362    else
363    {
364        // Stop adding file after demultiplexing <=> affects one channel only
365        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
366        voe::Channel* channelPtr = ch.channel();
367        if (channelPtr == NULL)
368        {
369            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
370                "IsPlayingFileAsMicrophone() failed to locate channel");
371            return -1;
372        }
373        return channelPtr->IsPlayingFileAsMicrophone();
374    }
375}
376
377int VoEFileImpl::StartRecordingPlayout(
378    int channel, const char* fileNameUTF8, CodecInst* compression,
379    int maxSizeBytes)
380{
381    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
382                 "StartRecordingPlayout(channel=%d, fileNameUTF8=%s, "
383                 "compression, maxSizeBytes=%d)",
384                 channel, fileNameUTF8, maxSizeBytes);
385    assert(1024 == FileWrapper::kMaxFileNameSize);
386
387    if (!_shared->statistics().Initialized())
388    {
389        _shared->SetLastError(VE_NOT_INITED, kTraceError);
390        return -1;
391    }
392    if (channel == -1)
393    {
394        return _shared->output_mixer()->StartRecordingPlayout
395          (fileNameUTF8, compression);
396    }
397    else
398    {
399        // Add file after demultiplexing <=> affects one channel only
400        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
401        voe::Channel* channelPtr = ch.channel();
402        if (channelPtr == NULL)
403        {
404            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
405                "StartRecordingPlayout() failed to locate channel");
406            return -1;
407        }
408        return channelPtr->StartRecordingPlayout(fileNameUTF8, compression);
409    }
410}
411
412int VoEFileImpl::StartRecordingPlayout(
413    int channel, OutStream* stream, CodecInst* compression)
414{
415    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
416                 "StartRecordingPlayout(channel=%d, stream, compression)",
417                 channel);
418    if (!_shared->statistics().Initialized())
419    {
420        _shared->SetLastError(VE_NOT_INITED, kTraceError);
421        return -1;
422    }
423    if (channel == -1)
424    {
425        return _shared->output_mixer()->
426            StartRecordingPlayout(stream, compression);
427    }
428    else
429    {
430        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
431        voe::Channel* channelPtr = ch.channel();
432        if (channelPtr == NULL)
433        {
434            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
435                "StartRecordingPlayout() failed to locate channel");
436            return -1;
437        }
438        return channelPtr->StartRecordingPlayout(stream, compression);
439    }
440}
441
442int VoEFileImpl::StopRecordingPlayout(int channel)
443{
444    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
445                 "StopRecordingPlayout(channel=%d)", channel);
446    if (!_shared->statistics().Initialized())
447    {
448        _shared->SetLastError(VE_NOT_INITED, kTraceError);
449        return -1;
450    }
451    if (channel == -1)
452    {
453        return _shared->output_mixer()->StopRecordingPlayout();
454    }
455    else
456    {
457        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
458        voe::Channel* channelPtr = ch.channel();
459        if (channelPtr == NULL)
460        {
461            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
462                "StopRecordingPlayout() failed to locate channel");
463            return -1;
464        }
465        return channelPtr->StopRecordingPlayout();
466    }
467}
468
469int VoEFileImpl::StartRecordingMicrophone(
470    const char* fileNameUTF8, CodecInst* compression, int maxSizeBytes)
471{
472    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
473                 "StartRecordingMicrophone(fileNameUTF8=%s, compression, "
474                 "maxSizeBytes=%d)", fileNameUTF8, maxSizeBytes);
475    assert(1024 == FileWrapper::kMaxFileNameSize);
476
477    if (!_shared->statistics().Initialized())
478    {
479        _shared->SetLastError(VE_NOT_INITED, kTraceError);
480        return -1;
481    }
482    if (_shared->transmit_mixer()->StartRecordingMicrophone(fileNameUTF8,
483                                                          compression))
484    {
485        WEBRTC_TRACE(kTraceError, kTraceVoice,
486            VoEId(_shared->instance_id(), -1),
487            "StartRecordingMicrophone() failed to start recording");
488        return -1;
489    }
490    if (_shared->audio_device()->Recording())
491    {
492        return 0;
493    }
494    if (!_shared->ext_recording())
495    {
496        if (_shared->audio_device()->InitRecording() != 0)
497        {
498            WEBRTC_TRACE(kTraceError, kTraceVoice,
499                VoEId(_shared->instance_id(), -1),
500                "StartRecordingMicrophone() failed to initialize recording");
501            return -1;
502        }
503        if (_shared->audio_device()->StartRecording() != 0)
504        {
505            WEBRTC_TRACE(kTraceError, kTraceVoice,
506                VoEId(_shared->instance_id(), -1),
507                "StartRecordingMicrophone() failed to start recording");
508            return -1;
509        }
510    }
511    return 0;
512}
513
514int VoEFileImpl::StartRecordingMicrophone(
515    OutStream* stream, CodecInst* compression)
516{
517    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
518                 "StartRecordingMicrophone(stream, compression)");
519
520    if (!_shared->statistics().Initialized())
521    {
522        _shared->SetLastError(VE_NOT_INITED, kTraceError);
523        return -1;
524    }
525    if (_shared->transmit_mixer()->StartRecordingMicrophone(stream,
526                                                          compression) == -1)
527    {
528        WEBRTC_TRACE(kTraceError, kTraceVoice,
529            VoEId(_shared->instance_id(), -1),
530            "StartRecordingMicrophone() failed to start recording");
531        return -1;
532    }
533    if (_shared->audio_device()->Recording())
534    {
535        return 0;
536    }
537    if (!_shared->ext_recording())
538    {
539        if (_shared->audio_device()->InitRecording() != 0)
540        {
541            WEBRTC_TRACE(kTraceError, kTraceVoice,
542                VoEId(_shared->instance_id(), -1),
543                "StartRecordingMicrophone() failed to initialize recording");
544            return -1;
545        }
546        if (_shared->audio_device()->StartRecording() != 0)
547        {
548            WEBRTC_TRACE(kTraceError, kTraceVoice,
549                VoEId(_shared->instance_id(), -1),
550                "StartRecordingMicrophone() failed to start recording");
551            return -1;
552        }
553    }
554    return 0;
555}
556
557int VoEFileImpl::StopRecordingMicrophone()
558{
559    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
560                 "StopRecordingMicrophone()");
561    if (!_shared->statistics().Initialized())
562    {
563        _shared->SetLastError(VE_NOT_INITED, kTraceError);
564        return -1;
565    }
566
567    int err = 0;
568
569    // TODO(xians): consider removing Start/StopRecording() in
570    // Start/StopRecordingMicrophone() if no channel is recording.
571    if (_shared->NumOfSendingChannels() == 0 &&
572        _shared->audio_device()->Recording())
573    {
574        // Stop audio-device recording if no channel is recording
575        if (_shared->audio_device()->StopRecording() != 0)
576        {
577            _shared->SetLastError(VE_CANNOT_STOP_RECORDING, kTraceError,
578                "StopRecordingMicrophone() failed to stop recording");
579            err = -1;
580        }
581    }
582
583    if (_shared->transmit_mixer()->StopRecordingMicrophone() != 0)
584    {
585        WEBRTC_TRACE(kTraceError, kTraceVoice,
586                VoEId(_shared->instance_id(), -1),
587                "StopRecordingMicrophone() failed to stop recording to mixer");
588        err = -1;
589    }
590
591    return err;
592}
593
594#endif  // #ifdef WEBRTC_VOICE_ENGINE_FILE_API
595
596}  // namespace webrtc
597