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