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