blob: 9f9511d837ea6dfe424f38834628ee23e9be552e [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000013#include "webrtc/base/format_macros.h"
Henrik Kjellander0b9e29c2015-11-16 11:12:24 +010014#include "webrtc/modules/media_file/media_file_impl.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010015#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
16#include "webrtc/system_wrappers/include/file_wrapper.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010017#include "webrtc/system_wrappers/include/trace.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000018
niklase@google.com470e71d2011-07-07 08:21:25 +000019namespace webrtc {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +000020MediaFile* MediaFile::CreateMediaFile(const int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000021{
niklase@google.com470e71d2011-07-07 08:21:25 +000022 return new MediaFileImpl(id);
23}
24
25void MediaFile::DestroyMediaFile(MediaFile* module)
26{
niklase@google.com470e71d2011-07-07 08:21:25 +000027 delete static_cast<MediaFileImpl*>(module);
28}
29
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +000030MediaFileImpl::MediaFileImpl(const int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000031 : _id(id),
henrike@webrtc.org7cdcde32011-12-14 17:27:58 +000032 _crit(CriticalSectionWrapper::CreateCriticalSection()),
33 _callbackCrit(CriticalSectionWrapper::CreateCriticalSection()),
niklase@google.com470e71d2011-07-07 08:21:25 +000034 _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
56MediaFileImpl::~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
henrike@webrtc.org7cdcde32011-12-14 17:27:58 +000083 delete _crit;
84 delete _callbackCrit;
niklase@google.com470e71d2011-07-07 08:21:25 +000085}
86
pkasting@chromium.org0b1534c2014-12-15 22:09:40 +000087int64_t MediaFileImpl::TimeUntilNextProcess()
niklase@google.com470e71d2011-07-07 08:21:25 +000088{
89 WEBRTC_TRACE(
90 kTraceWarning,
91 kTraceFile,
92 _id,
93 "TimeUntilNextProcess: This method is not used by MediaFile class.");
94 return -1;
95}
96
pbosa26ac922016-02-25 04:50:01 -080097void MediaFileImpl::Process()
niklase@google.com470e71d2011-07-07 08:21:25 +000098{
99 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
100 "Process: This method is not used by MediaFile class.");
niklase@google.com470e71d2011-07-07 08:21:25 +0000101}
102
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000103int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000104 size_t& dataLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000105{
niklase@google.com470e71d2011-07-07 08:21:25 +0000106 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000107 "MediaFileImpl::PlayoutData(buffer= 0x%x, bufLen= %" PRIuS ")",
niklase@google.com470e71d2011-07-07 08:21:25 +0000108 buffer, dataLengthInBytes);
109
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000110 const size_t bufferLengthInBytes = dataLengthInBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000111 dataLengthInBytes = 0;
112
113 if(buffer == NULL || bufferLengthInBytes == 0)
114 {
115 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
116 "Buffer pointer or length is NULL!");
117 return -1;
118 }
119
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000120 int32_t bytesRead = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000121 {
122 CriticalSectionScoped lock(_crit);
123
124 if(!_playingActive)
125 {
126 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
127 "Not currently playing!");
128 return -1;
129 }
130
131 if(!_ptrFileUtilityObj)
132 {
133 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
134 "Playing, but no FileUtility object!");
135 StopPlaying();
136 return -1;
137 }
138
139 switch(_fileFormat)
140 {
141 case kFileFormatPcm32kHzFile:
142 case kFileFormatPcm16kHzFile:
143 case kFileFormatPcm8kHzFile:
144 bytesRead = _ptrFileUtilityObj->ReadPCMData(
145 *_ptrInStream,
146 buffer,
147 bufferLengthInBytes);
148 break;
149 case kFileFormatCompressedFile:
150 bytesRead = _ptrFileUtilityObj->ReadCompressedData(
151 *_ptrInStream,
152 buffer,
153 bufferLengthInBytes);
154 break;
155 case kFileFormatWavFile:
156 bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono(
157 *_ptrInStream,
158 buffer,
159 bufferLengthInBytes);
160 break;
161 case kFileFormatPreencodedFile:
162 bytesRead = _ptrFileUtilityObj->ReadPreEncodedData(
163 *_ptrInStream,
164 buffer,
165 bufferLengthInBytes);
166 if(bytesRead > 0)
167 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000168 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000169 return 0;
170 }
171 break;
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000172 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000173 {
niklase@google.com470e71d2011-07-07 08:21:25 +0000174 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000175 "Invalid file format: %d", _fileFormat);
niklase@google.com470e71d2011-07-07 08:21:25 +0000176 assert(false);
177 break;
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000178 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000179 }
180
181 if( bytesRead > 0)
182 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000183 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000184 }
185 }
186 HandlePlayCallbacks(bytesRead);
187 return 0;
188}
189
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000190void MediaFileImpl::HandlePlayCallbacks(int32_t bytesRead)
niklase@google.com470e71d2011-07-07 08:21:25 +0000191{
192 bool playEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000193 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000194
195 if(bytesRead > 0)
196 {
197 // Check if it's time for PlayNotification(..).
198 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
199 if(_notificationMs)
200 {
201 if(_playoutPositionMs >= _notificationMs)
202 {
203 _notificationMs = 0;
204 callbackNotifyMs = _playoutPositionMs;
205 }
206 }
207 }
208 else
209 {
210 // If no bytes were read assume end of file.
211 StopPlaying();
212 playEnded = true;
213 }
214
215 // Only _callbackCrit may and should be taken when making callbacks.
216 CriticalSectionScoped lock(_callbackCrit);
217 if(_ptrCallback)
218 {
219 if(callbackNotifyMs)
220 {
221 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
222 }
223 if(playEnded)
224 {
225 _ptrCallback->PlayFileEnded(_id);
226 }
227 }
228}
229
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000230int32_t MediaFileImpl::PlayoutStereoData(
231 int8_t* bufferLeft,
232 int8_t* bufferRight,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000233 size_t& dataLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000234{
235 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000236 "MediaFileImpl::PlayoutStereoData(Left = 0x%x, Right = 0x%x,"
237 " Len= %" PRIuS ")",
niklase@google.com470e71d2011-07-07 08:21:25 +0000238 bufferLeft,
239 bufferRight,
240 dataLengthInBytes);
241
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000242 const size_t bufferLengthInBytes = dataLengthInBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000243 dataLengthInBytes = 0;
244
245 if(bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0)
246 {
247 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
248 "A buffer pointer or the length is NULL!");
249 return -1;
250 }
251
252 bool playEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000253 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000254 {
255 CriticalSectionScoped lock(_crit);
256
257 if(!_playingActive || !_isStereo)
258 {
259 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
260 "Not currently playing stereo!");
261 return -1;
262 }
263
264 if(!_ptrFileUtilityObj)
265 {
266 WEBRTC_TRACE(
267 kTraceError,
268 kTraceFile,
269 _id,
270 "Playing stereo, but the FileUtility objects is NULL!");
271 StopPlaying();
272 return -1;
273 }
274
275 // Stereo playout only supported for WAV files.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000276 int32_t bytesRead = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000277 switch(_fileFormat)
278 {
279 case kFileFormatWavFile:
280 bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo(
281 *_ptrInStream,
282 bufferLeft,
283 bufferRight,
284 bufferLengthInBytes);
285 break;
286 default:
287 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
288 "Trying to read non-WAV as stereo audio\
289 (not supported)");
290 break;
291 }
292
293 if(bytesRead > 0)
294 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000295 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000296
297 // Check if it's time for PlayNotification(..).
298 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
299 if(_notificationMs)
300 {
301 if(_playoutPositionMs >= _notificationMs)
302 {
303 _notificationMs = 0;
304 callbackNotifyMs = _playoutPositionMs;
305 }
306 }
307 }
308 else
309 {
310 // If no bytes were read assume end of file.
311 StopPlaying();
312 playEnded = true;
313 }
314 }
315
316 CriticalSectionScoped lock(_callbackCrit);
317 if(_ptrCallback)
318 {
319 if(callbackNotifyMs)
320 {
321 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
322 }
323 if(playEnded)
324 {
325 _ptrCallback->PlayFileEnded(_id);
326 }
327 }
328 return 0;
329}
330
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000331int32_t MediaFileImpl::StartPlayingAudioFile(
leozwang@webrtc.org09e77192012-03-01 18:35:54 +0000332 const char* fileName,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000333 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000334 const bool loop,
335 const FileFormats format,
336 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000337 const uint32_t startPointMs,
338 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000339{
niklase@google.com470e71d2011-07-07 08:21:25 +0000340 if(!ValidFileName(fileName))
341 {
342 return -1;
343 }
344 if(!ValidFileFormat(format,codecInst))
345 {
346 return -1;
347 }
348 if(!ValidFilePositions(startPointMs,stopPointMs))
349 {
350 return -1;
351 }
352
353 // Check that the file will play longer than notificationTimeMs ms.
354 if((startPointMs && stopPointMs && !loop) &&
355 (notificationTimeMs > (stopPointMs - startPointMs)))
356 {
357 WEBRTC_TRACE(
358 kTraceError,
359 kTraceFile,
360 _id,
361 "specified notification time is longer than amount of ms that will\
362 be played");
363 return -1;
364 }
365
366 FileWrapper* inputStream = FileWrapper::Create();
367 if(inputStream == NULL)
368 {
369 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
370 "Failed to allocate input stream for file %s", fileName);
371 return -1;
372 }
373
tommia6219cc2016-06-15 10:30:14 -0700374 if (!inputStream->OpenFile(fileName, true)) {
375 delete inputStream;
376 WEBRTC_TRACE(kTraceError, kTraceFile, _id, "Could not open input file %s",
377 fileName);
378 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000379 }
380
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000381 if(StartPlayingStream(*inputStream, loop, notificationTimeMs,
382 format, codecInst, startPointMs, stopPointMs) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000383 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000384 inputStream->CloseFile();
niklase@google.com470e71d2011-07-07 08:21:25 +0000385 delete inputStream;
386 return -1;
387 }
388
389 CriticalSectionScoped lock(_crit);
390 _openFile = true;
391 strncpy(_fileName, fileName, sizeof(_fileName));
392 _fileName[sizeof(_fileName) - 1] = '\0';
393 return 0;
394}
395
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000396int32_t MediaFileImpl::StartPlayingAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000397 InStream& stream,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000398 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000399 const FileFormats format,
400 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000401 const uint32_t startPointMs,
402 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000403{
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000404 return StartPlayingStream(stream, false, notificationTimeMs, format,
niklase@google.com470e71d2011-07-07 08:21:25 +0000405 codecInst, startPointMs, stopPointMs);
406}
407
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000408int32_t MediaFileImpl::StartPlayingStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000409 InStream& stream,
niklase@google.com470e71d2011-07-07 08:21:25 +0000410 bool loop,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000411 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000412 const FileFormats format,
413 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000414 const uint32_t startPointMs,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000415 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000416{
niklase@google.com470e71d2011-07-07 08:21:25 +0000417 if(!ValidFileFormat(format,codecInst))
418 {
419 return -1;
420 }
421
422 if(!ValidFilePositions(startPointMs,stopPointMs))
423 {
424 return -1;
425 }
426
427 CriticalSectionScoped lock(_crit);
428 if(_playingActive || _recordingActive)
429 {
430 WEBRTC_TRACE(
431 kTraceError,
432 kTraceFile,
433 _id,
434 "StartPlaying called, but already playing or recording file %s",
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000435 (_fileName[0] == '\0') ? "(name not set)" : _fileName);
niklase@google.com470e71d2011-07-07 08:21:25 +0000436 return -1;
437 }
438
439 if(_ptrFileUtilityObj != NULL)
440 {
441 WEBRTC_TRACE(kTraceError,
442 kTraceFile,
443 _id,
444 "StartPlaying called, but FileUtilityObj already exists!");
445 StopPlaying();
446 return -1;
447 }
448
449 _ptrFileUtilityObj = new ModuleFileUtility(_id);
450 if(_ptrFileUtilityObj == NULL)
451 {
452 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
453 "Failed to create FileUtilityObj!");
454 return -1;
455 }
456
457 switch(format)
458 {
459 case kFileFormatWavFile:
460 {
461 if(_ptrFileUtilityObj->InitWavReading(stream, startPointMs,
462 stopPointMs) == -1)
463 {
464 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
465 "Not a valid WAV file!");
466 StopPlaying();
467 return -1;
468 }
469 _fileFormat = kFileFormatWavFile;
470 break;
471 }
472 case kFileFormatCompressedFile:
473 {
474 if(_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs,
475 stopPointMs) == -1)
476 {
477 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
478 "Not a valid Compressed file!");
479 StopPlaying();
480 return -1;
481 }
482 _fileFormat = kFileFormatCompressedFile;
483 break;
484 }
485 case kFileFormatPcm8kHzFile:
486 case kFileFormatPcm16kHzFile:
487 case kFileFormatPcm32kHzFile:
488 {
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000489 // ValidFileFormat() called in the beginneing of this function
490 // prevents codecInst from being NULL here.
491 assert(codecInst != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000492 if(!ValidFrequency(codecInst->plfreq) ||
493 _ptrFileUtilityObj->InitPCMReading(stream, startPointMs,
494 stopPointMs,
495 codecInst->plfreq) == -1)
496 {
497 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
498 "Not a valid raw 8 or 16 KHz PCM file!");
499 StopPlaying();
500 return -1;
501 }
502
503 _fileFormat = format;
504 break;
505 }
506 case kFileFormatPreencodedFile:
507 {
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000508 // ValidFileFormat() called in the beginneing of this function
509 // prevents codecInst from being NULL here.
510 assert(codecInst != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000511 if(_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) ==
512 -1)
513 {
514 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
515 "Not a valid PreEncoded file!");
516 StopPlaying();
517 return -1;
518 }
519
520 _fileFormat = kFileFormatPreencodedFile;
521 break;
522 }
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000523 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000524 {
niklase@google.com470e71d2011-07-07 08:21:25 +0000525 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000526 "Invalid file format: %d", format);
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000527 assert(false);
528 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000529 }
530 }
531 if(_ptrFileUtilityObj->codec_info(codec_info_) == -1)
532 {
533 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
534 "Failed to retrieve codec info!");
535 StopPlaying();
536 return -1;
537 }
538
539 _isStereo = (codec_info_.channels == 2);
540 if(_isStereo && (_fileFormat != kFileFormatWavFile))
541 {
542 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
543 "Stereo is only allowed for WAV files");
544 StopPlaying();
545 return -1;
546 }
547 _playingActive = true;
548 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
549 _ptrInStream = &stream;
550 _notificationMs = notificationTimeMs;
551
552 return 0;
553}
554
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000555int32_t MediaFileImpl::StopPlaying()
niklase@google.com470e71d2011-07-07 08:21:25 +0000556{
niklase@google.com470e71d2011-07-07 08:21:25 +0000557
558 CriticalSectionScoped lock(_crit);
559 _isStereo = false;
560 if(_ptrFileUtilityObj)
561 {
562 delete _ptrFileUtilityObj;
563 _ptrFileUtilityObj = NULL;
564 }
565 if(_ptrInStream)
566 {
567 // If MediaFileImpl opened the InStream it must be reclaimed here.
568 if(_openFile)
569 {
570 delete _ptrInStream;
571 _openFile = false;
572 }
573 _ptrInStream = NULL;
574 }
575
576 codec_info_.pltype = 0;
577 codec_info_.plname[0] = '\0';
578
579 if(!_playingActive)
580 {
581 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
582 "playing is not active!");
583 return -1;
584 }
585
586 _playingActive = false;
587 return 0;
588}
589
590bool MediaFileImpl::IsPlaying()
591{
592 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsPlaying()");
593 CriticalSectionScoped lock(_crit);
594 return _playingActive;
595}
596
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000597int32_t MediaFileImpl::IncomingAudioData(
598 const int8_t* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000599 const size_t bufferLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000600{
niklase@google.com470e71d2011-07-07 08:21:25 +0000601 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000602 "MediaFile::IncomingData(buffer= 0x%x, bufLen= %" PRIuS,
niklase@google.com470e71d2011-07-07 08:21:25 +0000603 buffer, bufferLengthInBytes);
604
605 if(buffer == NULL || bufferLengthInBytes == 0)
606 {
607 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
608 "Buffer pointer or length is NULL!");
609 return -1;
610 }
611
612 bool recordingEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000613 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000614 {
615 CriticalSectionScoped lock(_crit);
616
617 if(!_recordingActive)
618 {
619 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
620 "Not currently recording!");
621 return -1;
622 }
623 if(_ptrOutStream == NULL)
624 {
625 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
626 "Recording is active, but output stream is NULL!");
627 assert(false);
628 return -1;
629 }
630
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000631 int32_t bytesWritten = 0;
632 uint32_t samplesWritten = codec_info_.pacsize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000633 if(_ptrFileUtilityObj)
634 {
635 switch(_fileFormat)
636 {
637 case kFileFormatPcm8kHzFile:
638 case kFileFormatPcm16kHzFile:
639 case kFileFormatPcm32kHzFile:
640 bytesWritten = _ptrFileUtilityObj->WritePCMData(
641 *_ptrOutStream,
642 buffer,
643 bufferLengthInBytes);
644
645 // Sample size is 2 bytes.
646 if(bytesWritten > 0)
647 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000648 samplesWritten = bytesWritten/sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000649 }
650 break;
651 case kFileFormatCompressedFile:
652 bytesWritten = _ptrFileUtilityObj->WriteCompressedData(
653 *_ptrOutStream, buffer, bufferLengthInBytes);
654 break;
655 case kFileFormatWavFile:
656 bytesWritten = _ptrFileUtilityObj->WriteWavData(
657 *_ptrOutStream,
658 buffer,
659 bufferLengthInBytes);
660 if(bytesWritten > 0 && STR_NCASE_CMP(codec_info_.plname,
661 "L16", 4) == 0)
662 {
663 // Sample size is 2 bytes.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000664 samplesWritten = bytesWritten/sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000665 }
666 break;
667 case kFileFormatPreencodedFile:
668 bytesWritten = _ptrFileUtilityObj->WritePreEncodedData(
669 *_ptrOutStream, buffer, bufferLengthInBytes);
670 break;
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000671 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000672 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000673 "Invalid file format: %d", _fileFormat);
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000674 assert(false);
niklase@google.com470e71d2011-07-07 08:21:25 +0000675 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000676 }
677 } else {
678 // TODO (hellner): quick look at the code makes me think that this
679 // code is never executed. Remove?
680 if(_ptrOutStream)
681 {
682 if(_ptrOutStream->Write(buffer, bufferLengthInBytes))
683 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000684 bytesWritten = static_cast<int32_t>(bufferLengthInBytes);
niklase@google.com470e71d2011-07-07 08:21:25 +0000685 }
686 }
687 }
688
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000689 _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000);
niklase@google.com470e71d2011-07-07 08:21:25 +0000690
691 // Check if it's time for RecordNotification(..).
692 if(_notificationMs)
693 {
694 if(_recordDurationMs >= _notificationMs)
695 {
696 _notificationMs = 0;
697 callbackNotifyMs = _recordDurationMs;
698 }
699 }
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000700 if(bytesWritten < (int32_t)bufferLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000701 {
702 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
703 "Failed to write all requested bytes!");
704 StopRecording();
705 recordingEnded = true;
706 }
707 }
708
709 // Only _callbackCrit may and should be taken when making callbacks.
710 CriticalSectionScoped lock(_callbackCrit);
711 if(_ptrCallback)
712 {
713 if(callbackNotifyMs)
714 {
715 _ptrCallback->RecordNotification(_id, callbackNotifyMs);
716 }
717 if(recordingEnded)
718 {
719 _ptrCallback->RecordFileEnded(_id);
720 return -1;
721 }
722 }
723 return 0;
724}
725
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000726int32_t MediaFileImpl::StartRecordingAudioFile(
leozwang@webrtc.org09e77192012-03-01 18:35:54 +0000727 const char* fileName,
niklase@google.com470e71d2011-07-07 08:21:25 +0000728 const FileFormats format,
729 const CodecInst& codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000730 const uint32_t notificationTimeMs,
731 const uint32_t maxSizeBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000732{
niklase@google.com470e71d2011-07-07 08:21:25 +0000733 if(!ValidFileName(fileName))
734 {
735 return -1;
736 }
737 if(!ValidFileFormat(format,&codecInst))
738 {
739 return -1;
740 }
741
742 FileWrapper* outputStream = FileWrapper::Create();
743 if(outputStream == NULL)
744 {
745 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
746 "Failed to allocate memory for output stream");
747 return -1;
748 }
749
tommia6219cc2016-06-15 10:30:14 -0700750 if (!outputStream->OpenFile(fileName, false)) {
751 delete outputStream;
752 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
753 "Could not open output file '%s' for writing!", fileName);
754 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000755 }
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000756
niklase@google.com470e71d2011-07-07 08:21:25 +0000757 if(maxSizeBytes)
758 {
759 outputStream->SetMaxFileSize(maxSizeBytes);
760 }
761
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000762 if(StartRecordingAudioStream(*outputStream, format, codecInst,
763 notificationTimeMs) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000764 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000765 outputStream->CloseFile();
niklase@google.com470e71d2011-07-07 08:21:25 +0000766 delete outputStream;
767 return -1;
768 }
769
770 CriticalSectionScoped lock(_crit);
771 _openFile = true;
772 strncpy(_fileName, fileName, sizeof(_fileName));
773 _fileName[sizeof(_fileName) - 1] = '\0';
774 return 0;
775}
776
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000777int32_t MediaFileImpl::StartRecordingAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000778 OutStream& stream,
779 const FileFormats format,
780 const CodecInst& codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000781 const uint32_t notificationTimeMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000782{
niklase@google.com470e71d2011-07-07 08:21:25 +0000783 // Check codec info
784 if(!ValidFileFormat(format,&codecInst))
785 {
786 return -1;
787 }
788
789 CriticalSectionScoped lock(_crit);
790 if(_recordingActive || _playingActive)
791 {
792 WEBRTC_TRACE(
793 kTraceError,
794 kTraceFile,
795 _id,
796 "StartRecording called, but already recording or playing file %s!",
797 _fileName);
798 return -1;
799 }
800
801 if(_ptrFileUtilityObj != NULL)
802 {
803 WEBRTC_TRACE(
804 kTraceError,
805 kTraceFile,
806 _id,
807 "StartRecording called, but fileUtilityObj already exists!");
808 StopRecording();
809 return -1;
810 }
811
812 _ptrFileUtilityObj = new ModuleFileUtility(_id);
813 if(_ptrFileUtilityObj == NULL)
814 {
815 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
816 "Cannot allocate fileUtilityObj!");
817 return -1;
818 }
819
820 CodecInst tmpAudioCodec;
821 memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst));
822 switch(format)
823 {
824 case kFileFormatWavFile:
825 {
826 if(_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1)
827 {
828 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
829 "Failed to initialize WAV file!");
830 delete _ptrFileUtilityObj;
831 _ptrFileUtilityObj = NULL;
832 return -1;
833 }
834 _fileFormat = kFileFormatWavFile;
835 break;
836 }
837 case kFileFormatCompressedFile:
838 {
839 // Write compression codec name at beginning of file
840 if(_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) ==
841 -1)
842 {
843 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
844 "Failed to initialize Compressed file!");
845 delete _ptrFileUtilityObj;
846 _ptrFileUtilityObj = NULL;
847 return -1;
848 }
849 _fileFormat = kFileFormatCompressedFile;
850 break;
851 }
852 case kFileFormatPcm8kHzFile:
853 case kFileFormatPcm16kHzFile:
854 {
855 if(!ValidFrequency(codecInst.plfreq) ||
856 _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) ==
857 -1)
858 {
859 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
860 "Failed to initialize 8 or 16KHz PCM file!");
861 delete _ptrFileUtilityObj;
862 _ptrFileUtilityObj = NULL;
863 return -1;
864 }
865 _fileFormat = format;
866 break;
867 }
868 case kFileFormatPreencodedFile:
869 {
870 if(_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) ==
871 -1)
872 {
873 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
874 "Failed to initialize Pre-Encoded file!");
875 delete _ptrFileUtilityObj;
876 _ptrFileUtilityObj = NULL;
877 return -1;
878 }
879
880 _fileFormat = kFileFormatPreencodedFile;
881 break;
882 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000883 default:
884 {
885 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
886 "Invalid file format %d specified!", format);
887 delete _ptrFileUtilityObj;
888 _ptrFileUtilityObj = NULL;
889 return -1;
890 }
891 }
892 _isStereo = (tmpAudioCodec.channels == 2);
893 if(_isStereo)
894 {
895 if(_fileFormat != kFileFormatWavFile)
896 {
897 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
898 "Stereo is only allowed for WAV files");
899 StopRecording();
900 return -1;
901 }
niklas.enbom@webrtc.org87885e82012-02-07 14:48:59 +0000902 if((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) &&
903 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) &&
904 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0))
niklase@google.com470e71d2011-07-07 08:21:25 +0000905 {
906 WEBRTC_TRACE(
907 kTraceWarning,
908 kTraceFile,
909 _id,
910 "Stereo is only allowed for codec PCMU, PCMA and L16 ");
911 StopRecording();
912 return -1;
913 }
914 }
915 memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst));
916 _recordingActive = true;
917 _ptrOutStream = &stream;
918 _notificationMs = notificationTimeMs;
919 _recordDurationMs = 0;
920 return 0;
921}
922
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000923int32_t MediaFileImpl::StopRecording()
niklase@google.com470e71d2011-07-07 08:21:25 +0000924{
niklase@google.com470e71d2011-07-07 08:21:25 +0000925
926 CriticalSectionScoped lock(_crit);
927 if(!_recordingActive)
928 {
929 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
930 "recording is not active!");
931 return -1;
932 }
933
934 _isStereo = false;
935
936 if(_ptrFileUtilityObj != NULL)
937 {
938 // Both AVI and WAV header has to be updated before closing the stream
939 // because they contain size information.
940 if((_fileFormat == kFileFormatWavFile) &&
941 (_ptrOutStream != NULL))
942 {
943 _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream);
944 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000945 delete _ptrFileUtilityObj;
946 _ptrFileUtilityObj = NULL;
947 }
948
949 if(_ptrOutStream != NULL)
950 {
951 // If MediaFileImpl opened the OutStream it must be reclaimed here.
952 if(_openFile)
953 {
954 delete _ptrOutStream;
955 _openFile = false;
956 }
957 _ptrOutStream = NULL;
958 }
959
960 _recordingActive = false;
961 codec_info_.pltype = 0;
962 codec_info_.plname[0] = '\0';
963
964 return 0;
965}
966
967bool MediaFileImpl::IsRecording()
968{
969 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsRecording()");
970 CriticalSectionScoped lock(_crit);
971 return _recordingActive;
972}
973
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000974int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000975{
niklase@google.com470e71d2011-07-07 08:21:25 +0000976
977 CriticalSectionScoped lock(_crit);
978 if(!_recordingActive)
979 {
980 durationMs = 0;
981 return -1;
982 }
983 durationMs = _recordDurationMs;
984 return 0;
985}
986
987bool MediaFileImpl::IsStereo()
988{
989 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsStereo()");
990 CriticalSectionScoped lock(_crit);
991 return _isStereo;
992}
993
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000994int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback)
niklase@google.com470e71d2011-07-07 08:21:25 +0000995{
niklase@google.com470e71d2011-07-07 08:21:25 +0000996
997 CriticalSectionScoped lock(_callbackCrit);
998
999 _ptrCallback = callback;
1000 return 0;
1001}
1002
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001003int32_t MediaFileImpl::FileDurationMs(const char* fileName,
1004 uint32_t& durationMs,
1005 const FileFormats format,
1006 const uint32_t freqInHz)
niklase@google.com470e71d2011-07-07 08:21:25 +00001007{
niklase@google.com470e71d2011-07-07 08:21:25 +00001008
1009 if(!ValidFileName(fileName))
1010 {
1011 return -1;
1012 }
1013 if(!ValidFrequency(freqInHz))
1014 {
1015 return -1;
1016 }
1017
1018 ModuleFileUtility* utilityObj = new ModuleFileUtility(_id);
1019 if(utilityObj == NULL)
1020 {
1021 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1022 "failed to allocate utility object!");
1023 return -1;
1024 }
1025
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001026 const int32_t duration = utilityObj->FileDurationMs(fileName, format,
1027 freqInHz);
niklase@google.com470e71d2011-07-07 08:21:25 +00001028 delete utilityObj;
1029 if(duration == -1)
1030 {
1031 durationMs = 0;
1032 return -1;
1033 }
1034
1035 durationMs = duration;
1036 return 0;
1037}
1038
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001039int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const
niklase@google.com470e71d2011-07-07 08:21:25 +00001040{
niklase@google.com470e71d2011-07-07 08:21:25 +00001041 CriticalSectionScoped lock(_crit);
1042 if(!_playingActive)
1043 {
1044 positionMs = 0;
1045 return -1;
1046 }
1047 positionMs = _playoutPositionMs;
1048 return 0;
1049}
1050
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001051int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const
niklase@google.com470e71d2011-07-07 08:21:25 +00001052{
niklase@google.com470e71d2011-07-07 08:21:25 +00001053 CriticalSectionScoped lock(_crit);
1054 if(!_playingActive && !_recordingActive)
1055 {
1056 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1057 "Neither playout nor recording has been initialized!");
1058 return -1;
1059 }
1060 if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0')
1061 {
1062 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1063 "The CodecInst for %s is unknown!",
1064 _playingActive ? "Playback" : "Recording");
1065 return -1;
1066 }
1067 memcpy(&codecInst,&codec_info_,sizeof(CodecInst));
1068 return 0;
1069}
1070
niklase@google.com470e71d2011-07-07 08:21:25 +00001071bool MediaFileImpl::ValidFileFormat(const FileFormats format,
1072 const CodecInst* codecInst)
1073{
1074 if(codecInst == NULL)
1075 {
1076 if(format == kFileFormatPreencodedFile ||
1077 format == kFileFormatPcm8kHzFile ||
1078 format == kFileFormatPcm16kHzFile ||
1079 format == kFileFormatPcm32kHzFile)
1080 {
1081 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1082 "Codec info required for file format specified!");
1083 return false;
1084 }
1085 }
1086 return true;
1087}
1088
leozwang@webrtc.org09e77192012-03-01 18:35:54 +00001089bool MediaFileImpl::ValidFileName(const char* fileName)
niklase@google.com470e71d2011-07-07 08:21:25 +00001090{
1091 if((fileName == NULL) ||(fileName[0] == '\0'))
1092 {
1093 WEBRTC_TRACE(kTraceError, kTraceFile, -1, "FileName not specified!");
1094 return false;
1095 }
1096 return true;
1097}
1098
1099
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001100bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs,
1101 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +00001102{
1103 if(startPointMs == 0 && stopPointMs == 0) // Default values
1104 {
1105 return true;
1106 }
1107 if(stopPointMs &&(startPointMs >= stopPointMs))
1108 {
1109 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1110 "startPointMs must be less than stopPointMs!");
1111 return false;
1112 }
1113 if(stopPointMs &&((stopPointMs - startPointMs) < 20))
1114 {
1115 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1116 "minimum play duration for files is 20 ms!");
1117 return false;
1118 }
1119 return true;
1120}
1121
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001122bool MediaFileImpl::ValidFrequency(const uint32_t frequency)
niklase@google.com470e71d2011-07-07 08:21:25 +00001123{
1124 if((frequency == 8000) || (frequency == 16000)|| (frequency == 32000))
1125 {
1126 return true;
1127 }
1128 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1129 "Frequency should be 8000, 16000 or 32000 (Hz)");
1130 return false;
1131}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +00001132} // namespace webrtc