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 <assert.h>
12
13 #include "webrtc/base/format_macros.h"
14 #include "webrtc/modules/media_file/media_file_impl.h"
15 #include "webrtc/system_wrappers/include/critical_section_wrapper.h"
16 #include "webrtc/system_wrappers/include/file_wrapper.h"
17 #include "webrtc/system_wrappers/include/trace.h"
18
19 namespace webrtc {
CreateMediaFile(const int32_t id)20 MediaFile* MediaFile::CreateMediaFile(const int32_t id)
21 {
22 return new MediaFileImpl(id);
23 }
24
DestroyMediaFile(MediaFile * module)25 void MediaFile::DestroyMediaFile(MediaFile* module)
26 {
27 delete static_cast<MediaFileImpl*>(module);
28 }
29
MediaFileImpl(const int32_t id)30 MediaFileImpl::MediaFileImpl(const int32_t id)
31 : _id(id),
32 _crit(CriticalSectionWrapper::CreateCriticalSection()),
33 _callbackCrit(CriticalSectionWrapper::CreateCriticalSection()),
34 _ptrFileUtilityObj(NULL),
35 codec_info_(),
36 _ptrInStream(NULL),
37 _ptrOutStream(NULL),
38 _fileFormat((FileFormats)-1),
39 _recordDurationMs(0),
40 _playoutPositionMs(0),
41 _notificationMs(0),
42 _playingActive(false),
43 _recordingActive(false),
44 _isStereo(false),
45 _openFile(false),
46 _fileName(),
47 _ptrCallback(NULL)
48 {
49 WEBRTC_TRACE(kTraceMemory, kTraceFile, id, "Created");
50
51 codec_info_.plname[0] = '\0';
52 _fileName[0] = '\0';
53 }
54
55
~MediaFileImpl()56 MediaFileImpl::~MediaFileImpl()
57 {
58 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, "~MediaFileImpl()");
59 {
60 CriticalSectionScoped lock(_crit);
61
62 if(_playingActive)
63 {
64 StopPlaying();
65 }
66
67 if(_recordingActive)
68 {
69 StopRecording();
70 }
71
72 delete _ptrFileUtilityObj;
73
74 if(_openFile)
75 {
76 delete _ptrInStream;
77 _ptrInStream = NULL;
78 delete _ptrOutStream;
79 _ptrOutStream = NULL;
80 }
81 }
82
83 delete _crit;
84 delete _callbackCrit;
85 }
86
TimeUntilNextProcess()87 int64_t MediaFileImpl::TimeUntilNextProcess()
88 {
89 WEBRTC_TRACE(
90 kTraceWarning,
91 kTraceFile,
92 _id,
93 "TimeUntilNextProcess: This method is not used by MediaFile class.");
94 return -1;
95 }
96
Process()97 void MediaFileImpl::Process()
98 {
99 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
100 "Process: This method is not used by MediaFile class.");
101 }
102
PlayoutAudioData(int8_t * buffer,size_t & dataLengthInBytes)103 int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer,
104 size_t& dataLengthInBytes)
105 {
106 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
107 "MediaFileImpl::PlayoutData(buffer= 0x%x, bufLen= %" PRIuS ")",
108 buffer, dataLengthInBytes);
109
110 const size_t bufferLengthInBytes = dataLengthInBytes;
111 dataLengthInBytes = 0;
112
113 if(buffer == NULL || bufferLengthInBytes == 0)
114 {
115 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
116 "Buffer pointer or length is NULL!");
117 return -1;
118 }
119
120 int32_t bytesRead = 0;
121 {
122 CriticalSectionScoped lock(_crit);
123
124 if(!_playingActive)
125 {
126 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
127 "Not currently playing!");
128 return -1;
129 }
130
131 if(!_ptrFileUtilityObj)
132 {
133 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
134 "Playing, but no FileUtility object!");
135 StopPlaying();
136 return -1;
137 }
138
139 switch(_fileFormat)
140 {
141 case kFileFormatPcm32kHzFile:
142 case kFileFormatPcm16kHzFile:
143 case kFileFormatPcm8kHzFile:
144 bytesRead = _ptrFileUtilityObj->ReadPCMData(
145 *_ptrInStream,
146 buffer,
147 bufferLengthInBytes);
148 break;
149 case kFileFormatCompressedFile:
150 bytesRead = _ptrFileUtilityObj->ReadCompressedData(
151 *_ptrInStream,
152 buffer,
153 bufferLengthInBytes);
154 break;
155 case kFileFormatWavFile:
156 bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono(
157 *_ptrInStream,
158 buffer,
159 bufferLengthInBytes);
160 break;
161 case kFileFormatPreencodedFile:
162 bytesRead = _ptrFileUtilityObj->ReadPreEncodedData(
163 *_ptrInStream,
164 buffer,
165 bufferLengthInBytes);
166 if(bytesRead > 0)
167 {
168 dataLengthInBytes = static_cast<size_t>(bytesRead);
169 return 0;
170 }
171 break;
172 default:
173 {
174 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
175 "Invalid file format: %d", _fileFormat);
176 assert(false);
177 break;
178 }
179 }
180
181 if( bytesRead > 0)
182 {
183 dataLengthInBytes = static_cast<size_t>(bytesRead);
184 }
185 }
186 HandlePlayCallbacks(bytesRead);
187 return 0;
188 }
189
HandlePlayCallbacks(int32_t bytesRead)190 void MediaFileImpl::HandlePlayCallbacks(int32_t bytesRead)
191 {
192 bool playEnded = false;
193 uint32_t callbackNotifyMs = 0;
194
195 if(bytesRead > 0)
196 {
197 // Check if it's time for PlayNotification(..).
198 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
199 if(_notificationMs)
200 {
201 if(_playoutPositionMs >= _notificationMs)
202 {
203 _notificationMs = 0;
204 callbackNotifyMs = _playoutPositionMs;
205 }
206 }
207 }
208 else
209 {
210 // If no bytes were read assume end of file.
211 StopPlaying();
212 playEnded = true;
213 }
214
215 // Only _callbackCrit may and should be taken when making callbacks.
216 CriticalSectionScoped lock(_callbackCrit);
217 if(_ptrCallback)
218 {
219 if(callbackNotifyMs)
220 {
221 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
222 }
223 if(playEnded)
224 {
225 _ptrCallback->PlayFileEnded(_id);
226 }
227 }
228 }
229
PlayoutStereoData(int8_t * bufferLeft,int8_t * bufferRight,size_t & dataLengthInBytes)230 int32_t MediaFileImpl::PlayoutStereoData(
231 int8_t* bufferLeft,
232 int8_t* bufferRight,
233 size_t& dataLengthInBytes)
234 {
235 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
236 "MediaFileImpl::PlayoutStereoData(Left = 0x%x, Right = 0x%x,"
237 " Len= %" PRIuS ")",
238 bufferLeft,
239 bufferRight,
240 dataLengthInBytes);
241
242 const size_t bufferLengthInBytes = dataLengthInBytes;
243 dataLengthInBytes = 0;
244
245 if(bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0)
246 {
247 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
248 "A buffer pointer or the length is NULL!");
249 return -1;
250 }
251
252 bool playEnded = false;
253 uint32_t callbackNotifyMs = 0;
254 {
255 CriticalSectionScoped lock(_crit);
256
257 if(!_playingActive || !_isStereo)
258 {
259 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
260 "Not currently playing stereo!");
261 return -1;
262 }
263
264 if(!_ptrFileUtilityObj)
265 {
266 WEBRTC_TRACE(
267 kTraceError,
268 kTraceFile,
269 _id,
270 "Playing stereo, but the FileUtility objects is NULL!");
271 StopPlaying();
272 return -1;
273 }
274
275 // Stereo playout only supported for WAV files.
276 int32_t bytesRead = 0;
277 switch(_fileFormat)
278 {
279 case kFileFormatWavFile:
280 bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo(
281 *_ptrInStream,
282 bufferLeft,
283 bufferRight,
284 bufferLengthInBytes);
285 break;
286 default:
287 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
288 "Trying to read non-WAV as stereo audio\
289 (not supported)");
290 break;
291 }
292
293 if(bytesRead > 0)
294 {
295 dataLengthInBytes = static_cast<size_t>(bytesRead);
296
297 // Check if it's time for PlayNotification(..).
298 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
299 if(_notificationMs)
300 {
301 if(_playoutPositionMs >= _notificationMs)
302 {
303 _notificationMs = 0;
304 callbackNotifyMs = _playoutPositionMs;
305 }
306 }
307 }
308 else
309 {
310 // If no bytes were read assume end of file.
311 StopPlaying();
312 playEnded = true;
313 }
314 }
315
316 CriticalSectionScoped lock(_callbackCrit);
317 if(_ptrCallback)
318 {
319 if(callbackNotifyMs)
320 {
321 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
322 }
323 if(playEnded)
324 {
325 _ptrCallback->PlayFileEnded(_id);
326 }
327 }
328 return 0;
329 }
330
StartPlayingAudioFile(const char * fileName,const uint32_t notificationTimeMs,const bool loop,const FileFormats format,const CodecInst * codecInst,const uint32_t startPointMs,const uint32_t stopPointMs)331 int32_t MediaFileImpl::StartPlayingAudioFile(
332 const char* fileName,
333 const uint32_t notificationTimeMs,
334 const bool loop,
335 const FileFormats format,
336 const CodecInst* codecInst,
337 const uint32_t startPointMs,
338 const uint32_t stopPointMs)
339 {
340 if(!ValidFileName(fileName))
341 {
342 return -1;
343 }
344 if(!ValidFileFormat(format,codecInst))
345 {
346 return -1;
347 }
348 if(!ValidFilePositions(startPointMs,stopPointMs))
349 {
350 return -1;
351 }
352
353 // Check that the file will play longer than notificationTimeMs ms.
354 if((startPointMs && stopPointMs && !loop) &&
355 (notificationTimeMs > (stopPointMs - startPointMs)))
356 {
357 WEBRTC_TRACE(
358 kTraceError,
359 kTraceFile,
360 _id,
361 "specified notification time is longer than amount of ms that will\
362 be played");
363 return -1;
364 }
365
366 FileWrapper* inputStream = FileWrapper::Create();
367 if(inputStream == NULL)
368 {
369 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
370 "Failed to allocate input stream for file %s", fileName);
371 return -1;
372 }
373
374 if (!inputStream->OpenFile(fileName, true)) {
375 delete inputStream;
376 WEBRTC_TRACE(kTraceError, kTraceFile, _id, "Could not open input file %s",
377 fileName);
378 return -1;
379 }
380
381 if(StartPlayingStream(*inputStream, loop, notificationTimeMs,
382 format, codecInst, startPointMs, stopPointMs) == -1)
383 {
384 inputStream->CloseFile();
385 delete inputStream;
386 return -1;
387 }
388
389 CriticalSectionScoped lock(_crit);
390 _openFile = true;
391 strncpy(_fileName, fileName, sizeof(_fileName));
392 _fileName[sizeof(_fileName) - 1] = '\0';
393 return 0;
394 }
395
StartPlayingAudioStream(InStream & stream,const uint32_t notificationTimeMs,const FileFormats format,const CodecInst * codecInst,const uint32_t startPointMs,const uint32_t stopPointMs)396 int32_t MediaFileImpl::StartPlayingAudioStream(
397 InStream& stream,
398 const uint32_t notificationTimeMs,
399 const FileFormats format,
400 const CodecInst* codecInst,
401 const uint32_t startPointMs,
402 const uint32_t stopPointMs)
403 {
404 return StartPlayingStream(stream, false, notificationTimeMs, format,
405 codecInst, startPointMs, stopPointMs);
406 }
407
StartPlayingStream(InStream & stream,bool loop,const uint32_t notificationTimeMs,const FileFormats format,const CodecInst * codecInst,const uint32_t startPointMs,const uint32_t stopPointMs)408 int32_t MediaFileImpl::StartPlayingStream(
409 InStream& stream,
410 bool loop,
411 const uint32_t notificationTimeMs,
412 const FileFormats format,
413 const CodecInst* codecInst,
414 const uint32_t startPointMs,
415 const uint32_t stopPointMs)
416 {
417 if(!ValidFileFormat(format,codecInst))
418 {
419 return -1;
420 }
421
422 if(!ValidFilePositions(startPointMs,stopPointMs))
423 {
424 return -1;
425 }
426
427 CriticalSectionScoped lock(_crit);
428 if(_playingActive || _recordingActive)
429 {
430 WEBRTC_TRACE(
431 kTraceError,
432 kTraceFile,
433 _id,
434 "StartPlaying called, but already playing or recording file %s",
435 (_fileName[0] == '\0') ? "(name not set)" : _fileName);
436 return -1;
437 }
438
439 if(_ptrFileUtilityObj != NULL)
440 {
441 WEBRTC_TRACE(kTraceError,
442 kTraceFile,
443 _id,
444 "StartPlaying called, but FileUtilityObj already exists!");
445 StopPlaying();
446 return -1;
447 }
448
449 _ptrFileUtilityObj = new ModuleFileUtility(_id);
450 if(_ptrFileUtilityObj == NULL)
451 {
452 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
453 "Failed to create FileUtilityObj!");
454 return -1;
455 }
456
457 switch(format)
458 {
459 case kFileFormatWavFile:
460 {
461 if(_ptrFileUtilityObj->InitWavReading(stream, startPointMs,
462 stopPointMs) == -1)
463 {
464 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
465 "Not a valid WAV file!");
466 StopPlaying();
467 return -1;
468 }
469 _fileFormat = kFileFormatWavFile;
470 break;
471 }
472 case kFileFormatCompressedFile:
473 {
474 if(_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs,
475 stopPointMs) == -1)
476 {
477 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
478 "Not a valid Compressed file!");
479 StopPlaying();
480 return -1;
481 }
482 _fileFormat = kFileFormatCompressedFile;
483 break;
484 }
485 case kFileFormatPcm8kHzFile:
486 case kFileFormatPcm16kHzFile:
487 case kFileFormatPcm32kHzFile:
488 {
489 // ValidFileFormat() called in the beginneing of this function
490 // prevents codecInst from being NULL here.
491 assert(codecInst != NULL);
492 if(!ValidFrequency(codecInst->plfreq) ||
493 _ptrFileUtilityObj->InitPCMReading(stream, startPointMs,
494 stopPointMs,
495 codecInst->plfreq) == -1)
496 {
497 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
498 "Not a valid raw 8 or 16 KHz PCM file!");
499 StopPlaying();
500 return -1;
501 }
502
503 _fileFormat = format;
504 break;
505 }
506 case kFileFormatPreencodedFile:
507 {
508 // ValidFileFormat() called in the beginneing of this function
509 // prevents codecInst from being NULL here.
510 assert(codecInst != NULL);
511 if(_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) ==
512 -1)
513 {
514 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
515 "Not a valid PreEncoded file!");
516 StopPlaying();
517 return -1;
518 }
519
520 _fileFormat = kFileFormatPreencodedFile;
521 break;
522 }
523 default:
524 {
525 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
526 "Invalid file format: %d", format);
527 assert(false);
528 break;
529 }
530 }
531 if(_ptrFileUtilityObj->codec_info(codec_info_) == -1)
532 {
533 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
534 "Failed to retrieve codec info!");
535 StopPlaying();
536 return -1;
537 }
538
539 _isStereo = (codec_info_.channels == 2);
540 if(_isStereo && (_fileFormat != kFileFormatWavFile))
541 {
542 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
543 "Stereo is only allowed for WAV files");
544 StopPlaying();
545 return -1;
546 }
547 _playingActive = true;
548 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
549 _ptrInStream = &stream;
550 _notificationMs = notificationTimeMs;
551
552 return 0;
553 }
554
StopPlaying()555 int32_t MediaFileImpl::StopPlaying()
556 {
557
558 CriticalSectionScoped lock(_crit);
559 _isStereo = false;
560 if(_ptrFileUtilityObj)
561 {
562 delete _ptrFileUtilityObj;
563 _ptrFileUtilityObj = NULL;
564 }
565 if(_ptrInStream)
566 {
567 // If MediaFileImpl opened the InStream it must be reclaimed here.
568 if(_openFile)
569 {
570 delete _ptrInStream;
571 _openFile = false;
572 }
573 _ptrInStream = NULL;
574 }
575
576 codec_info_.pltype = 0;
577 codec_info_.plname[0] = '\0';
578
579 if(!_playingActive)
580 {
581 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
582 "playing is not active!");
583 return -1;
584 }
585
586 _playingActive = false;
587 return 0;
588 }
589
IsPlaying()590 bool MediaFileImpl::IsPlaying()
591 {
592 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsPlaying()");
593 CriticalSectionScoped lock(_crit);
594 return _playingActive;
595 }
596
IncomingAudioData(const int8_t * buffer,const size_t bufferLengthInBytes)597 int32_t MediaFileImpl::IncomingAudioData(
598 const int8_t* buffer,
599 const size_t bufferLengthInBytes)
600 {
601 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
602 "MediaFile::IncomingData(buffer= 0x%x, bufLen= %" PRIuS,
603 buffer, bufferLengthInBytes);
604
605 if(buffer == NULL || bufferLengthInBytes == 0)
606 {
607 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
608 "Buffer pointer or length is NULL!");
609 return -1;
610 }
611
612 bool recordingEnded = false;
613 uint32_t callbackNotifyMs = 0;
614 {
615 CriticalSectionScoped lock(_crit);
616
617 if(!_recordingActive)
618 {
619 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
620 "Not currently recording!");
621 return -1;
622 }
623 if(_ptrOutStream == NULL)
624 {
625 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
626 "Recording is active, but output stream is NULL!");
627 assert(false);
628 return -1;
629 }
630
631 int32_t bytesWritten = 0;
632 uint32_t samplesWritten = codec_info_.pacsize;
633 if(_ptrFileUtilityObj)
634 {
635 switch(_fileFormat)
636 {
637 case kFileFormatPcm8kHzFile:
638 case kFileFormatPcm16kHzFile:
639 case kFileFormatPcm32kHzFile:
640 bytesWritten = _ptrFileUtilityObj->WritePCMData(
641 *_ptrOutStream,
642 buffer,
643 bufferLengthInBytes);
644
645 // Sample size is 2 bytes.
646 if(bytesWritten > 0)
647 {
648 samplesWritten = bytesWritten/sizeof(int16_t);
649 }
650 break;
651 case kFileFormatCompressedFile:
652 bytesWritten = _ptrFileUtilityObj->WriteCompressedData(
653 *_ptrOutStream, buffer, bufferLengthInBytes);
654 break;
655 case kFileFormatWavFile:
656 bytesWritten = _ptrFileUtilityObj->WriteWavData(
657 *_ptrOutStream,
658 buffer,
659 bufferLengthInBytes);
660 if(bytesWritten > 0 && STR_NCASE_CMP(codec_info_.plname,
661 "L16", 4) == 0)
662 {
663 // Sample size is 2 bytes.
664 samplesWritten = bytesWritten/sizeof(int16_t);
665 }
666 break;
667 case kFileFormatPreencodedFile:
668 bytesWritten = _ptrFileUtilityObj->WritePreEncodedData(
669 *_ptrOutStream, buffer, bufferLengthInBytes);
670 break;
671 default:
672 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
673 "Invalid file format: %d", _fileFormat);
674 assert(false);
675 break;
676 }
677 } else {
678 // TODO (hellner): quick look at the code makes me think that this
679 // code is never executed. Remove?
680 if(_ptrOutStream)
681 {
682 if(_ptrOutStream->Write(buffer, bufferLengthInBytes))
683 {
684 bytesWritten = static_cast<int32_t>(bufferLengthInBytes);
685 }
686 }
687 }
688
689 _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000);
690
691 // Check if it's time for RecordNotification(..).
692 if(_notificationMs)
693 {
694 if(_recordDurationMs >= _notificationMs)
695 {
696 _notificationMs = 0;
697 callbackNotifyMs = _recordDurationMs;
698 }
699 }
700 if(bytesWritten < (int32_t)bufferLengthInBytes)
701 {
702 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
703 "Failed to write all requested bytes!");
704 StopRecording();
705 recordingEnded = true;
706 }
707 }
708
709 // Only _callbackCrit may and should be taken when making callbacks.
710 CriticalSectionScoped lock(_callbackCrit);
711 if(_ptrCallback)
712 {
713 if(callbackNotifyMs)
714 {
715 _ptrCallback->RecordNotification(_id, callbackNotifyMs);
716 }
717 if(recordingEnded)
718 {
719 _ptrCallback->RecordFileEnded(_id);
720 return -1;
721 }
722 }
723 return 0;
724 }
725
StartRecordingAudioFile(const char * fileName,const FileFormats format,const CodecInst & codecInst,const uint32_t notificationTimeMs,const uint32_t maxSizeBytes)726 int32_t MediaFileImpl::StartRecordingAudioFile(
727 const char* fileName,
728 const FileFormats format,
729 const CodecInst& codecInst,
730 const uint32_t notificationTimeMs,
731 const uint32_t maxSizeBytes)
732 {
733 if(!ValidFileName(fileName))
734 {
735 return -1;
736 }
737 if(!ValidFileFormat(format,&codecInst))
738 {
739 return -1;
740 }
741
742 FileWrapper* outputStream = FileWrapper::Create();
743 if(outputStream == NULL)
744 {
745 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
746 "Failed to allocate memory for output stream");
747 return -1;
748 }
749
750 if (!outputStream->OpenFile(fileName, false)) {
751 delete outputStream;
752 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
753 "Could not open output file '%s' for writing!", fileName);
754 return -1;
755 }
756
757 if(maxSizeBytes)
758 {
759 outputStream->SetMaxFileSize(maxSizeBytes);
760 }
761
762 if(StartRecordingAudioStream(*outputStream, format, codecInst,
763 notificationTimeMs) == -1)
764 {
765 outputStream->CloseFile();
766 delete outputStream;
767 return -1;
768 }
769
770 CriticalSectionScoped lock(_crit);
771 _openFile = true;
772 strncpy(_fileName, fileName, sizeof(_fileName));
773 _fileName[sizeof(_fileName) - 1] = '\0';
774 return 0;
775 }
776
StartRecordingAudioStream(OutStream & stream,const FileFormats format,const CodecInst & codecInst,const uint32_t notificationTimeMs)777 int32_t MediaFileImpl::StartRecordingAudioStream(
778 OutStream& stream,
779 const FileFormats format,
780 const CodecInst& codecInst,
781 const uint32_t notificationTimeMs)
782 {
783 // Check codec info
784 if(!ValidFileFormat(format,&codecInst))
785 {
786 return -1;
787 }
788
789 CriticalSectionScoped lock(_crit);
790 if(_recordingActive || _playingActive)
791 {
792 WEBRTC_TRACE(
793 kTraceError,
794 kTraceFile,
795 _id,
796 "StartRecording called, but already recording or playing file %s!",
797 _fileName);
798 return -1;
799 }
800
801 if(_ptrFileUtilityObj != NULL)
802 {
803 WEBRTC_TRACE(
804 kTraceError,
805 kTraceFile,
806 _id,
807 "StartRecording called, but fileUtilityObj already exists!");
808 StopRecording();
809 return -1;
810 }
811
812 _ptrFileUtilityObj = new ModuleFileUtility(_id);
813 if(_ptrFileUtilityObj == NULL)
814 {
815 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
816 "Cannot allocate fileUtilityObj!");
817 return -1;
818 }
819
820 CodecInst tmpAudioCodec;
821 memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst));
822 switch(format)
823 {
824 case kFileFormatWavFile:
825 {
826 if(_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1)
827 {
828 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
829 "Failed to initialize WAV file!");
830 delete _ptrFileUtilityObj;
831 _ptrFileUtilityObj = NULL;
832 return -1;
833 }
834 _fileFormat = kFileFormatWavFile;
835 break;
836 }
837 case kFileFormatCompressedFile:
838 {
839 // Write compression codec name at beginning of file
840 if(_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) ==
841 -1)
842 {
843 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
844 "Failed to initialize Compressed file!");
845 delete _ptrFileUtilityObj;
846 _ptrFileUtilityObj = NULL;
847 return -1;
848 }
849 _fileFormat = kFileFormatCompressedFile;
850 break;
851 }
852 case kFileFormatPcm8kHzFile:
853 case kFileFormatPcm16kHzFile:
854 {
855 if(!ValidFrequency(codecInst.plfreq) ||
856 _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) ==
857 -1)
858 {
859 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
860 "Failed to initialize 8 or 16KHz PCM file!");
861 delete _ptrFileUtilityObj;
862 _ptrFileUtilityObj = NULL;
863 return -1;
864 }
865 _fileFormat = format;
866 break;
867 }
868 case kFileFormatPreencodedFile:
869 {
870 if(_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) ==
871 -1)
872 {
873 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
874 "Failed to initialize Pre-Encoded file!");
875 delete _ptrFileUtilityObj;
876 _ptrFileUtilityObj = NULL;
877 return -1;
878 }
879
880 _fileFormat = kFileFormatPreencodedFile;
881 break;
882 }
883 default:
884 {
885 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
886 "Invalid file format %d specified!", format);
887 delete _ptrFileUtilityObj;
888 _ptrFileUtilityObj = NULL;
889 return -1;
890 }
891 }
892 _isStereo = (tmpAudioCodec.channels == 2);
893 if(_isStereo)
894 {
895 if(_fileFormat != kFileFormatWavFile)
896 {
897 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
898 "Stereo is only allowed for WAV files");
899 StopRecording();
900 return -1;
901 }
902 if((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) &&
903 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) &&
904 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0))
905 {
906 WEBRTC_TRACE(
907 kTraceWarning,
908 kTraceFile,
909 _id,
910 "Stereo is only allowed for codec PCMU, PCMA and L16 ");
911 StopRecording();
912 return -1;
913 }
914 }
915 memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst));
916 _recordingActive = true;
917 _ptrOutStream = &stream;
918 _notificationMs = notificationTimeMs;
919 _recordDurationMs = 0;
920 return 0;
921 }
922
StopRecording()923 int32_t MediaFileImpl::StopRecording()
924 {
925
926 CriticalSectionScoped lock(_crit);
927 if(!_recordingActive)
928 {
929 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
930 "recording is not active!");
931 return -1;
932 }
933
934 _isStereo = false;
935
936 if(_ptrFileUtilityObj != NULL)
937 {
938 // Both AVI and WAV header has to be updated before closing the stream
939 // because they contain size information.
940 if((_fileFormat == kFileFormatWavFile) &&
941 (_ptrOutStream != NULL))
942 {
943 _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream);
944 }
945 delete _ptrFileUtilityObj;
946 _ptrFileUtilityObj = NULL;
947 }
948
949 if(_ptrOutStream != NULL)
950 {
951 // If MediaFileImpl opened the OutStream it must be reclaimed here.
952 if(_openFile)
953 {
954 delete _ptrOutStream;
955 _openFile = false;
956 }
957 _ptrOutStream = NULL;
958 }
959
960 _recordingActive = false;
961 codec_info_.pltype = 0;
962 codec_info_.plname[0] = '\0';
963
964 return 0;
965 }
966
IsRecording()967 bool MediaFileImpl::IsRecording()
968 {
969 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsRecording()");
970 CriticalSectionScoped lock(_crit);
971 return _recordingActive;
972 }
973
RecordDurationMs(uint32_t & durationMs)974 int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs)
975 {
976
977 CriticalSectionScoped lock(_crit);
978 if(!_recordingActive)
979 {
980 durationMs = 0;
981 return -1;
982 }
983 durationMs = _recordDurationMs;
984 return 0;
985 }
986
IsStereo()987 bool MediaFileImpl::IsStereo()
988 {
989 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsStereo()");
990 CriticalSectionScoped lock(_crit);
991 return _isStereo;
992 }
993
SetModuleFileCallback(FileCallback * callback)994 int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback)
995 {
996
997 CriticalSectionScoped lock(_callbackCrit);
998
999 _ptrCallback = callback;
1000 return 0;
1001 }
1002
FileDurationMs(const char * fileName,uint32_t & durationMs,const FileFormats format,const uint32_t freqInHz)1003 int32_t MediaFileImpl::FileDurationMs(const char* fileName,
1004 uint32_t& durationMs,
1005 const FileFormats format,
1006 const uint32_t freqInHz)
1007 {
1008
1009 if(!ValidFileName(fileName))
1010 {
1011 return -1;
1012 }
1013 if(!ValidFrequency(freqInHz))
1014 {
1015 return -1;
1016 }
1017
1018 ModuleFileUtility* utilityObj = new ModuleFileUtility(_id);
1019 if(utilityObj == NULL)
1020 {
1021 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1022 "failed to allocate utility object!");
1023 return -1;
1024 }
1025
1026 const int32_t duration = utilityObj->FileDurationMs(fileName, format,
1027 freqInHz);
1028 delete utilityObj;
1029 if(duration == -1)
1030 {
1031 durationMs = 0;
1032 return -1;
1033 }
1034
1035 durationMs = duration;
1036 return 0;
1037 }
1038
PlayoutPositionMs(uint32_t & positionMs) const1039 int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const
1040 {
1041 CriticalSectionScoped lock(_crit);
1042 if(!_playingActive)
1043 {
1044 positionMs = 0;
1045 return -1;
1046 }
1047 positionMs = _playoutPositionMs;
1048 return 0;
1049 }
1050
codec_info(CodecInst & codecInst) const1051 int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const
1052 {
1053 CriticalSectionScoped lock(_crit);
1054 if(!_playingActive && !_recordingActive)
1055 {
1056 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1057 "Neither playout nor recording has been initialized!");
1058 return -1;
1059 }
1060 if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0')
1061 {
1062 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1063 "The CodecInst for %s is unknown!",
1064 _playingActive ? "Playback" : "Recording");
1065 return -1;
1066 }
1067 memcpy(&codecInst,&codec_info_,sizeof(CodecInst));
1068 return 0;
1069 }
1070
ValidFileFormat(const FileFormats format,const CodecInst * codecInst)1071 bool MediaFileImpl::ValidFileFormat(const FileFormats format,
1072 const CodecInst* codecInst)
1073 {
1074 if(codecInst == NULL)
1075 {
1076 if(format == kFileFormatPreencodedFile ||
1077 format == kFileFormatPcm8kHzFile ||
1078 format == kFileFormatPcm16kHzFile ||
1079 format == kFileFormatPcm32kHzFile)
1080 {
1081 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1082 "Codec info required for file format specified!");
1083 return false;
1084 }
1085 }
1086 return true;
1087 }
1088
ValidFileName(const char * fileName)1089 bool MediaFileImpl::ValidFileName(const char* fileName)
1090 {
1091 if((fileName == NULL) ||(fileName[0] == '\0'))
1092 {
1093 WEBRTC_TRACE(kTraceError, kTraceFile, -1, "FileName not specified!");
1094 return false;
1095 }
1096 return true;
1097 }
1098
1099
ValidFilePositions(const uint32_t startPointMs,const uint32_t stopPointMs)1100 bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs,
1101 const uint32_t stopPointMs)
1102 {
1103 if(startPointMs == 0 && stopPointMs == 0) // Default values
1104 {
1105 return true;
1106 }
1107 if(stopPointMs &&(startPointMs >= stopPointMs))
1108 {
1109 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1110 "startPointMs must be less than stopPointMs!");
1111 return false;
1112 }
1113 if(stopPointMs &&((stopPointMs - startPointMs) < 20))
1114 {
1115 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1116 "minimum play duration for files is 20 ms!");
1117 return false;
1118 }
1119 return true;
1120 }
1121
ValidFrequency(const uint32_t frequency)1122 bool MediaFileImpl::ValidFrequency(const uint32_t frequency)
1123 {
1124 if((frequency == 8000) || (frequency == 16000)|| (frequency == 32000))
1125 {
1126 return true;
1127 }
1128 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1129 "Frequency should be 8000, 16000 or 32000 (Hz)");
1130 return false;
1131 }
1132 } // namespace webrtc
1133