blob: 27fe9613a3ba8ab8833a2b0833946262303aebdb [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
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000374 if(inputStream->OpenFile(fileName, true, loop) != 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000375 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000376 delete inputStream;
377 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
378 "Could not open input file %s", fileName);
379 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000380 }
381
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000382 if(StartPlayingStream(*inputStream, loop, notificationTimeMs,
383 format, codecInst, startPointMs, stopPointMs) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000384 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000385 inputStream->CloseFile();
niklase@google.com470e71d2011-07-07 08:21:25 +0000386 delete inputStream;
387 return -1;
388 }
389
390 CriticalSectionScoped lock(_crit);
391 _openFile = true;
392 strncpy(_fileName, fileName, sizeof(_fileName));
393 _fileName[sizeof(_fileName) - 1] = '\0';
394 return 0;
395}
396
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000397int32_t MediaFileImpl::StartPlayingAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000398 InStream& stream,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000399 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000400 const FileFormats format,
401 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000402 const uint32_t startPointMs,
403 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000404{
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000405 return StartPlayingStream(stream, false, notificationTimeMs, format,
niklase@google.com470e71d2011-07-07 08:21:25 +0000406 codecInst, startPointMs, stopPointMs);
407}
408
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000409int32_t MediaFileImpl::StartPlayingStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000410 InStream& stream,
niklase@google.com470e71d2011-07-07 08:21:25 +0000411 bool loop,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000412 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000413 const FileFormats format,
414 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000415 const uint32_t startPointMs,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000416 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000417{
niklase@google.com470e71d2011-07-07 08:21:25 +0000418 if(!ValidFileFormat(format,codecInst))
419 {
420 return -1;
421 }
422
423 if(!ValidFilePositions(startPointMs,stopPointMs))
424 {
425 return -1;
426 }
427
428 CriticalSectionScoped lock(_crit);
429 if(_playingActive || _recordingActive)
430 {
431 WEBRTC_TRACE(
432 kTraceError,
433 kTraceFile,
434 _id,
435 "StartPlaying called, but already playing or recording file %s",
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000436 (_fileName[0] == '\0') ? "(name not set)" : _fileName);
niklase@google.com470e71d2011-07-07 08:21:25 +0000437 return -1;
438 }
439
440 if(_ptrFileUtilityObj != NULL)
441 {
442 WEBRTC_TRACE(kTraceError,
443 kTraceFile,
444 _id,
445 "StartPlaying called, but FileUtilityObj already exists!");
446 StopPlaying();
447 return -1;
448 }
449
450 _ptrFileUtilityObj = new ModuleFileUtility(_id);
451 if(_ptrFileUtilityObj == NULL)
452 {
453 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
454 "Failed to create FileUtilityObj!");
455 return -1;
456 }
457
458 switch(format)
459 {
460 case kFileFormatWavFile:
461 {
462 if(_ptrFileUtilityObj->InitWavReading(stream, startPointMs,
463 stopPointMs) == -1)
464 {
465 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
466 "Not a valid WAV file!");
467 StopPlaying();
468 return -1;
469 }
470 _fileFormat = kFileFormatWavFile;
471 break;
472 }
473 case kFileFormatCompressedFile:
474 {
475 if(_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs,
476 stopPointMs) == -1)
477 {
478 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
479 "Not a valid Compressed file!");
480 StopPlaying();
481 return -1;
482 }
483 _fileFormat = kFileFormatCompressedFile;
484 break;
485 }
486 case kFileFormatPcm8kHzFile:
487 case kFileFormatPcm16kHzFile:
488 case kFileFormatPcm32kHzFile:
489 {
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000490 // ValidFileFormat() called in the beginneing of this function
491 // prevents codecInst from being NULL here.
492 assert(codecInst != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000493 if(!ValidFrequency(codecInst->plfreq) ||
494 _ptrFileUtilityObj->InitPCMReading(stream, startPointMs,
495 stopPointMs,
496 codecInst->plfreq) == -1)
497 {
498 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
499 "Not a valid raw 8 or 16 KHz PCM file!");
500 StopPlaying();
501 return -1;
502 }
503
504 _fileFormat = format;
505 break;
506 }
507 case kFileFormatPreencodedFile:
508 {
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000509 // ValidFileFormat() called in the beginneing of this function
510 // prevents codecInst from being NULL here.
511 assert(codecInst != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000512 if(_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) ==
513 -1)
514 {
515 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
516 "Not a valid PreEncoded file!");
517 StopPlaying();
518 return -1;
519 }
520
521 _fileFormat = kFileFormatPreencodedFile;
522 break;
523 }
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000524 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000525 {
niklase@google.com470e71d2011-07-07 08:21:25 +0000526 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000527 "Invalid file format: %d", format);
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000528 assert(false);
529 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000530 }
531 }
532 if(_ptrFileUtilityObj->codec_info(codec_info_) == -1)
533 {
534 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
535 "Failed to retrieve codec info!");
536 StopPlaying();
537 return -1;
538 }
539
540 _isStereo = (codec_info_.channels == 2);
541 if(_isStereo && (_fileFormat != kFileFormatWavFile))
542 {
543 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
544 "Stereo is only allowed for WAV files");
545 StopPlaying();
546 return -1;
547 }
548 _playingActive = true;
549 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
550 _ptrInStream = &stream;
551 _notificationMs = notificationTimeMs;
552
553 return 0;
554}
555
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000556int32_t MediaFileImpl::StopPlaying()
niklase@google.com470e71d2011-07-07 08:21:25 +0000557{
niklase@google.com470e71d2011-07-07 08:21:25 +0000558
559 CriticalSectionScoped lock(_crit);
560 _isStereo = false;
561 if(_ptrFileUtilityObj)
562 {
563 delete _ptrFileUtilityObj;
564 _ptrFileUtilityObj = NULL;
565 }
566 if(_ptrInStream)
567 {
568 // If MediaFileImpl opened the InStream it must be reclaimed here.
569 if(_openFile)
570 {
571 delete _ptrInStream;
572 _openFile = false;
573 }
574 _ptrInStream = NULL;
575 }
576
577 codec_info_.pltype = 0;
578 codec_info_.plname[0] = '\0';
579
580 if(!_playingActive)
581 {
582 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
583 "playing is not active!");
584 return -1;
585 }
586
587 _playingActive = false;
588 return 0;
589}
590
591bool MediaFileImpl::IsPlaying()
592{
593 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsPlaying()");
594 CriticalSectionScoped lock(_crit);
595 return _playingActive;
596}
597
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000598int32_t MediaFileImpl::IncomingAudioData(
599 const int8_t* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000600 const size_t bufferLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000601{
niklase@google.com470e71d2011-07-07 08:21:25 +0000602 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000603 "MediaFile::IncomingData(buffer= 0x%x, bufLen= %" PRIuS,
niklase@google.com470e71d2011-07-07 08:21:25 +0000604 buffer, bufferLengthInBytes);
605
606 if(buffer == NULL || bufferLengthInBytes == 0)
607 {
608 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
609 "Buffer pointer or length is NULL!");
610 return -1;
611 }
612
613 bool recordingEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000614 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000615 {
616 CriticalSectionScoped lock(_crit);
617
618 if(!_recordingActive)
619 {
620 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
621 "Not currently recording!");
622 return -1;
623 }
624 if(_ptrOutStream == NULL)
625 {
626 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
627 "Recording is active, but output stream is NULL!");
628 assert(false);
629 return -1;
630 }
631
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000632 int32_t bytesWritten = 0;
633 uint32_t samplesWritten = codec_info_.pacsize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000634 if(_ptrFileUtilityObj)
635 {
636 switch(_fileFormat)
637 {
638 case kFileFormatPcm8kHzFile:
639 case kFileFormatPcm16kHzFile:
640 case kFileFormatPcm32kHzFile:
641 bytesWritten = _ptrFileUtilityObj->WritePCMData(
642 *_ptrOutStream,
643 buffer,
644 bufferLengthInBytes);
645
646 // Sample size is 2 bytes.
647 if(bytesWritten > 0)
648 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000649 samplesWritten = bytesWritten/sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000650 }
651 break;
652 case kFileFormatCompressedFile:
653 bytesWritten = _ptrFileUtilityObj->WriteCompressedData(
654 *_ptrOutStream, buffer, bufferLengthInBytes);
655 break;
656 case kFileFormatWavFile:
657 bytesWritten = _ptrFileUtilityObj->WriteWavData(
658 *_ptrOutStream,
659 buffer,
660 bufferLengthInBytes);
661 if(bytesWritten > 0 && STR_NCASE_CMP(codec_info_.plname,
662 "L16", 4) == 0)
663 {
664 // Sample size is 2 bytes.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000665 samplesWritten = bytesWritten/sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000666 }
667 break;
668 case kFileFormatPreencodedFile:
669 bytesWritten = _ptrFileUtilityObj->WritePreEncodedData(
670 *_ptrOutStream, buffer, bufferLengthInBytes);
671 break;
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000672 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000673 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000674 "Invalid file format: %d", _fileFormat);
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000675 assert(false);
niklase@google.com470e71d2011-07-07 08:21:25 +0000676 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000677 }
678 } else {
679 // TODO (hellner): quick look at the code makes me think that this
680 // code is never executed. Remove?
681 if(_ptrOutStream)
682 {
683 if(_ptrOutStream->Write(buffer, bufferLengthInBytes))
684 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000685 bytesWritten = static_cast<int32_t>(bufferLengthInBytes);
niklase@google.com470e71d2011-07-07 08:21:25 +0000686 }
687 }
688 }
689
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000690 _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000);
niklase@google.com470e71d2011-07-07 08:21:25 +0000691
692 // Check if it's time for RecordNotification(..).
693 if(_notificationMs)
694 {
695 if(_recordDurationMs >= _notificationMs)
696 {
697 _notificationMs = 0;
698 callbackNotifyMs = _recordDurationMs;
699 }
700 }
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000701 if(bytesWritten < (int32_t)bufferLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000702 {
703 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
704 "Failed to write all requested bytes!");
705 StopRecording();
706 recordingEnded = true;
707 }
708 }
709
710 // Only _callbackCrit may and should be taken when making callbacks.
711 CriticalSectionScoped lock(_callbackCrit);
712 if(_ptrCallback)
713 {
714 if(callbackNotifyMs)
715 {
716 _ptrCallback->RecordNotification(_id, callbackNotifyMs);
717 }
718 if(recordingEnded)
719 {
720 _ptrCallback->RecordFileEnded(_id);
721 return -1;
722 }
723 }
724 return 0;
725}
726
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000727int32_t MediaFileImpl::StartRecordingAudioFile(
leozwang@webrtc.org09e77192012-03-01 18:35:54 +0000728 const char* fileName,
niklase@google.com470e71d2011-07-07 08:21:25 +0000729 const FileFormats format,
730 const CodecInst& codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000731 const uint32_t notificationTimeMs,
732 const uint32_t maxSizeBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000733{
niklase@google.com470e71d2011-07-07 08:21:25 +0000734 if(!ValidFileName(fileName))
735 {
736 return -1;
737 }
738 if(!ValidFileFormat(format,&codecInst))
739 {
740 return -1;
741 }
742
743 FileWrapper* outputStream = FileWrapper::Create();
744 if(outputStream == NULL)
745 {
746 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
747 "Failed to allocate memory for output stream");
748 return -1;
749 }
750
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000751 if(outputStream->OpenFile(fileName, false) != 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000752 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000753 delete outputStream;
754 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
755 "Could not open output file '%s' for writing!",
756 fileName);
757 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000758 }
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000759
niklase@google.com470e71d2011-07-07 08:21:25 +0000760 if(maxSizeBytes)
761 {
762 outputStream->SetMaxFileSize(maxSizeBytes);
763 }
764
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000765 if(StartRecordingAudioStream(*outputStream, format, codecInst,
766 notificationTimeMs) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000767 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000768 outputStream->CloseFile();
niklase@google.com470e71d2011-07-07 08:21:25 +0000769 delete outputStream;
770 return -1;
771 }
772
773 CriticalSectionScoped lock(_crit);
774 _openFile = true;
775 strncpy(_fileName, fileName, sizeof(_fileName));
776 _fileName[sizeof(_fileName) - 1] = '\0';
777 return 0;
778}
779
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000780int32_t MediaFileImpl::StartRecordingAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000781 OutStream& stream,
782 const FileFormats format,
783 const CodecInst& codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000784 const uint32_t notificationTimeMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000785{
niklase@google.com470e71d2011-07-07 08:21:25 +0000786 // Check codec info
787 if(!ValidFileFormat(format,&codecInst))
788 {
789 return -1;
790 }
791
792 CriticalSectionScoped lock(_crit);
793 if(_recordingActive || _playingActive)
794 {
795 WEBRTC_TRACE(
796 kTraceError,
797 kTraceFile,
798 _id,
799 "StartRecording called, but already recording or playing file %s!",
800 _fileName);
801 return -1;
802 }
803
804 if(_ptrFileUtilityObj != NULL)
805 {
806 WEBRTC_TRACE(
807 kTraceError,
808 kTraceFile,
809 _id,
810 "StartRecording called, but fileUtilityObj already exists!");
811 StopRecording();
812 return -1;
813 }
814
815 _ptrFileUtilityObj = new ModuleFileUtility(_id);
816 if(_ptrFileUtilityObj == NULL)
817 {
818 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
819 "Cannot allocate fileUtilityObj!");
820 return -1;
821 }
822
823 CodecInst tmpAudioCodec;
824 memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst));
825 switch(format)
826 {
827 case kFileFormatWavFile:
828 {
829 if(_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1)
830 {
831 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
832 "Failed to initialize WAV file!");
833 delete _ptrFileUtilityObj;
834 _ptrFileUtilityObj = NULL;
835 return -1;
836 }
837 _fileFormat = kFileFormatWavFile;
838 break;
839 }
840 case kFileFormatCompressedFile:
841 {
842 // Write compression codec name at beginning of file
843 if(_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) ==
844 -1)
845 {
846 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
847 "Failed to initialize Compressed file!");
848 delete _ptrFileUtilityObj;
849 _ptrFileUtilityObj = NULL;
850 return -1;
851 }
852 _fileFormat = kFileFormatCompressedFile;
853 break;
854 }
855 case kFileFormatPcm8kHzFile:
856 case kFileFormatPcm16kHzFile:
857 {
858 if(!ValidFrequency(codecInst.plfreq) ||
859 _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) ==
860 -1)
861 {
862 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
863 "Failed to initialize 8 or 16KHz PCM file!");
864 delete _ptrFileUtilityObj;
865 _ptrFileUtilityObj = NULL;
866 return -1;
867 }
868 _fileFormat = format;
869 break;
870 }
871 case kFileFormatPreencodedFile:
872 {
873 if(_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) ==
874 -1)
875 {
876 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
877 "Failed to initialize Pre-Encoded file!");
878 delete _ptrFileUtilityObj;
879 _ptrFileUtilityObj = NULL;
880 return -1;
881 }
882
883 _fileFormat = kFileFormatPreencodedFile;
884 break;
885 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000886 default:
887 {
888 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
889 "Invalid file format %d specified!", format);
890 delete _ptrFileUtilityObj;
891 _ptrFileUtilityObj = NULL;
892 return -1;
893 }
894 }
895 _isStereo = (tmpAudioCodec.channels == 2);
896 if(_isStereo)
897 {
898 if(_fileFormat != kFileFormatWavFile)
899 {
900 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
901 "Stereo is only allowed for WAV files");
902 StopRecording();
903 return -1;
904 }
niklas.enbom@webrtc.org87885e82012-02-07 14:48:59 +0000905 if((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) &&
906 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) &&
907 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0))
niklase@google.com470e71d2011-07-07 08:21:25 +0000908 {
909 WEBRTC_TRACE(
910 kTraceWarning,
911 kTraceFile,
912 _id,
913 "Stereo is only allowed for codec PCMU, PCMA and L16 ");
914 StopRecording();
915 return -1;
916 }
917 }
918 memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst));
919 _recordingActive = true;
920 _ptrOutStream = &stream;
921 _notificationMs = notificationTimeMs;
922 _recordDurationMs = 0;
923 return 0;
924}
925
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000926int32_t MediaFileImpl::StopRecording()
niklase@google.com470e71d2011-07-07 08:21:25 +0000927{
niklase@google.com470e71d2011-07-07 08:21:25 +0000928
929 CriticalSectionScoped lock(_crit);
930 if(!_recordingActive)
931 {
932 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
933 "recording is not active!");
934 return -1;
935 }
936
937 _isStereo = false;
938
939 if(_ptrFileUtilityObj != NULL)
940 {
941 // Both AVI and WAV header has to be updated before closing the stream
942 // because they contain size information.
943 if((_fileFormat == kFileFormatWavFile) &&
944 (_ptrOutStream != NULL))
945 {
946 _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream);
947 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000948 delete _ptrFileUtilityObj;
949 _ptrFileUtilityObj = NULL;
950 }
951
952 if(_ptrOutStream != NULL)
953 {
954 // If MediaFileImpl opened the OutStream it must be reclaimed here.
955 if(_openFile)
956 {
957 delete _ptrOutStream;
958 _openFile = false;
959 }
960 _ptrOutStream = NULL;
961 }
962
963 _recordingActive = false;
964 codec_info_.pltype = 0;
965 codec_info_.plname[0] = '\0';
966
967 return 0;
968}
969
970bool MediaFileImpl::IsRecording()
971{
972 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsRecording()");
973 CriticalSectionScoped lock(_crit);
974 return _recordingActive;
975}
976
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000977int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000978{
niklase@google.com470e71d2011-07-07 08:21:25 +0000979
980 CriticalSectionScoped lock(_crit);
981 if(!_recordingActive)
982 {
983 durationMs = 0;
984 return -1;
985 }
986 durationMs = _recordDurationMs;
987 return 0;
988}
989
990bool MediaFileImpl::IsStereo()
991{
992 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsStereo()");
993 CriticalSectionScoped lock(_crit);
994 return _isStereo;
995}
996
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000997int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback)
niklase@google.com470e71d2011-07-07 08:21:25 +0000998{
niklase@google.com470e71d2011-07-07 08:21:25 +0000999
1000 CriticalSectionScoped lock(_callbackCrit);
1001
1002 _ptrCallback = callback;
1003 return 0;
1004}
1005
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001006int32_t MediaFileImpl::FileDurationMs(const char* fileName,
1007 uint32_t& durationMs,
1008 const FileFormats format,
1009 const uint32_t freqInHz)
niklase@google.com470e71d2011-07-07 08:21:25 +00001010{
niklase@google.com470e71d2011-07-07 08:21:25 +00001011
1012 if(!ValidFileName(fileName))
1013 {
1014 return -1;
1015 }
1016 if(!ValidFrequency(freqInHz))
1017 {
1018 return -1;
1019 }
1020
1021 ModuleFileUtility* utilityObj = new ModuleFileUtility(_id);
1022 if(utilityObj == NULL)
1023 {
1024 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1025 "failed to allocate utility object!");
1026 return -1;
1027 }
1028
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001029 const int32_t duration = utilityObj->FileDurationMs(fileName, format,
1030 freqInHz);
niklase@google.com470e71d2011-07-07 08:21:25 +00001031 delete utilityObj;
1032 if(duration == -1)
1033 {
1034 durationMs = 0;
1035 return -1;
1036 }
1037
1038 durationMs = duration;
1039 return 0;
1040}
1041
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001042int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const
niklase@google.com470e71d2011-07-07 08:21:25 +00001043{
niklase@google.com470e71d2011-07-07 08:21:25 +00001044 CriticalSectionScoped lock(_crit);
1045 if(!_playingActive)
1046 {
1047 positionMs = 0;
1048 return -1;
1049 }
1050 positionMs = _playoutPositionMs;
1051 return 0;
1052}
1053
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001054int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const
niklase@google.com470e71d2011-07-07 08:21:25 +00001055{
niklase@google.com470e71d2011-07-07 08:21:25 +00001056 CriticalSectionScoped lock(_crit);
1057 if(!_playingActive && !_recordingActive)
1058 {
1059 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1060 "Neither playout nor recording has been initialized!");
1061 return -1;
1062 }
1063 if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0')
1064 {
1065 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1066 "The CodecInst for %s is unknown!",
1067 _playingActive ? "Playback" : "Recording");
1068 return -1;
1069 }
1070 memcpy(&codecInst,&codec_info_,sizeof(CodecInst));
1071 return 0;
1072}
1073
niklase@google.com470e71d2011-07-07 08:21:25 +00001074bool MediaFileImpl::ValidFileFormat(const FileFormats format,
1075 const CodecInst* codecInst)
1076{
1077 if(codecInst == NULL)
1078 {
1079 if(format == kFileFormatPreencodedFile ||
1080 format == kFileFormatPcm8kHzFile ||
1081 format == kFileFormatPcm16kHzFile ||
1082 format == kFileFormatPcm32kHzFile)
1083 {
1084 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1085 "Codec info required for file format specified!");
1086 return false;
1087 }
1088 }
1089 return true;
1090}
1091
leozwang@webrtc.org09e77192012-03-01 18:35:54 +00001092bool MediaFileImpl::ValidFileName(const char* fileName)
niklase@google.com470e71d2011-07-07 08:21:25 +00001093{
1094 if((fileName == NULL) ||(fileName[0] == '\0'))
1095 {
1096 WEBRTC_TRACE(kTraceError, kTraceFile, -1, "FileName not specified!");
1097 return false;
1098 }
1099 return true;
1100}
1101
1102
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001103bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs,
1104 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +00001105{
1106 if(startPointMs == 0 && stopPointMs == 0) // Default values
1107 {
1108 return true;
1109 }
1110 if(stopPointMs &&(startPointMs >= stopPointMs))
1111 {
1112 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1113 "startPointMs must be less than stopPointMs!");
1114 return false;
1115 }
1116 if(stopPointMs &&((stopPointMs - startPointMs) < 20))
1117 {
1118 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1119 "minimum play duration for files is 20 ms!");
1120 return false;
1121 }
1122 return true;
1123}
1124
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001125bool MediaFileImpl::ValidFrequency(const uint32_t frequency)
niklase@google.com470e71d2011-07-07 08:21:25 +00001126{
1127 if((frequency == 8000) || (frequency == 16000)|| (frequency == 32000))
1128 {
1129 return true;
1130 }
1131 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1132 "Frequency should be 8000, 16000 or 32000 (Hz)");
1133 return false;
1134}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +00001135} // namespace webrtc