blob: 76bcca74d2c66d9b63ebe97d579ddb74b39ae774 [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
Peter Boströmf14c47a2016-02-24 16:51:18 +010098void 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.");
niklase@google.com470e71d2011-07-07 08:21:25 +0000102}
103
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000104int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000105 size_t& dataLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000106{
niklase@google.com470e71d2011-07-07 08:21:25 +0000107 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000108 "MediaFileImpl::PlayoutData(buffer= 0x%x, bufLen= %" PRIuS ")",
niklase@google.com470e71d2011-07-07 08:21:25 +0000109 buffer, dataLengthInBytes);
110
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000111 const size_t bufferLengthInBytes = dataLengthInBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000112 dataLengthInBytes = 0;
113
114 if(buffer == NULL || bufferLengthInBytes == 0)
115 {
116 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
117 "Buffer pointer or length is NULL!");
118 return -1;
119 }
120
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000121 int32_t bytesRead = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000122 {
123 CriticalSectionScoped lock(_crit);
124
125 if(!_playingActive)
126 {
127 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
128 "Not currently playing!");
129 return -1;
130 }
131
132 if(!_ptrFileUtilityObj)
133 {
134 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
135 "Playing, but no FileUtility object!");
136 StopPlaying();
137 return -1;
138 }
139
140 switch(_fileFormat)
141 {
142 case kFileFormatPcm32kHzFile:
143 case kFileFormatPcm16kHzFile:
144 case kFileFormatPcm8kHzFile:
145 bytesRead = _ptrFileUtilityObj->ReadPCMData(
146 *_ptrInStream,
147 buffer,
148 bufferLengthInBytes);
149 break;
150 case kFileFormatCompressedFile:
151 bytesRead = _ptrFileUtilityObj->ReadCompressedData(
152 *_ptrInStream,
153 buffer,
154 bufferLengthInBytes);
155 break;
156 case kFileFormatWavFile:
157 bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono(
158 *_ptrInStream,
159 buffer,
160 bufferLengthInBytes);
161 break;
162 case kFileFormatPreencodedFile:
163 bytesRead = _ptrFileUtilityObj->ReadPreEncodedData(
164 *_ptrInStream,
165 buffer,
166 bufferLengthInBytes);
167 if(bytesRead > 0)
168 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000169 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000170 return 0;
171 }
172 break;
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000173 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000174 {
niklase@google.com470e71d2011-07-07 08:21:25 +0000175 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000176 "Invalid file format: %d", _fileFormat);
niklase@google.com470e71d2011-07-07 08:21:25 +0000177 assert(false);
178 break;
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000179 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000180 }
181
182 if( bytesRead > 0)
183 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000184 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000185 }
186 }
187 HandlePlayCallbacks(bytesRead);
188 return 0;
189}
190
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000191void MediaFileImpl::HandlePlayCallbacks(int32_t bytesRead)
niklase@google.com470e71d2011-07-07 08:21:25 +0000192{
193 bool playEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000194 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000195
196 if(bytesRead > 0)
197 {
198 // Check if it's time for PlayNotification(..).
199 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
200 if(_notificationMs)
201 {
202 if(_playoutPositionMs >= _notificationMs)
203 {
204 _notificationMs = 0;
205 callbackNotifyMs = _playoutPositionMs;
206 }
207 }
208 }
209 else
210 {
211 // If no bytes were read assume end of file.
212 StopPlaying();
213 playEnded = true;
214 }
215
216 // Only _callbackCrit may and should be taken when making callbacks.
217 CriticalSectionScoped lock(_callbackCrit);
218 if(_ptrCallback)
219 {
220 if(callbackNotifyMs)
221 {
222 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
223 }
224 if(playEnded)
225 {
226 _ptrCallback->PlayFileEnded(_id);
227 }
228 }
229}
230
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000231int32_t MediaFileImpl::PlayoutStereoData(
232 int8_t* bufferLeft,
233 int8_t* bufferRight,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000234 size_t& dataLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000235{
236 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000237 "MediaFileImpl::PlayoutStereoData(Left = 0x%x, Right = 0x%x,"
238 " Len= %" PRIuS ")",
niklase@google.com470e71d2011-07-07 08:21:25 +0000239 bufferLeft,
240 bufferRight,
241 dataLengthInBytes);
242
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000243 const size_t bufferLengthInBytes = dataLengthInBytes;
niklase@google.com470e71d2011-07-07 08:21:25 +0000244 dataLengthInBytes = 0;
245
246 if(bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0)
247 {
248 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
249 "A buffer pointer or the length is NULL!");
250 return -1;
251 }
252
253 bool playEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000254 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000255 {
256 CriticalSectionScoped lock(_crit);
257
258 if(!_playingActive || !_isStereo)
259 {
260 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
261 "Not currently playing stereo!");
262 return -1;
263 }
264
265 if(!_ptrFileUtilityObj)
266 {
267 WEBRTC_TRACE(
268 kTraceError,
269 kTraceFile,
270 _id,
271 "Playing stereo, but the FileUtility objects is NULL!");
272 StopPlaying();
273 return -1;
274 }
275
276 // Stereo playout only supported for WAV files.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000277 int32_t bytesRead = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000278 switch(_fileFormat)
279 {
280 case kFileFormatWavFile:
281 bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo(
282 *_ptrInStream,
283 bufferLeft,
284 bufferRight,
285 bufferLengthInBytes);
286 break;
287 default:
288 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
289 "Trying to read non-WAV as stereo audio\
290 (not supported)");
291 break;
292 }
293
294 if(bytesRead > 0)
295 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000296 dataLengthInBytes = static_cast<size_t>(bytesRead);
niklase@google.com470e71d2011-07-07 08:21:25 +0000297
298 // Check if it's time for PlayNotification(..).
299 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
300 if(_notificationMs)
301 {
302 if(_playoutPositionMs >= _notificationMs)
303 {
304 _notificationMs = 0;
305 callbackNotifyMs = _playoutPositionMs;
306 }
307 }
308 }
309 else
310 {
311 // If no bytes were read assume end of file.
312 StopPlaying();
313 playEnded = true;
314 }
315 }
316
317 CriticalSectionScoped lock(_callbackCrit);
318 if(_ptrCallback)
319 {
320 if(callbackNotifyMs)
321 {
322 _ptrCallback->PlayNotification(_id, callbackNotifyMs);
323 }
324 if(playEnded)
325 {
326 _ptrCallback->PlayFileEnded(_id);
327 }
328 }
329 return 0;
330}
331
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000332int32_t MediaFileImpl::StartPlayingAudioFile(
leozwang@webrtc.org09e77192012-03-01 18:35:54 +0000333 const char* fileName,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000334 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000335 const bool loop,
336 const FileFormats format,
337 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000338 const uint32_t startPointMs,
339 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000340{
niklase@google.com470e71d2011-07-07 08:21:25 +0000341 if(!ValidFileName(fileName))
342 {
343 return -1;
344 }
345 if(!ValidFileFormat(format,codecInst))
346 {
347 return -1;
348 }
349 if(!ValidFilePositions(startPointMs,stopPointMs))
350 {
351 return -1;
352 }
353
354 // Check that the file will play longer than notificationTimeMs ms.
355 if((startPointMs && stopPointMs && !loop) &&
356 (notificationTimeMs > (stopPointMs - startPointMs)))
357 {
358 WEBRTC_TRACE(
359 kTraceError,
360 kTraceFile,
361 _id,
362 "specified notification time is longer than amount of ms that will\
363 be played");
364 return -1;
365 }
366
367 FileWrapper* inputStream = FileWrapper::Create();
368 if(inputStream == NULL)
369 {
370 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
371 "Failed to allocate input stream for file %s", fileName);
372 return -1;
373 }
374
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000375 if(inputStream->OpenFile(fileName, true, loop) != 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000376 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000377 delete inputStream;
378 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
379 "Could not open input file %s", fileName);
380 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000381 }
382
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000383 if(StartPlayingStream(*inputStream, loop, notificationTimeMs,
384 format, codecInst, startPointMs, stopPointMs) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000385 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000386 inputStream->CloseFile();
niklase@google.com470e71d2011-07-07 08:21:25 +0000387 delete inputStream;
388 return -1;
389 }
390
391 CriticalSectionScoped lock(_crit);
392 _openFile = true;
393 strncpy(_fileName, fileName, sizeof(_fileName));
394 _fileName[sizeof(_fileName) - 1] = '\0';
395 return 0;
396}
397
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000398int32_t MediaFileImpl::StartPlayingAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000399 InStream& stream,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000400 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000401 const FileFormats format,
402 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000403 const uint32_t startPointMs,
404 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000405{
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000406 return StartPlayingStream(stream, false, notificationTimeMs, format,
niklase@google.com470e71d2011-07-07 08:21:25 +0000407 codecInst, startPointMs, stopPointMs);
408}
409
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000410int32_t MediaFileImpl::StartPlayingStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000411 InStream& stream,
niklase@google.com470e71d2011-07-07 08:21:25 +0000412 bool loop,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000413 const uint32_t notificationTimeMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000414 const FileFormats format,
415 const CodecInst* codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000416 const uint32_t startPointMs,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000417 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000418{
niklase@google.com470e71d2011-07-07 08:21:25 +0000419 if(!ValidFileFormat(format,codecInst))
420 {
421 return -1;
422 }
423
424 if(!ValidFilePositions(startPointMs,stopPointMs))
425 {
426 return -1;
427 }
428
429 CriticalSectionScoped lock(_crit);
430 if(_playingActive || _recordingActive)
431 {
432 WEBRTC_TRACE(
433 kTraceError,
434 kTraceFile,
435 _id,
436 "StartPlaying called, but already playing or recording file %s",
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000437 (_fileName[0] == '\0') ? "(name not set)" : _fileName);
niklase@google.com470e71d2011-07-07 08:21:25 +0000438 return -1;
439 }
440
441 if(_ptrFileUtilityObj != NULL)
442 {
443 WEBRTC_TRACE(kTraceError,
444 kTraceFile,
445 _id,
446 "StartPlaying called, but FileUtilityObj already exists!");
447 StopPlaying();
448 return -1;
449 }
450
451 _ptrFileUtilityObj = new ModuleFileUtility(_id);
452 if(_ptrFileUtilityObj == NULL)
453 {
454 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
455 "Failed to create FileUtilityObj!");
456 return -1;
457 }
458
459 switch(format)
460 {
461 case kFileFormatWavFile:
462 {
463 if(_ptrFileUtilityObj->InitWavReading(stream, startPointMs,
464 stopPointMs) == -1)
465 {
466 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
467 "Not a valid WAV file!");
468 StopPlaying();
469 return -1;
470 }
471 _fileFormat = kFileFormatWavFile;
472 break;
473 }
474 case kFileFormatCompressedFile:
475 {
476 if(_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs,
477 stopPointMs) == -1)
478 {
479 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
480 "Not a valid Compressed file!");
481 StopPlaying();
482 return -1;
483 }
484 _fileFormat = kFileFormatCompressedFile;
485 break;
486 }
487 case kFileFormatPcm8kHzFile:
488 case kFileFormatPcm16kHzFile:
489 case kFileFormatPcm32kHzFile:
490 {
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000491 // ValidFileFormat() called in the beginneing of this function
492 // prevents codecInst from being NULL here.
493 assert(codecInst != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000494 if(!ValidFrequency(codecInst->plfreq) ||
495 _ptrFileUtilityObj->InitPCMReading(stream, startPointMs,
496 stopPointMs,
497 codecInst->plfreq) == -1)
498 {
499 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
500 "Not a valid raw 8 or 16 KHz PCM file!");
501 StopPlaying();
502 return -1;
503 }
504
505 _fileFormat = format;
506 break;
507 }
508 case kFileFormatPreencodedFile:
509 {
henrike@webrtc.org26085e12012-02-27 21:50:40 +0000510 // ValidFileFormat() called in the beginneing of this function
511 // prevents codecInst from being NULL here.
512 assert(codecInst != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000513 if(_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) ==
514 -1)
515 {
516 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
517 "Not a valid PreEncoded file!");
518 StopPlaying();
519 return -1;
520 }
521
522 _fileFormat = kFileFormatPreencodedFile;
523 break;
524 }
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000525 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000526 {
niklase@google.com470e71d2011-07-07 08:21:25 +0000527 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000528 "Invalid file format: %d", format);
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000529 assert(false);
530 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000531 }
532 }
533 if(_ptrFileUtilityObj->codec_info(codec_info_) == -1)
534 {
535 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
536 "Failed to retrieve codec info!");
537 StopPlaying();
538 return -1;
539 }
540
541 _isStereo = (codec_info_.channels == 2);
542 if(_isStereo && (_fileFormat != kFileFormatWavFile))
543 {
544 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
545 "Stereo is only allowed for WAV files");
546 StopPlaying();
547 return -1;
548 }
549 _playingActive = true;
550 _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
551 _ptrInStream = &stream;
552 _notificationMs = notificationTimeMs;
553
554 return 0;
555}
556
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000557int32_t MediaFileImpl::StopPlaying()
niklase@google.com470e71d2011-07-07 08:21:25 +0000558{
niklase@google.com470e71d2011-07-07 08:21:25 +0000559
560 CriticalSectionScoped lock(_crit);
561 _isStereo = false;
562 if(_ptrFileUtilityObj)
563 {
564 delete _ptrFileUtilityObj;
565 _ptrFileUtilityObj = NULL;
566 }
567 if(_ptrInStream)
568 {
569 // If MediaFileImpl opened the InStream it must be reclaimed here.
570 if(_openFile)
571 {
572 delete _ptrInStream;
573 _openFile = false;
574 }
575 _ptrInStream = NULL;
576 }
577
578 codec_info_.pltype = 0;
579 codec_info_.plname[0] = '\0';
580
581 if(!_playingActive)
582 {
583 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
584 "playing is not active!");
585 return -1;
586 }
587
588 _playingActive = false;
589 return 0;
590}
591
592bool MediaFileImpl::IsPlaying()
593{
594 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsPlaying()");
595 CriticalSectionScoped lock(_crit);
596 return _playingActive;
597}
598
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000599int32_t MediaFileImpl::IncomingAudioData(
600 const int8_t* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000601 const size_t bufferLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000602{
niklase@google.com470e71d2011-07-07 08:21:25 +0000603 WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000604 "MediaFile::IncomingData(buffer= 0x%x, bufLen= %" PRIuS,
niklase@google.com470e71d2011-07-07 08:21:25 +0000605 buffer, bufferLengthInBytes);
606
607 if(buffer == NULL || bufferLengthInBytes == 0)
608 {
609 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
610 "Buffer pointer or length is NULL!");
611 return -1;
612 }
613
614 bool recordingEnded = false;
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000615 uint32_t callbackNotifyMs = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000616 {
617 CriticalSectionScoped lock(_crit);
618
619 if(!_recordingActive)
620 {
621 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
622 "Not currently recording!");
623 return -1;
624 }
625 if(_ptrOutStream == NULL)
626 {
627 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
628 "Recording is active, but output stream is NULL!");
629 assert(false);
630 return -1;
631 }
632
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000633 int32_t bytesWritten = 0;
634 uint32_t samplesWritten = codec_info_.pacsize;
niklase@google.com470e71d2011-07-07 08:21:25 +0000635 if(_ptrFileUtilityObj)
636 {
637 switch(_fileFormat)
638 {
639 case kFileFormatPcm8kHzFile:
640 case kFileFormatPcm16kHzFile:
641 case kFileFormatPcm32kHzFile:
642 bytesWritten = _ptrFileUtilityObj->WritePCMData(
643 *_ptrOutStream,
644 buffer,
645 bufferLengthInBytes);
646
647 // Sample size is 2 bytes.
648 if(bytesWritten > 0)
649 {
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000650 samplesWritten = bytesWritten/sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000651 }
652 break;
653 case kFileFormatCompressedFile:
654 bytesWritten = _ptrFileUtilityObj->WriteCompressedData(
655 *_ptrOutStream, buffer, bufferLengthInBytes);
656 break;
657 case kFileFormatWavFile:
658 bytesWritten = _ptrFileUtilityObj->WriteWavData(
659 *_ptrOutStream,
660 buffer,
661 bufferLengthInBytes);
662 if(bytesWritten > 0 && STR_NCASE_CMP(codec_info_.plname,
663 "L16", 4) == 0)
664 {
665 // Sample size is 2 bytes.
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000666 samplesWritten = bytesWritten/sizeof(int16_t);
niklase@google.com470e71d2011-07-07 08:21:25 +0000667 }
668 break;
669 case kFileFormatPreencodedFile:
670 bytesWritten = _ptrFileUtilityObj->WritePreEncodedData(
671 *_ptrOutStream, buffer, bufferLengthInBytes);
672 break;
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000673 default:
niklase@google.com470e71d2011-07-07 08:21:25 +0000674 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000675 "Invalid file format: %d", _fileFormat);
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +0000676 assert(false);
niklase@google.com470e71d2011-07-07 08:21:25 +0000677 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000678 }
679 } else {
680 // TODO (hellner): quick look at the code makes me think that this
681 // code is never executed. Remove?
682 if(_ptrOutStream)
683 {
684 if(_ptrOutStream->Write(buffer, bufferLengthInBytes))
685 {
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000686 bytesWritten = static_cast<int32_t>(bufferLengthInBytes);
niklase@google.com470e71d2011-07-07 08:21:25 +0000687 }
688 }
689 }
690
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000691 _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000);
niklase@google.com470e71d2011-07-07 08:21:25 +0000692
693 // Check if it's time for RecordNotification(..).
694 if(_notificationMs)
695 {
696 if(_recordDurationMs >= _notificationMs)
697 {
698 _notificationMs = 0;
699 callbackNotifyMs = _recordDurationMs;
700 }
701 }
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000702 if(bytesWritten < (int32_t)bufferLengthInBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000703 {
704 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
705 "Failed to write all requested bytes!");
706 StopRecording();
707 recordingEnded = true;
708 }
709 }
710
711 // Only _callbackCrit may and should be taken when making callbacks.
712 CriticalSectionScoped lock(_callbackCrit);
713 if(_ptrCallback)
714 {
715 if(callbackNotifyMs)
716 {
717 _ptrCallback->RecordNotification(_id, callbackNotifyMs);
718 }
719 if(recordingEnded)
720 {
721 _ptrCallback->RecordFileEnded(_id);
722 return -1;
723 }
724 }
725 return 0;
726}
727
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000728int32_t MediaFileImpl::StartRecordingAudioFile(
leozwang@webrtc.org09e77192012-03-01 18:35:54 +0000729 const char* fileName,
niklase@google.com470e71d2011-07-07 08:21:25 +0000730 const FileFormats format,
731 const CodecInst& codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000732 const uint32_t notificationTimeMs,
733 const uint32_t maxSizeBytes)
niklase@google.com470e71d2011-07-07 08:21:25 +0000734{
niklase@google.com470e71d2011-07-07 08:21:25 +0000735 if(!ValidFileName(fileName))
736 {
737 return -1;
738 }
739 if(!ValidFileFormat(format,&codecInst))
740 {
741 return -1;
742 }
743
744 FileWrapper* outputStream = FileWrapper::Create();
745 if(outputStream == NULL)
746 {
747 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
748 "Failed to allocate memory for output stream");
749 return -1;
750 }
751
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000752 if(outputStream->OpenFile(fileName, false) != 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000753 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000754 delete outputStream;
755 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
756 "Could not open output file '%s' for writing!",
757 fileName);
758 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000759 }
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000760
niklase@google.com470e71d2011-07-07 08:21:25 +0000761 if(maxSizeBytes)
762 {
763 outputStream->SetMaxFileSize(maxSizeBytes);
764 }
765
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000766 if(StartRecordingAudioStream(*outputStream, format, codecInst,
767 notificationTimeMs) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000768 {
andresp@webrtc.orge8f50df2015-03-02 13:07:02 +0000769 outputStream->CloseFile();
niklase@google.com470e71d2011-07-07 08:21:25 +0000770 delete outputStream;
771 return -1;
772 }
773
774 CriticalSectionScoped lock(_crit);
775 _openFile = true;
776 strncpy(_fileName, fileName, sizeof(_fileName));
777 _fileName[sizeof(_fileName) - 1] = '\0';
778 return 0;
779}
780
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000781int32_t MediaFileImpl::StartRecordingAudioStream(
niklase@google.com470e71d2011-07-07 08:21:25 +0000782 OutStream& stream,
783 const FileFormats format,
784 const CodecInst& codecInst,
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000785 const uint32_t notificationTimeMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000786{
niklase@google.com470e71d2011-07-07 08:21:25 +0000787 // Check codec info
788 if(!ValidFileFormat(format,&codecInst))
789 {
790 return -1;
791 }
792
793 CriticalSectionScoped lock(_crit);
794 if(_recordingActive || _playingActive)
795 {
796 WEBRTC_TRACE(
797 kTraceError,
798 kTraceFile,
799 _id,
800 "StartRecording called, but already recording or playing file %s!",
801 _fileName);
802 return -1;
803 }
804
805 if(_ptrFileUtilityObj != NULL)
806 {
807 WEBRTC_TRACE(
808 kTraceError,
809 kTraceFile,
810 _id,
811 "StartRecording called, but fileUtilityObj already exists!");
812 StopRecording();
813 return -1;
814 }
815
816 _ptrFileUtilityObj = new ModuleFileUtility(_id);
817 if(_ptrFileUtilityObj == NULL)
818 {
819 WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
820 "Cannot allocate fileUtilityObj!");
821 return -1;
822 }
823
824 CodecInst tmpAudioCodec;
825 memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst));
826 switch(format)
827 {
828 case kFileFormatWavFile:
829 {
830 if(_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1)
831 {
832 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
833 "Failed to initialize WAV file!");
834 delete _ptrFileUtilityObj;
835 _ptrFileUtilityObj = NULL;
836 return -1;
837 }
838 _fileFormat = kFileFormatWavFile;
839 break;
840 }
841 case kFileFormatCompressedFile:
842 {
843 // Write compression codec name at beginning of file
844 if(_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) ==
845 -1)
846 {
847 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
848 "Failed to initialize Compressed file!");
849 delete _ptrFileUtilityObj;
850 _ptrFileUtilityObj = NULL;
851 return -1;
852 }
853 _fileFormat = kFileFormatCompressedFile;
854 break;
855 }
856 case kFileFormatPcm8kHzFile:
857 case kFileFormatPcm16kHzFile:
858 {
859 if(!ValidFrequency(codecInst.plfreq) ||
860 _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) ==
861 -1)
862 {
863 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
864 "Failed to initialize 8 or 16KHz PCM file!");
865 delete _ptrFileUtilityObj;
866 _ptrFileUtilityObj = NULL;
867 return -1;
868 }
869 _fileFormat = format;
870 break;
871 }
872 case kFileFormatPreencodedFile:
873 {
874 if(_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) ==
875 -1)
876 {
877 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
878 "Failed to initialize Pre-Encoded file!");
879 delete _ptrFileUtilityObj;
880 _ptrFileUtilityObj = NULL;
881 return -1;
882 }
883
884 _fileFormat = kFileFormatPreencodedFile;
885 break;
886 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000887 default:
888 {
889 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
890 "Invalid file format %d specified!", format);
891 delete _ptrFileUtilityObj;
892 _ptrFileUtilityObj = NULL;
893 return -1;
894 }
895 }
896 _isStereo = (tmpAudioCodec.channels == 2);
897 if(_isStereo)
898 {
899 if(_fileFormat != kFileFormatWavFile)
900 {
901 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
902 "Stereo is only allowed for WAV files");
903 StopRecording();
904 return -1;
905 }
niklas.enbom@webrtc.org87885e82012-02-07 14:48:59 +0000906 if((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) &&
907 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) &&
908 (STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0))
niklase@google.com470e71d2011-07-07 08:21:25 +0000909 {
910 WEBRTC_TRACE(
911 kTraceWarning,
912 kTraceFile,
913 _id,
914 "Stereo is only allowed for codec PCMU, PCMA and L16 ");
915 StopRecording();
916 return -1;
917 }
918 }
919 memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst));
920 _recordingActive = true;
921 _ptrOutStream = &stream;
922 _notificationMs = notificationTimeMs;
923 _recordDurationMs = 0;
924 return 0;
925}
926
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000927int32_t MediaFileImpl::StopRecording()
niklase@google.com470e71d2011-07-07 08:21:25 +0000928{
niklase@google.com470e71d2011-07-07 08:21:25 +0000929
930 CriticalSectionScoped lock(_crit);
931 if(!_recordingActive)
932 {
933 WEBRTC_TRACE(kTraceWarning, kTraceFile, _id,
934 "recording is not active!");
935 return -1;
936 }
937
938 _isStereo = false;
939
940 if(_ptrFileUtilityObj != NULL)
941 {
942 // Both AVI and WAV header has to be updated before closing the stream
943 // because they contain size information.
944 if((_fileFormat == kFileFormatWavFile) &&
945 (_ptrOutStream != NULL))
946 {
947 _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream);
948 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000949 delete _ptrFileUtilityObj;
950 _ptrFileUtilityObj = NULL;
951 }
952
953 if(_ptrOutStream != NULL)
954 {
955 // If MediaFileImpl opened the OutStream it must be reclaimed here.
956 if(_openFile)
957 {
958 delete _ptrOutStream;
959 _openFile = false;
960 }
961 _ptrOutStream = NULL;
962 }
963
964 _recordingActive = false;
965 codec_info_.pltype = 0;
966 codec_info_.plname[0] = '\0';
967
968 return 0;
969}
970
971bool MediaFileImpl::IsRecording()
972{
973 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsRecording()");
974 CriticalSectionScoped lock(_crit);
975 return _recordingActive;
976}
977
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000978int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +0000979{
niklase@google.com470e71d2011-07-07 08:21:25 +0000980
981 CriticalSectionScoped lock(_crit);
982 if(!_recordingActive)
983 {
984 durationMs = 0;
985 return -1;
986 }
987 durationMs = _recordDurationMs;
988 return 0;
989}
990
991bool MediaFileImpl::IsStereo()
992{
993 WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsStereo()");
994 CriticalSectionScoped lock(_crit);
995 return _isStereo;
996}
997
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +0000998int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback)
niklase@google.com470e71d2011-07-07 08:21:25 +0000999{
niklase@google.com470e71d2011-07-07 08:21:25 +00001000
1001 CriticalSectionScoped lock(_callbackCrit);
1002
1003 _ptrCallback = callback;
1004 return 0;
1005}
1006
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001007int32_t MediaFileImpl::FileDurationMs(const char* fileName,
1008 uint32_t& durationMs,
1009 const FileFormats format,
1010 const uint32_t freqInHz)
niklase@google.com470e71d2011-07-07 08:21:25 +00001011{
niklase@google.com470e71d2011-07-07 08:21:25 +00001012
1013 if(!ValidFileName(fileName))
1014 {
1015 return -1;
1016 }
1017 if(!ValidFrequency(freqInHz))
1018 {
1019 return -1;
1020 }
1021
1022 ModuleFileUtility* utilityObj = new ModuleFileUtility(_id);
1023 if(utilityObj == NULL)
1024 {
1025 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1026 "failed to allocate utility object!");
1027 return -1;
1028 }
1029
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001030 const int32_t duration = utilityObj->FileDurationMs(fileName, format,
1031 freqInHz);
niklase@google.com470e71d2011-07-07 08:21:25 +00001032 delete utilityObj;
1033 if(duration == -1)
1034 {
1035 durationMs = 0;
1036 return -1;
1037 }
1038
1039 durationMs = duration;
1040 return 0;
1041}
1042
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001043int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const
niklase@google.com470e71d2011-07-07 08:21:25 +00001044{
niklase@google.com470e71d2011-07-07 08:21:25 +00001045 CriticalSectionScoped lock(_crit);
1046 if(!_playingActive)
1047 {
1048 positionMs = 0;
1049 return -1;
1050 }
1051 positionMs = _playoutPositionMs;
1052 return 0;
1053}
1054
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001055int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const
niklase@google.com470e71d2011-07-07 08:21:25 +00001056{
niklase@google.com470e71d2011-07-07 08:21:25 +00001057 CriticalSectionScoped lock(_crit);
1058 if(!_playingActive && !_recordingActive)
1059 {
1060 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1061 "Neither playout nor recording has been initialized!");
1062 return -1;
1063 }
1064 if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0')
1065 {
1066 WEBRTC_TRACE(kTraceError, kTraceFile, _id,
1067 "The CodecInst for %s is unknown!",
1068 _playingActive ? "Playback" : "Recording");
1069 return -1;
1070 }
1071 memcpy(&codecInst,&codec_info_,sizeof(CodecInst));
1072 return 0;
1073}
1074
niklase@google.com470e71d2011-07-07 08:21:25 +00001075bool MediaFileImpl::ValidFileFormat(const FileFormats format,
1076 const CodecInst* codecInst)
1077{
1078 if(codecInst == NULL)
1079 {
1080 if(format == kFileFormatPreencodedFile ||
1081 format == kFileFormatPcm8kHzFile ||
1082 format == kFileFormatPcm16kHzFile ||
1083 format == kFileFormatPcm32kHzFile)
1084 {
1085 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1086 "Codec info required for file format specified!");
1087 return false;
1088 }
1089 }
1090 return true;
1091}
1092
leozwang@webrtc.org09e77192012-03-01 18:35:54 +00001093bool MediaFileImpl::ValidFileName(const char* fileName)
niklase@google.com470e71d2011-07-07 08:21:25 +00001094{
1095 if((fileName == NULL) ||(fileName[0] == '\0'))
1096 {
1097 WEBRTC_TRACE(kTraceError, kTraceFile, -1, "FileName not specified!");
1098 return false;
1099 }
1100 return true;
1101}
1102
1103
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001104bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs,
1105 const uint32_t stopPointMs)
niklase@google.com470e71d2011-07-07 08:21:25 +00001106{
1107 if(startPointMs == 0 && stopPointMs == 0) // Default values
1108 {
1109 return true;
1110 }
1111 if(stopPointMs &&(startPointMs >= stopPointMs))
1112 {
1113 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1114 "startPointMs must be less than stopPointMs!");
1115 return false;
1116 }
1117 if(stopPointMs &&((stopPointMs - startPointMs) < 20))
1118 {
1119 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1120 "minimum play duration for files is 20 ms!");
1121 return false;
1122 }
1123 return true;
1124}
1125
pbos@webrtc.org0ea11c12013-04-09 13:31:37 +00001126bool MediaFileImpl::ValidFrequency(const uint32_t frequency)
niklase@google.com470e71d2011-07-07 08:21:25 +00001127{
1128 if((frequency == 8000) || (frequency == 16000)|| (frequency == 32000))
1129 {
1130 return true;
1131 }
1132 WEBRTC_TRACE(kTraceError, kTraceFile, -1,
1133 "Frequency should be 8000, 16000 or 32000 (Hz)");
1134 return false;
1135}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +00001136} // namespace webrtc