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