blob: abc7b9d9e083ebb14e0524095bb98e35788c6426 [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"
17#include "webrtc/system_wrappers/include/tick_util.h"
18#include "webrtc/system_wrappers/include/trace.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000019
niklase@google.com470e71d2011-07-07 08:21:25 +000020namespace webrtc {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +000021MediaFile* MediaFile::CreateMediaFile(const int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000022{
niklase@google.com470e71d2011-07-07 08:21:25 +000023 return new MediaFileImpl(id);
24}
25
26void MediaFile::DestroyMediaFile(MediaFile* module)
27{
niklase@google.com470e71d2011-07-07 08:21:25 +000028 delete static_cast<MediaFileImpl*>(module);
29}
30
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +000031MediaFileImpl::MediaFileImpl(const int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000032 : _id(id),
henrike@webrtc.org7cdcde32011-12-14 17:27:58 +000033 _crit(CriticalSectionWrapper::CreateCriticalSection()),
34 _callbackCrit(CriticalSectionWrapper::CreateCriticalSection()),
niklase@google.com470e71d2011-07-07 08:21:25 +000035 _ptrFileUtilityObj(NULL),
36 codec_info_(),
37 _ptrInStream(NULL),
38 _ptrOutStream(NULL),
39 _fileFormat((FileFormats)-1),
40 _recordDurationMs(0),
41 _playoutPositionMs(0),
42 _notificationMs(0),
43 _playingActive(false),
44 _recordingActive(false),
45 _isStereo(false),
46 _openFile(false),
47 _fileName(),
48 _ptrCallback(NULL)
49{
50 WEBRTC_TRACE(kTraceMemory, kTraceFile, id, "Created");
51
52 codec_info_.plname[0] = '\0';
53 _fileName[0] = '\0';
54}
55
56
57MediaFileImpl::~MediaFileImpl()
58{
59 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, "~MediaFileImpl()");
60 {
61 CriticalSectionScoped lock(_crit);
62
63 if(_playingActive)
64 {
65 StopPlaying();
66 }
67
68 if(_recordingActive)
69 {
70 StopRecording();
71 }
72
73 delete _ptrFileUtilityObj;
74
75 if(_openFile)
76 {
77 delete _ptrInStream;
78 _ptrInStream = NULL;
79 delete _ptrOutStream;
80 _ptrOutStream = NULL;
81 }
82 }
83
henrike@webrtc.org7cdcde32011-12-14 17:27:58 +000084 delete _crit;
85 delete _callbackCrit;
niklase@google.com470e71d2011-07-07 08:21:25 +000086}
87
pkasting@chromium.org0b1534c2014-12-15 22:09:40 +000088int64_t MediaFileImpl::TimeUntilNextProcess()
niklase@google.com470e71d2011-07-07 08:21:25 +000089{
90 WEBRTC_TRACE(
91 kTraceWarning,
92 kTraceFile,
93 _id,
94 "TimeUntilNextProcess: This method is not used by MediaFile class.");
95 return -1;
96}
97
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +000098int32_t MediaFileImpl::Process()
niklase@google.com470e71d2011-07-07 08:21:25 +000099{
100 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
101 "Process: This method is not used by MediaFile class.");
102 return -1;
103}
104
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000105int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000106 size_t& dataLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000107{
niklase@google.com470e71d2011-07-07 08:21:25 +0000108 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000109 "MediaFileImpl::PlayoutData(buffer= 0x%x, bufLen= %" PRIuS ")",
niklase@google.com470e71d2011-07-07 08:21:25 +0000110 buffer, dataLengthInBytes);
111
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000112 const size_t bufferLengthInBytes = dataLengthInBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000113 dataLengthInBytes = 0;
114
115 if(buffer == NULL || bufferLengthInBytes == 0)
116 {
117 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
118 "Buffer pointer or length is NULL!");
119 return -1;
120 }
121
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000122 int32_t bytesRead = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000123 {
124 CriticalSectionScoped lock(_crit);
125
126 if(!_playingActive)
127 {
128 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
129 "Not currently playing!");
130 return -1;
131 }
132
133 if(!_ptrFileUtilityObj)
134 {
135 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
136 "Playing, but no FileUtility object!");
137 StopPlaying();
138 return -1;
139 }
140
141 switch(_fileFormat)
142 {
143 case kFileFormatPcm32kHzFile:
144 case kFileFormatPcm16kHzFile:
145 case kFileFormatPcm8kHzFile:
146 bytesRead = _ptrFileUtilityObj->ReadPCMData(
147 *_ptrInStream,
148 buffer,
149 bufferLengthInBytes);
150 break;
151 case kFileFormatCompressedFile:
152 bytesRead = _ptrFileUtilityObj->ReadCompressedData(
153 *_ptrInStream,
154 buffer,
155 bufferLengthInBytes);
156 break;
157 case kFileFormatWavFile:
158 bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono(
159 *_ptrInStream,
160 buffer,
161 bufferLengthInBytes);
162 break;
163 case kFileFormatPreencodedFile:
164 bytesRead = _ptrFileUtilityObj->ReadPreEncodedData(
165 *_ptrInStream,
166 buffer,
167 bufferLengthInBytes);
168 if(bytesRead > 0)
169 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000170 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000171 return 0;
172 }
173 break;
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000174 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000175 {
niklase@google.com470e71d2011-07-07 08:21:25 +0000176 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000177 "Invalid file format: %d", _fileFormat);
niklase@google.com470e71d2011-07-07 08:21:25 +0000178 assert(false);
179 break;
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000180 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000181 }
182
183 if( bytesRead > 0)
184 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000185 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000186 }
187 }
188 HandlePlayCallbacks(bytesRead);
189 return 0;
190}
191
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000192void MediaFileImpl::HandlePlayCallbacks(int32_t bytesRead)
niklase@google.com470e71d2011-07-07 08:21:25 +0000193{
194 bool playEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000195 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000196
197 if(bytesRead > 0)
198 {
199 // Check if it's time for PlayNotification(..).
200 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
201 if(_notificationMs)
202 {
203 if(_playoutPositionMs >= _notificationMs)
204 {
205 _notificationMs = 0;
206 callbackNotifyMs = _playoutPositionMs;
207 }
208 }
209 }
210 else
211 {
212 // If no bytes were read assume end of file.
213 StopPlaying();
214 playEnded = true;
215 }
216
217 // Only _callbackCrit may and should be taken when making callbacks.
218 CriticalSectionScoped lock(_callbackCrit);
219 if(_ptrCallback)
220 {
221 if(callbackNotifyMs)
222 {
223 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
224 }
225 if(playEnded)
226 {
227 _ptrCallback->PlayFileEnded(_id);
228 }
229 }
230}
231
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000232int32_t MediaFileImpl::PlayoutStereoData(
233 int8_t* bufferLeft,
234 int8_t* bufferRight,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000235 size_t& dataLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000236{
237 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000238 "MediaFileImpl::PlayoutStereoData(Left = 0x%x, Right = 0x%x,"
239 " Len= %" PRIuS ")",
niklase@google.com470e71d2011-07-07 08:21:25 +0000240 bufferLeft,
241 bufferRight,
242 dataLengthInBytes);
243
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000244 const size_t bufferLengthInBytes = dataLengthInBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000245 dataLengthInBytes = 0;
246
247 if(bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0)
248 {
249 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
250 "A buffer pointer or the length is NULL!");
251 return -1;
252 }
253
254 bool playEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000255 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000256 {
257 CriticalSectionScoped lock(_crit);
258
259 if(!_playingActive || !_isStereo)
260 {
261 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
262 "Not currently playing stereo!");
263 return -1;
264 }
265
266 if(!_ptrFileUtilityObj)
267 {
268 WEBRTC_TRACE(
269 kTraceError,
270 kTraceFile,
271 _id,
272 "Playing stereo, but the FileUtility objects is NULL!");
273 StopPlaying();
274 return -1;
275 }
276
277 // Stereo playout only supported for WAV files.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000278 int32_t bytesRead = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000279 switch(_fileFormat)
280 {
281 case kFileFormatWavFile:
282 bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo(
283 *_ptrInStream,
284 bufferLeft,
285 bufferRight,
286 bufferLengthInBytes);
287 break;
288 default:
289 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
290 "Trying to read non-WAV as stereo audio\
291 (not supported)");
292 break;
293 }
294
295 if(bytesRead > 0)
296 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000297 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000298
299 // Check if it's time for PlayNotification(..).
300 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
301 if(_notificationMs)
302 {
303 if(_playoutPositionMs >= _notificationMs)
304 {
305 _notificationMs = 0;
306 callbackNotifyMs = _playoutPositionMs;
307 }
308 }
309 }
310 else
311 {
312 // If no bytes were read assume end of file.
313 StopPlaying();
314 playEnded = true;
315 }
316 }
317
318 CriticalSectionScoped lock(_callbackCrit);
319 if(_ptrCallback)
320 {
321 if(callbackNotifyMs)
322 {
323 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
324 }
325 if(playEnded)
326 {
327 _ptrCallback->PlayFileEnded(_id);
328 }
329 }
330 return 0;
331}
332
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000333int32_t MediaFileImpl::StartPlayingAudioFile(
leozwang@webrtc.org09e77192012-03-01 18:35:54 +0000334 const char* fileName,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000335 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000336 const bool loop,
337 const FileFormats format,
338 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000339 const uint32_t startPointMs,
340 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000341{
niklase@google.com470e71d2011-07-07 08:21:25 +0000342 if(!ValidFileName(fileName))
343 {
344 return -1;
345 }
346 if(!ValidFileFormat(format,codecInst))
347 {
348 return -1;
349 }
350 if(!ValidFilePositions(startPointMs,stopPointMs))
351 {
352 return -1;
353 }
354
355 // Check that the file will play longer than notificationTimeMs ms.
356 if((startPointMs && stopPointMs && !loop) &&
357 (notificationTimeMs > (stopPointMs - startPointMs)))
358 {
359 WEBRTC_TRACE(
360 kTraceError,
361 kTraceFile,
362 _id,
363 "specified notification time is longer than amount of ms that will\
364 be played");
365 return -1;
366 }
367
368 FileWrapper* inputStream = FileWrapper::Create();
369 if(inputStream == NULL)
370 {
371 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
372 "Failed to allocate input stream for file %s", fileName);
373 return -1;
374 }
375
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000376 if(inputStream->OpenFile(fileName, true, loop) != 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000377 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000378 delete inputStream;
379 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
380 "Could not open input file %s", fileName);
381 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000382 }
383
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000384 if(StartPlayingStream(*inputStream, loop, notificationTimeMs,
385 format, codecInst, startPointMs, stopPointMs) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000386 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000387 inputStream->CloseFile();
niklase@google.com470e71d2011-07-07 08:21:25 +0000388 delete inputStream;
389 return -1;
390 }
391
392 CriticalSectionScoped lock(_crit);
393 _openFile = true;
394 strncpy(_fileName, fileName, sizeof(_fileName));
395 _fileName[sizeof(_fileName) - 1] = '\0';
396 return 0;
397}
398
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000399int32_t MediaFileImpl::StartPlayingAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000400 InStream& stream,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000401 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000402 const FileFormats format,
403 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000404 const uint32_t startPointMs,
405 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000406{
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000407 return StartPlayingStream(stream, false, notificationTimeMs, format,
niklase@google.com470e71d2011-07-07 08:21:25 +0000408 codecInst, startPointMs, stopPointMs);
409}
410
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000411int32_t MediaFileImpl::StartPlayingStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000412 InStream& stream,
niklase@google.com470e71d2011-07-07 08:21:25 +0000413 bool loop,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000414 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000415 const FileFormats format,
416 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000417 const uint32_t startPointMs,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000418 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000419{
niklase@google.com470e71d2011-07-07 08:21:25 +0000420 if(!ValidFileFormat(format,codecInst))
421 {
422 return -1;
423 }
424
425 if(!ValidFilePositions(startPointMs,stopPointMs))
426 {
427 return -1;
428 }
429
430 CriticalSectionScoped lock(_crit);
431 if(_playingActive || _recordingActive)
432 {
433 WEBRTC_TRACE(
434 kTraceError,
435 kTraceFile,
436 _id,
437 "StartPlaying called, but already playing or recording file %s",
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000438 (_fileName[0] == '\0') ? "(name not set)" : _fileName);
niklase@google.com470e71d2011-07-07 08:21:25 +0000439 return -1;
440 }
441
442 if(_ptrFileUtilityObj != NULL)
443 {
444 WEBRTC_TRACE(kTraceError,
445 kTraceFile,
446 _id,
447 "StartPlaying called, but FileUtilityObj already exists!");
448 StopPlaying();
449 return -1;
450 }
451
452 _ptrFileUtilityObj = new ModuleFileUtility(_id);
453 if(_ptrFileUtilityObj == NULL)
454 {
455 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
456 "Failed to create FileUtilityObj!");
457 return -1;
458 }
459
460 switch(format)
461 {
462 case kFileFormatWavFile:
463 {
464 if(_ptrFileUtilityObj->InitWavReading(stream, startPointMs,
465 stopPointMs) == -1)
466 {
467 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
468 "Not a valid WAV file!");
469 StopPlaying();
470 return -1;
471 }
472 _fileFormat = kFileFormatWavFile;
473 break;
474 }
475 case kFileFormatCompressedFile:
476 {
477 if(_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs,
478 stopPointMs) == -1)
479 {
480 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
481 "Not a valid Compressed file!");
482 StopPlaying();
483 return -1;
484 }
485 _fileFormat = kFileFormatCompressedFile;
486 break;
487 }
488 case kFileFormatPcm8kHzFile:
489 case kFileFormatPcm16kHzFile:
490 case kFileFormatPcm32kHzFile:
491 {
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000492 // ValidFileFormat() called in the beginneing of this function
493 // prevents codecInst from being NULL here.
494 assert(codecInst != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000495 if(!ValidFrequency(codecInst->plfreq) ||
496 _ptrFileUtilityObj->InitPCMReading(stream, startPointMs,
497 stopPointMs,
498 codecInst->plfreq) == -1)
499 {
500 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
501 "Not a valid raw 8 or 16 KHz PCM file!");
502 StopPlaying();
503 return -1;
504 }
505
506 _fileFormat = format;
507 break;
508 }
509 case kFileFormatPreencodedFile:
510 {
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000511 // ValidFileFormat() called in the beginneing of this function
512 // prevents codecInst from being NULL here.
513 assert(codecInst != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000514 if(_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) ==
515 -1)
516 {
517 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
518 "Not a valid PreEncoded file!");
519 StopPlaying();
520 return -1;
521 }
522
523 _fileFormat = kFileFormatPreencodedFile;
524 break;
525 }
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000526 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000527 {
niklase@google.com470e71d2011-07-07 08:21:25 +0000528 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000529 "Invalid file format: %d", format);
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000530 assert(false);
531 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000532 }
533 }
534 if(_ptrFileUtilityObj->codec_info(codec_info_) == -1)
535 {
536 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
537 "Failed to retrieve codec info!");
538 StopPlaying();
539 return -1;
540 }
541
542 _isStereo = (codec_info_.channels == 2);
543 if(_isStereo && (_fileFormat != kFileFormatWavFile))
544 {
545 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
546 "Stereo is only allowed for WAV files");
547 StopPlaying();
548 return -1;
549 }
550 _playingActive = true;
551 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
552 _ptrInStream = &stream;
553 _notificationMs = notificationTimeMs;
554
555 return 0;
556}
557
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000558int32_t MediaFileImpl::StopPlaying()
niklase@google.com470e71d2011-07-07 08:21:25 +0000559{
niklase@google.com470e71d2011-07-07 08:21:25 +0000560
561 CriticalSectionScoped lock(_crit);
562 _isStereo = false;
563 if(_ptrFileUtilityObj)
564 {
565 delete _ptrFileUtilityObj;
566 _ptrFileUtilityObj = NULL;
567 }
568 if(_ptrInStream)
569 {
570 // If MediaFileImpl opened the InStream it must be reclaimed here.
571 if(_openFile)
572 {
573 delete _ptrInStream;
574 _openFile = false;
575 }
576 _ptrInStream = NULL;
577 }
578
579 codec_info_.pltype = 0;
580 codec_info_.plname[0] = '\0';
581
582 if(!_playingActive)
583 {
584 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
585 "playing is not active!");
586 return -1;
587 }
588
589 _playingActive = false;
590 return 0;
591}
592
593bool MediaFileImpl::IsPlaying()
594{
595 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsPlaying()");
596 CriticalSectionScoped lock(_crit);
597 return _playingActive;
598}
599
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000600int32_t MediaFileImpl::IncomingAudioData(
601 const int8_t* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000602 const size_t bufferLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000603{
niklase@google.com470e71d2011-07-07 08:21:25 +0000604 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000605 "MediaFile::IncomingData(buffer= 0x%x, bufLen= %" PRIuS,
niklase@google.com470e71d2011-07-07 08:21:25 +0000606 buffer, bufferLengthInBytes);
607
608 if(buffer == NULL || bufferLengthInBytes == 0)
609 {
610 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
611 "Buffer pointer or length is NULL!");
612 return -1;
613 }
614
615 bool recordingEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000616 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000617 {
618 CriticalSectionScoped lock(_crit);
619
620 if(!_recordingActive)
621 {
622 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
623 "Not currently recording!");
624 return -1;
625 }
626 if(_ptrOutStream == NULL)
627 {
628 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
629 "Recording is active, but output stream is NULL!");
630 assert(false);
631 return -1;
632 }
633
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000634 int32_t bytesWritten = 0;
635 uint32_t samplesWritten = codec_info_.pacsize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000636 if(_ptrFileUtilityObj)
637 {
638 switch(_fileFormat)
639 {
640 case kFileFormatPcm8kHzFile:
641 case kFileFormatPcm16kHzFile:
642 case kFileFormatPcm32kHzFile:
643 bytesWritten = _ptrFileUtilityObj->WritePCMData(
644 *_ptrOutStream,
645 buffer,
646 bufferLengthInBytes);
647
648 // Sample size is 2 bytes.
649 if(bytesWritten > 0)
650 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000651 samplesWritten = bytesWritten/sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000652 }
653 break;
654 case kFileFormatCompressedFile:
655 bytesWritten = _ptrFileUtilityObj->WriteCompressedData(
656 *_ptrOutStream, buffer, bufferLengthInBytes);
657 break;
658 case kFileFormatWavFile:
659 bytesWritten = _ptrFileUtilityObj->WriteWavData(
660 *_ptrOutStream,
661 buffer,
662 bufferLengthInBytes);
663 if(bytesWritten > 0 && STR_NCASE_CMP(codec_info_.plname,
664 "L16", 4) == 0)
665 {
666 // Sample size is 2 bytes.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000667 samplesWritten = bytesWritten/sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000668 }
669 break;
670 case kFileFormatPreencodedFile:
671 bytesWritten = _ptrFileUtilityObj->WritePreEncodedData(
672 *_ptrOutStream, buffer, bufferLengthInBytes);
673 break;
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000674 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000675 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000676 "Invalid file format: %d", _fileFormat);
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000677 assert(false);
niklase@google.com470e71d2011-07-07 08:21:25 +0000678 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000679 }
680 } else {
681 // TODO (hellner): quick look at the code makes me think that this
682 // code is never executed. Remove?
683 if(_ptrOutStream)
684 {
685 if(_ptrOutStream->Write(buffer, bufferLengthInBytes))
686 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000687 bytesWritten = static_cast<int32_t>(bufferLengthInBytes);
niklase@google.com470e71d2011-07-07 08:21:25 +0000688 }
689 }
690 }
691
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000692 _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000);
niklase@google.com470e71d2011-07-07 08:21:25 +0000693
694 // Check if it's time for RecordNotification(..).
695 if(_notificationMs)
696 {
697 if(_recordDurationMs >= _notificationMs)
698 {
699 _notificationMs = 0;
700 callbackNotifyMs = _recordDurationMs;
701 }
702 }
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000703 if(bytesWritten < (int32_t)bufferLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000704 {
705 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
706 "Failed to write all requested bytes!");
707 StopRecording();
708 recordingEnded = true;
709 }
710 }
711
712 // Only _callbackCrit may and should be taken when making callbacks.
713 CriticalSectionScoped lock(_callbackCrit);
714 if(_ptrCallback)
715 {
716 if(callbackNotifyMs)
717 {
718 _ptrCallback->RecordNotification(_id, callbackNotifyMs);
719 }
720 if(recordingEnded)
721 {
722 _ptrCallback->RecordFileEnded(_id);
723 return -1;
724 }
725 }
726 return 0;
727}
728
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000729int32_t MediaFileImpl::StartRecordingAudioFile(
leozwang@webrtc.org09e77192012-03-01 18:35:54 +0000730 const char* fileName,
niklase@google.com470e71d2011-07-07 08:21:25 +0000731 const FileFormats format,
732 const CodecInst& codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000733 const uint32_t notificationTimeMs,
734 const uint32_t maxSizeBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000735{
niklase@google.com470e71d2011-07-07 08:21:25 +0000736 if(!ValidFileName(fileName))
737 {
738 return -1;
739 }
740 if(!ValidFileFormat(format,&codecInst))
741 {
742 return -1;
743 }
744
745 FileWrapper* outputStream = FileWrapper::Create();
746 if(outputStream == NULL)
747 {
748 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
749 "Failed to allocate memory for output stream");
750 return -1;
751 }
752
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000753 if(outputStream->OpenFile(fileName, false) != 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000754 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000755 delete outputStream;
756 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
757 "Could not open output file '%s' for writing!",
758 fileName);
759 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000760 }
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000761
niklase@google.com470e71d2011-07-07 08:21:25 +0000762 if(maxSizeBytes)
763 {
764 outputStream->SetMaxFileSize(maxSizeBytes);
765 }
766
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000767 if(StartRecordingAudioStream(*outputStream, format, codecInst,
768 notificationTimeMs) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000769 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000770 outputStream->CloseFile();
niklase@google.com470e71d2011-07-07 08:21:25 +0000771 delete outputStream;
772 return -1;
773 }
774
775 CriticalSectionScoped lock(_crit);
776 _openFile = true;
777 strncpy(_fileName, fileName, sizeof(_fileName));
778 _fileName[sizeof(_fileName) - 1] = '\0';
779 return 0;
780}
781
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000782int32_t MediaFileImpl::StartRecordingAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000783 OutStream& stream,
784 const FileFormats format,
785 const CodecInst& codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000786 const uint32_t notificationTimeMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000787{
niklase@google.com470e71d2011-07-07 08:21:25 +0000788 // Check codec info
789 if(!ValidFileFormat(format,&codecInst))
790 {
791 return -1;
792 }
793
794 CriticalSectionScoped lock(_crit);
795 if(_recordingActive || _playingActive)
796 {
797 WEBRTC_TRACE(
798 kTraceError,
799 kTraceFile,
800 _id,
801 "StartRecording called, but already recording or playing file %s!",
802 _fileName);
803 return -1;
804 }
805
806 if(_ptrFileUtilityObj != NULL)
807 {
808 WEBRTC_TRACE(
809 kTraceError,
810 kTraceFile,
811 _id,
812 "StartRecording called, but fileUtilityObj already exists!");
813 StopRecording();
814 return -1;
815 }
816
817 _ptrFileUtilityObj = new ModuleFileUtility(_id);
818 if(_ptrFileUtilityObj == NULL)
819 {
820 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
821 "Cannot allocate fileUtilityObj!");
822 return -1;
823 }
824
825 CodecInst tmpAudioCodec;
826 memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst));
827 switch(format)
828 {
829 case kFileFormatWavFile:
830 {
831 if(_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1)
832 {
833 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
834 "Failed to initialize WAV file!");
835 delete _ptrFileUtilityObj;
836 _ptrFileUtilityObj = NULL;
837 return -1;
838 }
839 _fileFormat = kFileFormatWavFile;
840 break;
841 }
842 case kFileFormatCompressedFile:
843 {
844 // Write compression codec name at beginning of file
845 if(_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) ==
846 -1)
847 {
848 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
849 "Failed to initialize Compressed file!");
850 delete _ptrFileUtilityObj;
851 _ptrFileUtilityObj = NULL;
852 return -1;
853 }
854 _fileFormat = kFileFormatCompressedFile;
855 break;
856 }
857 case kFileFormatPcm8kHzFile:
858 case kFileFormatPcm16kHzFile:
859 {
860 if(!ValidFrequency(codecInst.plfreq) ||
861 _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) ==
862 -1)
863 {
864 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
865 "Failed to initialize 8 or 16KHz PCM file!");
866 delete _ptrFileUtilityObj;
867 _ptrFileUtilityObj = NULL;
868 return -1;
869 }
870 _fileFormat = format;
871 break;
872 }
873 case kFileFormatPreencodedFile:
874 {
875 if(_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) ==
876 -1)
877 {
878 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
879 "Failed to initialize Pre-Encoded file!");
880 delete _ptrFileUtilityObj;
881 _ptrFileUtilityObj = NULL;
882 return -1;
883 }
884
885 _fileFormat = kFileFormatPreencodedFile;
886 break;
887 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000888 default:
889 {
890 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
891 "Invalid file format %d specified!", format);
892 delete _ptrFileUtilityObj;
893 _ptrFileUtilityObj = NULL;
894 return -1;
895 }
896 }
897 _isStereo = (tmpAudioCodec.channels == 2);
898 if(_isStereo)
899 {
900 if(_fileFormat != kFileFormatWavFile)
901 {
902 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
903 "Stereo is only allowed for WAV files");
904 StopRecording();
905 return -1;
906 }
niklas.enbom@webrtc.org87885e82012-02-07 14:48:59 +0000907 if((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) &&
908 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) &&
909 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0))
niklase@google.com470e71d2011-07-07 08:21:25 +0000910 {
911 WEBRTC_TRACE(
912 kTraceWarning,
913 kTraceFile,
914 _id,
915 "Stereo is only allowed for codec PCMU, PCMA and L16 ");
916 StopRecording();
917 return -1;
918 }
919 }
920 memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst));
921 _recordingActive = true;
922 _ptrOutStream = &stream;
923 _notificationMs = notificationTimeMs;
924 _recordDurationMs = 0;
925 return 0;
926}
927
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000928int32_t MediaFileImpl::StopRecording()
niklase@google.com470e71d2011-07-07 08:21:25 +0000929{
niklase@google.com470e71d2011-07-07 08:21:25 +0000930
931 CriticalSectionScoped lock(_crit);
932 if(!_recordingActive)
933 {
934 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
935 "recording is not active!");
936 return -1;
937 }
938
939 _isStereo = false;
940
941 if(_ptrFileUtilityObj != NULL)
942 {
943 // Both AVI and WAV header has to be updated before closing the stream
944 // because they contain size information.
945 if((_fileFormat == kFileFormatWavFile) &&
946 (_ptrOutStream != NULL))
947 {
948 _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream);
949 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000950 delete _ptrFileUtilityObj;
951 _ptrFileUtilityObj = NULL;
952 }
953
954 if(_ptrOutStream != NULL)
955 {
956 // If MediaFileImpl opened the OutStream it must be reclaimed here.
957 if(_openFile)
958 {
959 delete _ptrOutStream;
960 _openFile = false;
961 }
962 _ptrOutStream = NULL;
963 }
964
965 _recordingActive = false;
966 codec_info_.pltype = 0;
967 codec_info_.plname[0] = '\0';
968
969 return 0;
970}
971
972bool MediaFileImpl::IsRecording()
973{
974 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsRecording()");
975 CriticalSectionScoped lock(_crit);
976 return _recordingActive;
977}
978
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000979int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000980{
niklase@google.com470e71d2011-07-07 08:21:25 +0000981
982 CriticalSectionScoped lock(_crit);
983 if(!_recordingActive)
984 {
985 durationMs = 0;
986 return -1;
987 }
988 durationMs = _recordDurationMs;
989 return 0;
990}
991
992bool MediaFileImpl::IsStereo()
993{
994 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsStereo()");
995 CriticalSectionScoped lock(_crit);
996 return _isStereo;
997}
998
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000999int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback)
niklase@google.com470e71d2011-07-07 08:21:25 +00001000{
niklase@google.com470e71d2011-07-07 08:21:25 +00001001
1002 CriticalSectionScoped lock(_callbackCrit);
1003
1004 _ptrCallback = callback;
1005 return 0;
1006}
1007
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001008int32_t MediaFileImpl::FileDurationMs(const char* fileName,
1009 uint32_t& durationMs,
1010 const FileFormats format,
1011 const uint32_t freqInHz)
niklase@google.com470e71d2011-07-07 08:21:25 +00001012{
niklase@google.com470e71d2011-07-07 08:21:25 +00001013
1014 if(!ValidFileName(fileName))
1015 {
1016 return -1;
1017 }
1018 if(!ValidFrequency(freqInHz))
1019 {
1020 return -1;
1021 }
1022
1023 ModuleFileUtility* utilityObj = new ModuleFileUtility(_id);
1024 if(utilityObj == NULL)
1025 {
1026 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1027 "failed to allocate utility object!");
1028 return -1;
1029 }
1030
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001031 const int32_t duration = utilityObj->FileDurationMs(fileName, format,
1032 freqInHz);
niklase@google.com470e71d2011-07-07 08:21:25 +00001033 delete utilityObj;
1034 if(duration == -1)
1035 {
1036 durationMs = 0;
1037 return -1;
1038 }
1039
1040 durationMs = duration;
1041 return 0;
1042}
1043
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001044int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const
niklase@google.com470e71d2011-07-07 08:21:25 +00001045{
niklase@google.com470e71d2011-07-07 08:21:25 +00001046 CriticalSectionScoped lock(_crit);
1047 if(!_playingActive)
1048 {
1049 positionMs = 0;
1050 return -1;
1051 }
1052 positionMs = _playoutPositionMs;
1053 return 0;
1054}
1055
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001056int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const
niklase@google.com470e71d2011-07-07 08:21:25 +00001057{
niklase@google.com470e71d2011-07-07 08:21:25 +00001058 CriticalSectionScoped lock(_crit);
1059 if(!_playingActive && !_recordingActive)
1060 {
1061 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1062 "Neither playout nor recording has been initialized!");
1063 return -1;
1064 }
1065 if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0')
1066 {
1067 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1068 "The CodecInst for %s is unknown!",
1069 _playingActive ? "Playback" : "Recording");
1070 return -1;
1071 }
1072 memcpy(&codecInst,&codec_info_,sizeof(CodecInst));
1073 return 0;
1074}
1075
niklase@google.com470e71d2011-07-07 08:21:25 +00001076bool MediaFileImpl::ValidFileFormat(const FileFormats format,
1077 const CodecInst* codecInst)
1078{
1079 if(codecInst == NULL)
1080 {
1081 if(format == kFileFormatPreencodedFile ||
1082 format == kFileFormatPcm8kHzFile ||
1083 format == kFileFormatPcm16kHzFile ||
1084 format == kFileFormatPcm32kHzFile)
1085 {
1086 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1087 "Codec info required for file format specified!");
1088 return false;
1089 }
1090 }
1091 return true;
1092}
1093
leozwang@webrtc.org09e77192012-03-01 18:35:54 +00001094bool MediaFileImpl::ValidFileName(const char* fileName)
niklase@google.com470e71d2011-07-07 08:21:25 +00001095{
1096 if((fileName == NULL) ||(fileName[0] == '\0'))
1097 {
1098 WEBRTC_TRACE(kTraceError, kTraceFile, -1, "FileName not specified!");
1099 return false;
1100 }
1101 return true;
1102}
1103
1104
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001105bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs,
1106 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +00001107{
1108 if(startPointMs == 0 && stopPointMs == 0) // Default values
1109 {
1110 return true;
1111 }
1112 if(stopPointMs &&(startPointMs >= stopPointMs))
1113 {
1114 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1115 "startPointMs must be less than stopPointMs!");
1116 return false;
1117 }
1118 if(stopPointMs &&((stopPointMs - startPointMs) < 20))
1119 {
1120 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1121 "minimum play duration for files is 20 ms!");
1122 return false;
1123 }
1124 return true;
1125}
1126
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001127bool MediaFileImpl::ValidFrequency(const uint32_t frequency)
niklase@google.com470e71d2011-07-07 08:21:25 +00001128{
1129 if((frequency == 8000) || (frequency == 16000)|| (frequency == 32000))
1130 {
1131 return true;
1132 }
1133 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1134 "Frequency should be 8000, 16000 or 32000 (Hz)");
1135 return false;
1136}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +00001137} // namespace webrtc