aboutsummaryrefslogtreecommitdiff
path: root/webrtc/modules/media_file/source/media_file_utility.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webrtc/modules/media_file/source/media_file_utility.cc')
-rw-r--r--webrtc/modules/media_file/source/media_file_utility.cc1656
1 files changed, 1656 insertions, 0 deletions
diff --git a/webrtc/modules/media_file/source/media_file_utility.cc b/webrtc/modules/media_file/source/media_file_utility.cc
new file mode 100644
index 0000000000..61ae442d0e
--- /dev/null
+++ b/webrtc/modules/media_file/source/media_file_utility.cc
@@ -0,0 +1,1656 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/media_file/source/media_file_utility.h"
+
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <limits>
+
+#include "webrtc/base/format_macros.h"
+#include "webrtc/common_audio/wav_header.h"
+#include "webrtc/common_types.h"
+#include "webrtc/engine_configurations.h"
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/system_wrappers/include/file_wrapper.h"
+#include "webrtc/system_wrappers/include/trace.h"
+
+namespace {
+
+// First 16 bytes the WAVE header. ckID should be "RIFF", wave_ckID should be
+// "WAVE" and ckSize is the chunk size (4 + n)
+struct WAVE_RIFF_header
+{
+ int8_t ckID[4];
+ int32_t ckSize;
+ int8_t wave_ckID[4];
+};
+
+// First 8 byte of the format chunk. fmt_ckID should be "fmt ". fmt_ckSize is
+// the chunk size (16, 18 or 40 byte)
+struct WAVE_CHUNK_header
+{
+ int8_t fmt_ckID[4];
+ int32_t fmt_ckSize;
+};
+} // unnamed namespace
+
+namespace webrtc {
+ModuleFileUtility::ModuleFileUtility(const int32_t id)
+ : _wavFormatObj(),
+ _dataSize(0),
+ _readSizeBytes(0),
+ _id(id),
+ _stopPointInMs(0),
+ _startPointInMs(0),
+ _playoutPositionMs(0),
+ _bytesWritten(0),
+ codec_info_(),
+ _codecId(kCodecNoCodec),
+ _bytesPerSample(0),
+ _readPos(0),
+ _reading(false),
+ _writing(false),
+ _tempData() {
+ WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
+ "ModuleFileUtility::ModuleFileUtility()");
+ memset(&codec_info_,0,sizeof(CodecInst));
+ codec_info_.pltype = -1;
+}
+
+ModuleFileUtility::~ModuleFileUtility()
+{
+ WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
+ "ModuleFileUtility::~ModuleFileUtility()");
+}
+
+int32_t ModuleFileUtility::ReadWavHeader(InStream& wav)
+{
+ WAVE_RIFF_header RIFFheaderObj;
+ WAVE_CHUNK_header CHUNKheaderObj;
+ // TODO (hellner): tmpStr and tmpStr2 seems unnecessary here.
+ char tmpStr[6] = "FOUR";
+ unsigned char tmpStr2[4];
+ int32_t i, len;
+ bool dataFound = false;
+ bool fmtFound = false;
+ int8_t dummyRead;
+
+
+ _dataSize = 0;
+ len = wav.Read(&RIFFheaderObj, sizeof(WAVE_RIFF_header));
+ if(len != sizeof(WAVE_RIFF_header))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "Not a wave file (too short)");
+ return -1;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ tmpStr[i] = RIFFheaderObj.ckID[i];
+ }
+ if(strcmp(tmpStr, "RIFF") != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "Not a wave file (does not have RIFF)");
+ return -1;
+ }
+ for (i = 0; i < 4; i++)
+ {
+ tmpStr[i] = RIFFheaderObj.wave_ckID[i];
+ }
+ if(strcmp(tmpStr, "WAVE") != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "Not a wave file (does not have WAVE)");
+ return -1;
+ }
+
+ len = wav.Read(&CHUNKheaderObj, sizeof(WAVE_CHUNK_header));
+
+ // WAVE files are stored in little endian byte order. Make sure that the
+ // data can be read on big endian as well.
+ // TODO (hellner): little endian to system byte order should be done in
+ // in a subroutine.
+ memcpy(tmpStr2, &CHUNKheaderObj.fmt_ckSize, 4);
+ CHUNKheaderObj.fmt_ckSize =
+ (int32_t) ((uint32_t) tmpStr2[0] +
+ (((uint32_t)tmpStr2[1])<<8) +
+ (((uint32_t)tmpStr2[2])<<16) +
+ (((uint32_t)tmpStr2[3])<<24));
+
+ memcpy(tmpStr, CHUNKheaderObj.fmt_ckID, 4);
+
+ while ((len == sizeof(WAVE_CHUNK_header)) && (!fmtFound || !dataFound))
+ {
+ if(strcmp(tmpStr, "fmt ") == 0)
+ {
+ len = wav.Read(&_wavFormatObj, sizeof(WAVE_FMTINFO_header));
+
+ memcpy(tmpStr2, &_wavFormatObj.formatTag, 2);
+ _wavFormatObj.formatTag =
+ (uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1])<<8);
+ memcpy(tmpStr2, &_wavFormatObj.nChannels, 2);
+ _wavFormatObj.nChannels =
+ (int16_t) ((uint32_t)tmpStr2[0] +
+ (((uint32_t)tmpStr2[1])<<8));
+ memcpy(tmpStr2, &_wavFormatObj.nSamplesPerSec, 4);
+ _wavFormatObj.nSamplesPerSec =
+ (int32_t) ((uint32_t)tmpStr2[0] +
+ (((uint32_t)tmpStr2[1])<<8) +
+ (((uint32_t)tmpStr2[2])<<16) +
+ (((uint32_t)tmpStr2[3])<<24));
+ memcpy(tmpStr2, &_wavFormatObj.nAvgBytesPerSec, 4);
+ _wavFormatObj.nAvgBytesPerSec =
+ (int32_t) ((uint32_t)tmpStr2[0] +
+ (((uint32_t)tmpStr2[1])<<8) +
+ (((uint32_t)tmpStr2[2])<<16) +
+ (((uint32_t)tmpStr2[3])<<24));
+ memcpy(tmpStr2, &_wavFormatObj.nBlockAlign, 2);
+ _wavFormatObj.nBlockAlign =
+ (int16_t) ((uint32_t)tmpStr2[0] +
+ (((uint32_t)tmpStr2[1])<<8));
+ memcpy(tmpStr2, &_wavFormatObj.nBitsPerSample, 2);
+ _wavFormatObj.nBitsPerSample =
+ (int16_t) ((uint32_t)tmpStr2[0] +
+ (((uint32_t)tmpStr2[1])<<8));
+
+ for (i = 0;
+ i < (CHUNKheaderObj.fmt_ckSize -
+ (int32_t)sizeof(WAVE_FMTINFO_header));
+ i++)
+ {
+ len = wav.Read(&dummyRead, 1);
+ if(len != 1)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "File corrupted, reached EOF (reading fmt)");
+ return -1;
+ }
+ }
+ fmtFound = true;
+ }
+ else if(strcmp(tmpStr, "data") == 0)
+ {
+ _dataSize = CHUNKheaderObj.fmt_ckSize;
+ dataFound = true;
+ break;
+ }
+ else
+ {
+ for (i = 0; i < (CHUNKheaderObj.fmt_ckSize); i++)
+ {
+ len = wav.Read(&dummyRead, 1);
+ if(len != 1)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "File corrupted, reached EOF (reading other)");
+ return -1;
+ }
+ }
+ }
+
+ len = wav.Read(&CHUNKheaderObj, sizeof(WAVE_CHUNK_header));
+
+ memcpy(tmpStr2, &CHUNKheaderObj.fmt_ckSize, 4);
+ CHUNKheaderObj.fmt_ckSize =
+ (int32_t) ((uint32_t)tmpStr2[0] +
+ (((uint32_t)tmpStr2[1])<<8) +
+ (((uint32_t)tmpStr2[2])<<16) +
+ (((uint32_t)tmpStr2[3])<<24));
+
+ memcpy(tmpStr, CHUNKheaderObj.fmt_ckID, 4);
+ }
+
+ // Either a proper format chunk has been read or a data chunk was come
+ // across.
+ if( (_wavFormatObj.formatTag != kWavFormatPcm) &&
+ (_wavFormatObj.formatTag != kWavFormatALaw) &&
+ (_wavFormatObj.formatTag != kWavFormatMuLaw))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "Coding formatTag value=%d not supported!",
+ _wavFormatObj.formatTag);
+ return -1;
+ }
+ if((_wavFormatObj.nChannels < 1) ||
+ (_wavFormatObj.nChannels > 2))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "nChannels value=%d not supported!",
+ _wavFormatObj.nChannels);
+ return -1;
+ }
+
+ if((_wavFormatObj.nBitsPerSample != 8) &&
+ (_wavFormatObj.nBitsPerSample != 16))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "nBitsPerSample value=%d not supported!",
+ _wavFormatObj.nBitsPerSample);
+ return -1;
+ }
+
+ // Calculate the number of bytes that 10 ms of audio data correspond to.
+ if(_wavFormatObj.formatTag == kWavFormatPcm)
+ {
+ // TODO (hellner): integer division for 22050 and 11025 would yield
+ // the same result as the else statement. Remove those
+ // special cases?
+ if(_wavFormatObj.nSamplesPerSec == 44100)
+ {
+ _readSizeBytes = 440 * _wavFormatObj.nChannels *
+ (_wavFormatObj.nBitsPerSample / 8);
+ } else if(_wavFormatObj.nSamplesPerSec == 22050) {
+ _readSizeBytes = 220 * _wavFormatObj.nChannels *
+ (_wavFormatObj.nBitsPerSample / 8);
+ } else if(_wavFormatObj.nSamplesPerSec == 11025) {
+ _readSizeBytes = 110 * _wavFormatObj.nChannels *
+ (_wavFormatObj.nBitsPerSample / 8);
+ } else {
+ _readSizeBytes = (_wavFormatObj.nSamplesPerSec/100) *
+ _wavFormatObj.nChannels * (_wavFormatObj.nBitsPerSample / 8);
+ }
+
+ } else {
+ _readSizeBytes = (_wavFormatObj.nSamplesPerSec/100) *
+ _wavFormatObj.nChannels * (_wavFormatObj.nBitsPerSample / 8);
+ }
+ return 0;
+}
+
+int32_t ModuleFileUtility::InitWavCodec(uint32_t samplesPerSec,
+ uint32_t channels,
+ uint32_t bitsPerSample,
+ uint32_t formatTag)
+{
+ codec_info_.pltype = -1;
+ codec_info_.plfreq = samplesPerSec;
+ codec_info_.channels = channels;
+ codec_info_.rate = bitsPerSample * samplesPerSec;
+
+ // Calculate the packet size for 10ms frames
+ switch(formatTag)
+ {
+ case kWavFormatALaw:
+ strcpy(codec_info_.plname, "PCMA");
+ _codecId = kCodecPcma;
+ codec_info_.pltype = 8;
+ codec_info_.pacsize = codec_info_.plfreq / 100;
+ break;
+ case kWavFormatMuLaw:
+ strcpy(codec_info_.plname, "PCMU");
+ _codecId = kCodecPcmu;
+ codec_info_.pltype = 0;
+ codec_info_.pacsize = codec_info_.plfreq / 100;
+ break;
+ case kWavFormatPcm:
+ codec_info_.pacsize = (bitsPerSample * (codec_info_.plfreq / 100)) / 8;
+ if(samplesPerSec == 8000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ _codecId = kCodecL16_8Khz;
+ }
+ else if(samplesPerSec == 16000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ _codecId = kCodecL16_16kHz;
+ }
+ else if(samplesPerSec == 32000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ _codecId = kCodecL16_32Khz;
+ }
+ // Set the packet size for "odd" sampling frequencies so that it
+ // properly corresponds to _readSizeBytes.
+ else if(samplesPerSec == 11025)
+ {
+ strcpy(codec_info_.plname, "L16");
+ _codecId = kCodecL16_16kHz;
+ codec_info_.pacsize = 110;
+ codec_info_.plfreq = 11000;
+ }
+ else if(samplesPerSec == 22050)
+ {
+ strcpy(codec_info_.plname, "L16");
+ _codecId = kCodecL16_16kHz;
+ codec_info_.pacsize = 220;
+ codec_info_.plfreq = 22000;
+ }
+ else if(samplesPerSec == 44100)
+ {
+ strcpy(codec_info_.plname, "L16");
+ _codecId = kCodecL16_16kHz;
+ codec_info_.pacsize = 440;
+ codec_info_.plfreq = 44000;
+ }
+ else if(samplesPerSec == 48000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ _codecId = kCodecL16_16kHz;
+ codec_info_.pacsize = 480;
+ codec_info_.plfreq = 48000;
+ }
+ else
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "Unsupported PCM frequency!");
+ return -1;
+ }
+ break;
+ default:
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "unknown WAV format TAG!");
+ return -1;
+ break;
+ }
+ return 0;
+}
+
+int32_t ModuleFileUtility::InitWavReading(InStream& wav,
+ const uint32_t start,
+ const uint32_t stop)
+{
+
+ _reading = false;
+
+ if(ReadWavHeader(wav) == -1)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "failed to read WAV header!");
+ return -1;
+ }
+
+ _playoutPositionMs = 0;
+ _readPos = 0;
+
+ if(start > 0)
+ {
+ uint8_t dummy[WAV_MAX_BUFFER_SIZE];
+ int32_t readLength;
+ if(_readSizeBytes <= WAV_MAX_BUFFER_SIZE)
+ {
+ while (_playoutPositionMs < start)
+ {
+ readLength = wav.Read(dummy, _readSizeBytes);
+ if(readLength == _readSizeBytes)
+ {
+ _readPos += readLength;
+ _playoutPositionMs += 10;
+ }
+ else // Must have reached EOF before start position!
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "InitWavReading(), EOF before start position");
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ if( InitWavCodec(_wavFormatObj.nSamplesPerSec, _wavFormatObj.nChannels,
+ _wavFormatObj.nBitsPerSample,
+ _wavFormatObj.formatTag) != 0)
+ {
+ return -1;
+ }
+ _bytesPerSample = _wavFormatObj.nBitsPerSample / 8;
+
+
+ _startPointInMs = start;
+ _stopPointInMs = stop;
+ _reading = true;
+ return 0;
+}
+
+int32_t ModuleFileUtility::ReadWavDataAsMono(
+ InStream& wav,
+ int8_t* outData,
+ const size_t bufferSize)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::ReadWavDataAsMono(wav= 0x%x, outData= 0x%d, "
+ "bufSize= %" PRIuS ")",
+ &wav,
+ outData,
+ bufferSize);
+
+ // The number of bytes that should be read from file.
+ const uint32_t totalBytesNeeded = _readSizeBytes;
+ // The number of bytes that will be written to outData.
+ const uint32_t bytesRequested = (codec_info_.channels == 2) ?
+ totalBytesNeeded >> 1 : totalBytesNeeded;
+ if(bufferSize < bytesRequested)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavDataAsMono: output buffer is too short!");
+ return -1;
+ }
+ if(outData == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavDataAsMono: output buffer NULL!");
+ return -1;
+ }
+
+ if(!_reading)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavDataAsMono: no longer reading file.");
+ return -1;
+ }
+
+ int32_t bytesRead = ReadWavData(
+ wav,
+ (codec_info_.channels == 2) ? _tempData : (uint8_t*)outData,
+ totalBytesNeeded);
+ if(bytesRead == 0)
+ {
+ return 0;
+ }
+ if(bytesRead < 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavDataAsMono: failed to read data from WAV file.");
+ return -1;
+ }
+ // Output data is should be mono.
+ if(codec_info_.channels == 2)
+ {
+ for (uint32_t i = 0; i < bytesRequested / _bytesPerSample; i++)
+ {
+ // Sample value is the average of left and right buffer rounded to
+ // closest integer value. Note samples can be either 1 or 2 byte.
+ if(_bytesPerSample == 1)
+ {
+ _tempData[i] = ((_tempData[2 * i] + _tempData[(2 * i) + 1] +
+ 1) >> 1);
+ }
+ else
+ {
+ int16_t* sampleData = (int16_t*) _tempData;
+ sampleData[i] = ((sampleData[2 * i] + sampleData[(2 * i) + 1] +
+ 1) >> 1);
+ }
+ }
+ memcpy(outData, _tempData, bytesRequested);
+ }
+ return bytesRequested;
+}
+
+int32_t ModuleFileUtility::ReadWavDataAsStereo(
+ InStream& wav,
+ int8_t* outDataLeft,
+ int8_t* outDataRight,
+ const size_t bufferSize)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::ReadWavDataAsStereo(wav= 0x%x, outLeft= 0x%x, "
+ "outRight= 0x%x, bufSize= %" PRIuS ")",
+ &wav,
+ outDataLeft,
+ outDataRight,
+ bufferSize);
+
+ if((outDataLeft == NULL) ||
+ (outDataRight == NULL))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavDataAsMono: an input buffer is NULL!");
+ return -1;
+ }
+ if(codec_info_.channels != 2)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceFile,
+ _id,
+ "ReadWavDataAsStereo: WAV file does not contain stereo data!");
+ return -1;
+ }
+ if(! _reading)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavDataAsStereo: no longer reading file.");
+ return -1;
+ }
+
+ // The number of bytes that should be read from file.
+ const uint32_t totalBytesNeeded = _readSizeBytes;
+ // The number of bytes that will be written to the left and the right
+ // buffers.
+ const uint32_t bytesRequested = totalBytesNeeded >> 1;
+ if(bufferSize < bytesRequested)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavData: Output buffers are too short!");
+ assert(false);
+ return -1;
+ }
+
+ int32_t bytesRead = ReadWavData(wav, _tempData, totalBytesNeeded);
+ if(bytesRead <= 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavDataAsStereo: failed to read data from WAV file.");
+ return -1;
+ }
+
+ // Turn interleaved audio to left and right buffer. Note samples can be
+ // either 1 or 2 bytes
+ if(_bytesPerSample == 1)
+ {
+ for (uint32_t i = 0; i < bytesRequested; i++)
+ {
+ outDataLeft[i] = _tempData[2 * i];
+ outDataRight[i] = _tempData[(2 * i) + 1];
+ }
+ }
+ else if(_bytesPerSample == 2)
+ {
+ int16_t* sampleData = reinterpret_cast<int16_t*>(_tempData);
+ int16_t* outLeft = reinterpret_cast<int16_t*>(outDataLeft);
+ int16_t* outRight = reinterpret_cast<int16_t*>(
+ outDataRight);
+
+ // Bytes requested to samples requested.
+ uint32_t sampleCount = bytesRequested >> 1;
+ for (uint32_t i = 0; i < sampleCount; i++)
+ {
+ outLeft[i] = sampleData[2 * i];
+ outRight[i] = sampleData[(2 * i) + 1];
+ }
+ } else {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavStereoData: unsupported sample size %d!",
+ _bytesPerSample);
+ assert(false);
+ return -1;
+ }
+ return bytesRequested;
+}
+
+int32_t ModuleFileUtility::ReadWavData(
+ InStream& wav,
+ uint8_t* buffer,
+ const uint32_t dataLengthInBytes)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::ReadWavData(wav= 0x%x, buffer= 0x%x, dataLen= %ld)",
+ &wav,
+ buffer,
+ dataLengthInBytes);
+
+
+ if(buffer == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadWavDataAsMono: output buffer NULL!");
+ return -1;
+ }
+
+ // Make sure that a read won't return too few samples.
+ // TODO (hellner): why not read the remaining bytes needed from the start
+ // of the file?
+ if((_dataSize - _readPos) < (int32_t)dataLengthInBytes)
+ {
+ // Rewind() being -1 may be due to the file not supposed to be looped.
+ if(wav.Rewind() == -1)
+ {
+ _reading = false;
+ return 0;
+ }
+ if(InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1)
+ {
+ _reading = false;
+ return -1;
+ }
+ }
+
+ int32_t bytesRead = wav.Read(buffer, dataLengthInBytes);
+ if(bytesRead < 0)
+ {
+ _reading = false;
+ return -1;
+ }
+
+ // This should never happen due to earlier sanity checks.
+ // TODO (hellner): change to an assert and fail here since this should
+ // never happen...
+ if(bytesRead < (int32_t)dataLengthInBytes)
+ {
+ if((wav.Rewind() == -1) ||
+ (InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1))
+ {
+ _reading = false;
+ return -1;
+ }
+ else
+ {
+ bytesRead = wav.Read(buffer, dataLengthInBytes);
+ if(bytesRead < (int32_t)dataLengthInBytes)
+ {
+ _reading = false;
+ return -1;
+ }
+ }
+ }
+
+ _readPos += bytesRead;
+
+ // TODO (hellner): Why is dataLengthInBytes let dictate the number of bytes
+ // to read when exactly 10ms should be read?!
+ _playoutPositionMs += 10;
+ if((_stopPointInMs > 0) &&
+ (_playoutPositionMs >= _stopPointInMs))
+ {
+ if((wav.Rewind() == -1) ||
+ (InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1))
+ {
+ _reading = false;
+ }
+ }
+ return bytesRead;
+}
+
+int32_t ModuleFileUtility::InitWavWriting(OutStream& wav,
+ const CodecInst& codecInst)
+{
+
+ if(set_codec_info(codecInst) != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "codecInst identifies unsupported codec!");
+ return -1;
+ }
+ _writing = false;
+ uint32_t channels = (codecInst.channels == 0) ?
+ 1 : codecInst.channels;
+
+ if(STR_CASE_CMP(codecInst.plname, "PCMU") == 0)
+ {
+ _bytesPerSample = 1;
+ if(WriteWavHeader(wav, 8000, _bytesPerSample, channels,
+ kWavFormatMuLaw, 0) == -1)
+ {
+ return -1;
+ }
+ }else if(STR_CASE_CMP(codecInst.plname, "PCMA") == 0)
+ {
+ _bytesPerSample = 1;
+ if(WriteWavHeader(wav, 8000, _bytesPerSample, channels, kWavFormatALaw,
+ 0) == -1)
+ {
+ return -1;
+ }
+ }
+ else if(STR_CASE_CMP(codecInst.plname, "L16") == 0)
+ {
+ _bytesPerSample = 2;
+ if(WriteWavHeader(wav, codecInst.plfreq, _bytesPerSample, channels,
+ kWavFormatPcm, 0) == -1)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "codecInst identifies unsupported codec for WAV file!");
+ return -1;
+ }
+ _writing = true;
+ _bytesWritten = 0;
+ return 0;
+}
+
+int32_t ModuleFileUtility::WriteWavData(OutStream& out,
+ const int8_t* buffer,
+ const size_t dataLength)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::WriteWavData(out= 0x%x, buf= 0x%x, dataLen= %" PRIuS
+ ")",
+ &out,
+ buffer,
+ dataLength);
+
+ if(buffer == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "WriteWavData: input buffer NULL!");
+ return -1;
+ }
+
+ if(!out.Write(buffer, dataLength))
+ {
+ return -1;
+ }
+ _bytesWritten += dataLength;
+ return static_cast<int32_t>(dataLength);
+}
+
+
+int32_t ModuleFileUtility::WriteWavHeader(
+ OutStream& wav,
+ const uint32_t freq,
+ const uint32_t bytesPerSample,
+ const uint32_t channels,
+ const uint32_t format,
+ const uint32_t lengthInBytes)
+{
+ // Frame size in bytes for 10 ms of audio.
+ // TODO (hellner): 44.1 kHz has 440 samples frame size. Doesn't seem to
+ // be taken into consideration here!
+ const int32_t frameSize = (freq / 100) * channels;
+
+ // Calculate the number of full frames that the wave file contain.
+ const int32_t dataLengthInBytes = frameSize * (lengthInBytes / frameSize);
+
+ uint8_t buf[kWavHeaderSize];
+ webrtc::WriteWavHeader(buf, channels, freq, static_cast<WavFormat>(format),
+ bytesPerSample, dataLengthInBytes / bytesPerSample);
+ wav.Write(buf, kWavHeaderSize);
+ return 0;
+}
+
+int32_t ModuleFileUtility::UpdateWavHeader(OutStream& wav)
+{
+ int32_t res = -1;
+ if(wav.Rewind() == -1)
+ {
+ return -1;
+ }
+ uint32_t channels = (codec_info_.channels == 0) ?
+ 1 : codec_info_.channels;
+
+ if(STR_CASE_CMP(codec_info_.plname, "L16") == 0)
+ {
+ res = WriteWavHeader(wav, codec_info_.plfreq, 2, channels,
+ kWavFormatPcm, _bytesWritten);
+ } else if(STR_CASE_CMP(codec_info_.plname, "PCMU") == 0) {
+ res = WriteWavHeader(wav, 8000, 1, channels, kWavFormatMuLaw,
+ _bytesWritten);
+ } else if(STR_CASE_CMP(codec_info_.plname, "PCMA") == 0) {
+ res = WriteWavHeader(wav, 8000, 1, channels, kWavFormatALaw,
+ _bytesWritten);
+ } else {
+ // Allow calling this API even if not writing to a WAVE file.
+ // TODO (hellner): why?!
+ return 0;
+ }
+ return res;
+}
+
+
+int32_t ModuleFileUtility::InitPreEncodedReading(InStream& in,
+ const CodecInst& cinst)
+{
+
+ uint8_t preEncodedID;
+ in.Read(&preEncodedID, 1);
+
+ MediaFileUtility_CodecType codecType =
+ (MediaFileUtility_CodecType)preEncodedID;
+
+ if(set_codec_info(cinst) != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "Pre-encoded file send codec mismatch!");
+ return -1;
+ }
+ if(codecType != _codecId)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "Pre-encoded file format codec mismatch!");
+ return -1;
+ }
+ memcpy(&codec_info_,&cinst,sizeof(CodecInst));
+ _reading = true;
+ return 0;
+}
+
+int32_t ModuleFileUtility::ReadPreEncodedData(
+ InStream& in,
+ int8_t* outData,
+ const size_t bufferSize)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::ReadPreEncodedData(in= 0x%x, outData= 0x%x, "
+ "bufferSize= %" PRIuS ")",
+ &in,
+ outData,
+ bufferSize);
+
+ if(outData == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id, "output buffer NULL");
+ }
+
+ uint32_t frameLen;
+ uint8_t buf[64];
+ // Each frame has a two byte header containing the frame length.
+ int32_t res = in.Read(buf, 2);
+ if(res != 2)
+ {
+ if(!in.Rewind())
+ {
+ // The first byte is the codec identifier.
+ in.Read(buf, 1);
+ res = in.Read(buf, 2);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ frameLen = buf[0] + buf[1] * 256;
+ if(bufferSize < frameLen)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceFile,
+ _id,
+ "buffer not large enough to read %d bytes of pre-encoded data!",
+ frameLen);
+ return -1;
+ }
+ return in.Read(outData, frameLen);
+}
+
+int32_t ModuleFileUtility::InitPreEncodedWriting(
+ OutStream& out,
+ const CodecInst& codecInst)
+{
+
+ if(set_codec_info(codecInst) != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id, "CodecInst not recognized!");
+ return -1;
+ }
+ _writing = true;
+ _bytesWritten = 1;
+ out.Write(&_codecId, 1);
+ return 0;
+}
+
+int32_t ModuleFileUtility::WritePreEncodedData(
+ OutStream& out,
+ const int8_t* buffer,
+ const size_t dataLength)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::WritePreEncodedData(out= 0x%x, inData= 0x%x, "
+ "dataLen= %" PRIuS ")",
+ &out,
+ buffer,
+ dataLength);
+
+ if(buffer == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,"buffer NULL");
+ }
+
+ size_t bytesWritten = 0;
+ // The first two bytes is the size of the frame.
+ int16_t lengthBuf;
+ lengthBuf = (int16_t)dataLength;
+ if(dataLength > static_cast<size_t>(std::numeric_limits<int16_t>::max()) ||
+ !out.Write(&lengthBuf, 2))
+ {
+ return -1;
+ }
+ bytesWritten = 2;
+
+ if(!out.Write(buffer, dataLength))
+ {
+ return -1;
+ }
+ bytesWritten += dataLength;
+ return static_cast<int32_t>(bytesWritten);
+}
+
+int32_t ModuleFileUtility::InitCompressedReading(
+ InStream& in,
+ const uint32_t start,
+ const uint32_t stop)
+{
+ WEBRTC_TRACE(
+ kTraceDebug,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::InitCompressedReading(in= 0x%x, start= %d,\
+ stop= %d)",
+ &in,
+ start,
+ stop);
+
+#if defined(WEBRTC_CODEC_ILBC)
+ int16_t read_len = 0;
+#endif
+ _codecId = kCodecNoCodec;
+ _playoutPositionMs = 0;
+ _reading = false;
+
+ _startPointInMs = start;
+ _stopPointInMs = stop;
+
+ // Read the codec name
+ int32_t cnt = 0;
+ char buf[64];
+ do
+ {
+ in.Read(&buf[cnt++], 1);
+ } while ((buf[cnt-1] != '\n') && (64 > cnt));
+
+ if(cnt==64)
+ {
+ return -1;
+ } else {
+ buf[cnt]=0;
+ }
+
+#ifdef WEBRTC_CODEC_ILBC
+ if(!strcmp("#!iLBC20\n", buf))
+ {
+ codec_info_.pltype = 102;
+ strcpy(codec_info_.plname, "ilbc");
+ codec_info_.plfreq = 8000;
+ codec_info_.pacsize = 160;
+ codec_info_.channels = 1;
+ codec_info_.rate = 13300;
+ _codecId = kCodecIlbc20Ms;
+
+ if(_startPointInMs > 0)
+ {
+ while (_playoutPositionMs <= _startPointInMs)
+ {
+ read_len = in.Read(buf, 38);
+ if(read_len == 38)
+ {
+ _playoutPositionMs += 20;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+ }
+
+ if(!strcmp("#!iLBC30\n", buf))
+ {
+ codec_info_.pltype = 102;
+ strcpy(codec_info_.plname, "ilbc");
+ codec_info_.plfreq = 8000;
+ codec_info_.pacsize = 240;
+ codec_info_.channels = 1;
+ codec_info_.rate = 13300;
+ _codecId = kCodecIlbc30Ms;
+
+ if(_startPointInMs > 0)
+ {
+ while (_playoutPositionMs <= _startPointInMs)
+ {
+ read_len = in.Read(buf, 50);
+ if(read_len == 50)
+ {
+ _playoutPositionMs += 20;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+ }
+#endif
+ if(_codecId == kCodecNoCodec)
+ {
+ return -1;
+ }
+ _reading = true;
+ return 0;
+}
+
+int32_t ModuleFileUtility::ReadCompressedData(InStream& in,
+ int8_t* outData,
+ size_t bufferSize)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::ReadCompressedData(in=0x%x, outData=0x%x, bytes=%"
+ PRIuS ")",
+ &in,
+ outData,
+ bufferSize);
+
+ uint32_t bytesRead = 0;
+
+ if(! _reading)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id, "not currently reading!");
+ return -1;
+ }
+
+#ifdef WEBRTC_CODEC_ILBC
+ if((_codecId == kCodecIlbc20Ms) ||
+ (_codecId == kCodecIlbc30Ms))
+ {
+ uint32_t byteSize = 0;
+ if(_codecId == kCodecIlbc30Ms)
+ {
+ byteSize = 50;
+ }
+ if(_codecId == kCodecIlbc20Ms)
+ {
+ byteSize = 38;
+ }
+ if(bufferSize < byteSize)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "output buffer is too short to read ILBC compressed\
+ data.");
+ assert(false);
+ return -1;
+ }
+
+ bytesRead = in.Read(outData, byteSize);
+ if(bytesRead != byteSize)
+ {
+ if(!in.Rewind())
+ {
+ InitCompressedReading(in, _startPointInMs, _stopPointInMs);
+ bytesRead = in.Read(outData, byteSize);
+ if(bytesRead != byteSize)
+ {
+ _reading = false;
+ return -1;
+ }
+ }
+ else
+ {
+ _reading = false;
+ return -1;
+ }
+ }
+ }
+#endif
+ if(bytesRead == 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadCompressedData() no bytes read, codec not supported");
+ return -1;
+ }
+
+ _playoutPositionMs += 20;
+ if((_stopPointInMs > 0) &&
+ (_playoutPositionMs >= _stopPointInMs))
+ {
+ if(!in.Rewind())
+ {
+ InitCompressedReading(in, _startPointInMs, _stopPointInMs);
+ }
+ else
+ {
+ _reading = false;
+ }
+ }
+ return bytesRead;
+}
+
+int32_t ModuleFileUtility::InitCompressedWriting(
+ OutStream& out,
+ const CodecInst& codecInst)
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceFile, _id,
+ "ModuleFileUtility::InitCompressedWriting(out= 0x%x,\
+ codecName= %s)",
+ &out, codecInst.plname);
+
+ _writing = false;
+
+#ifdef WEBRTC_CODEC_ILBC
+ if(STR_CASE_CMP(codecInst.plname, "ilbc") == 0)
+ {
+ if(codecInst.pacsize == 160)
+ {
+ _codecId = kCodecIlbc20Ms;
+ out.Write("#!iLBC20\n",9);
+ }
+ else if(codecInst.pacsize == 240)
+ {
+ _codecId = kCodecIlbc30Ms;
+ out.Write("#!iLBC30\n",9);
+ }
+ else
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "codecInst defines unsupported compression codec!");
+ return -1;
+ }
+ memcpy(&codec_info_,&codecInst,sizeof(CodecInst));
+ _writing = true;
+ return 0;
+ }
+#endif
+
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "codecInst defines unsupported compression codec!");
+ return -1;
+}
+
+int32_t ModuleFileUtility::WriteCompressedData(
+ OutStream& out,
+ const int8_t* buffer,
+ const size_t dataLength)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::WriteCompressedData(out= 0x%x, buf= 0x%x, "
+ "dataLen= %" PRIuS ")",
+ &out,
+ buffer,
+ dataLength);
+
+ if(buffer == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,"buffer NULL");
+ }
+
+ if(!out.Write(buffer, dataLength))
+ {
+ return -1;
+ }
+ return static_cast<int32_t>(dataLength);
+}
+
+int32_t ModuleFileUtility::InitPCMReading(InStream& pcm,
+ const uint32_t start,
+ const uint32_t stop,
+ uint32_t freq)
+{
+ WEBRTC_TRACE(
+ kTraceInfo,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::InitPCMReading(pcm= 0x%x, start=%d, stop=%d,\
+ freq=%d)",
+ &pcm,
+ start,
+ stop,
+ freq);
+
+ int8_t dummy[320];
+ int32_t read_len;
+
+ _playoutPositionMs = 0;
+ _startPointInMs = start;
+ _stopPointInMs = stop;
+ _reading = false;
+
+ if(freq == 8000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ codec_info_.pltype = -1;
+ codec_info_.plfreq = 8000;
+ codec_info_.pacsize = 160;
+ codec_info_.channels = 1;
+ codec_info_.rate = 128000;
+ _codecId = kCodecL16_8Khz;
+ }
+ else if(freq == 16000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ codec_info_.pltype = -1;
+ codec_info_.plfreq = 16000;
+ codec_info_.pacsize = 320;
+ codec_info_.channels = 1;
+ codec_info_.rate = 256000;
+ _codecId = kCodecL16_16kHz;
+ }
+ else if(freq == 32000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ codec_info_.pltype = -1;
+ codec_info_.plfreq = 32000;
+ codec_info_.pacsize = 320;
+ codec_info_.channels = 1;
+ codec_info_.rate = 512000;
+ _codecId = kCodecL16_32Khz;
+ }
+
+ // Readsize for 10ms of audio data (2 bytes per sample).
+ _readSizeBytes = 2 * codec_info_. plfreq / 100;
+ if(_startPointInMs > 0)
+ {
+ while (_playoutPositionMs < _startPointInMs)
+ {
+ read_len = pcm.Read(dummy, _readSizeBytes);
+ if(read_len == _readSizeBytes)
+ {
+ _playoutPositionMs += 10;
+ }
+ else // Must have reached EOF before start position!
+ {
+ return -1;
+ }
+ }
+ }
+ _reading = true;
+ return 0;
+}
+
+int32_t ModuleFileUtility::ReadPCMData(InStream& pcm,
+ int8_t* outData,
+ size_t bufferSize)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::ReadPCMData(pcm= 0x%x, outData= 0x%x, bufSize= %"
+ PRIuS ")",
+ &pcm,
+ outData,
+ bufferSize);
+
+ if(outData == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,"buffer NULL");
+ }
+
+ // Readsize for 10ms of audio data (2 bytes per sample).
+ uint32_t bytesRequested = 2 * codec_info_.plfreq / 100;
+ if(bufferSize < bytesRequested)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadPCMData: buffer not long enough for a 10ms frame.");
+ assert(false);
+ return -1;
+ }
+
+ uint32_t bytesRead = pcm.Read(outData, bytesRequested);
+ if(bytesRead < bytesRequested)
+ {
+ if(pcm.Rewind() == -1)
+ {
+ _reading = false;
+ }
+ else
+ {
+ if(InitPCMReading(pcm, _startPointInMs, _stopPointInMs,
+ codec_info_.plfreq) == -1)
+ {
+ _reading = false;
+ }
+ else
+ {
+ int32_t rest = bytesRequested - bytesRead;
+ int32_t len = pcm.Read(&(outData[bytesRead]), rest);
+ if(len == rest)
+ {
+ bytesRead += len;
+ }
+ else
+ {
+ _reading = false;
+ }
+ }
+ if(bytesRead <= 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "ReadPCMData: Failed to rewind audio file.");
+ return -1;
+ }
+ }
+ }
+
+ if(bytesRead <= 0)
+ {
+ WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
+ "ReadPCMData: end of file");
+ return -1;
+ }
+ _playoutPositionMs += 10;
+ if(_stopPointInMs && _playoutPositionMs >= _stopPointInMs)
+ {
+ if(!pcm.Rewind())
+ {
+ if(InitPCMReading(pcm, _startPointInMs, _stopPointInMs,
+ codec_info_.plfreq) == -1)
+ {
+ _reading = false;
+ }
+ }
+ }
+ return bytesRead;
+}
+
+int32_t ModuleFileUtility::InitPCMWriting(OutStream& out, uint32_t freq)
+{
+
+ if(freq == 8000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ codec_info_.pltype = -1;
+ codec_info_.plfreq = 8000;
+ codec_info_.pacsize = 160;
+ codec_info_.channels = 1;
+ codec_info_.rate = 128000;
+
+ _codecId = kCodecL16_8Khz;
+ }
+ else if(freq == 16000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ codec_info_.pltype = -1;
+ codec_info_.plfreq = 16000;
+ codec_info_.pacsize = 320;
+ codec_info_.channels = 1;
+ codec_info_.rate = 256000;
+
+ _codecId = kCodecL16_16kHz;
+ }
+ else if(freq == 32000)
+ {
+ strcpy(codec_info_.plname, "L16");
+ codec_info_.pltype = -1;
+ codec_info_.plfreq = 32000;
+ codec_info_.pacsize = 320;
+ codec_info_.channels = 1;
+ codec_info_.rate = 512000;
+
+ _codecId = kCodecL16_32Khz;
+ }
+ if((_codecId != kCodecL16_8Khz) &&
+ (_codecId != kCodecL16_16kHz) &&
+ (_codecId != kCodecL16_32Khz))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "CodecInst is not 8KHz PCM or 16KHz PCM!");
+ return -1;
+ }
+ _writing = true;
+ _bytesWritten = 0;
+ return 0;
+}
+
+int32_t ModuleFileUtility::WritePCMData(OutStream& out,
+ const int8_t* buffer,
+ const size_t dataLength)
+{
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceFile,
+ _id,
+ "ModuleFileUtility::WritePCMData(out= 0x%x, buf= 0x%x, dataLen= %" PRIuS
+ ")",
+ &out,
+ buffer,
+ dataLength);
+
+ if(buffer == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id, "buffer NULL");
+ }
+
+ if(!out.Write(buffer, dataLength))
+ {
+ return -1;
+ }
+
+ _bytesWritten += dataLength;
+ return static_cast<int32_t>(dataLength);
+}
+
+int32_t ModuleFileUtility::codec_info(CodecInst& codecInst)
+{
+ WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
+ "ModuleFileUtility::codec_info(codecInst= 0x%x)", &codecInst);
+
+ if(!_reading && !_writing)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "CodecInst: not currently reading audio file!");
+ return -1;
+ }
+ memcpy(&codecInst,&codec_info_,sizeof(CodecInst));
+ return 0;
+}
+
+int32_t ModuleFileUtility::set_codec_info(const CodecInst& codecInst)
+{
+
+ _codecId = kCodecNoCodec;
+ if(STR_CASE_CMP(codecInst.plname, "PCMU") == 0)
+ {
+ _codecId = kCodecPcmu;
+ }
+ else if(STR_CASE_CMP(codecInst.plname, "PCMA") == 0)
+ {
+ _codecId = kCodecPcma;
+ }
+ else if(STR_CASE_CMP(codecInst.plname, "L16") == 0)
+ {
+ if(codecInst.plfreq == 8000)
+ {
+ _codecId = kCodecL16_8Khz;
+ }
+ else if(codecInst.plfreq == 16000)
+ {
+ _codecId = kCodecL16_16kHz;
+ }
+ else if(codecInst.plfreq == 32000)
+ {
+ _codecId = kCodecL16_32Khz;
+ }
+ }
+#ifdef WEBRTC_CODEC_ILBC
+ else if(STR_CASE_CMP(codecInst.plname, "ilbc") == 0)
+ {
+ if(codecInst.pacsize == 160)
+ {
+ _codecId = kCodecIlbc20Ms;
+ }
+ else if(codecInst.pacsize == 240)
+ {
+ _codecId = kCodecIlbc30Ms;
+ }
+ }
+#endif
+#if(defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX))
+ else if(STR_CASE_CMP(codecInst.plname, "isac") == 0)
+ {
+ if(codecInst.plfreq == 16000)
+ {
+ _codecId = kCodecIsac;
+ }
+ else if(codecInst.plfreq == 32000)
+ {
+ _codecId = kCodecIsacSwb;
+ }
+ }
+#endif
+#ifdef WEBRTC_CODEC_G722
+ else if(STR_CASE_CMP(codecInst.plname, "G722") == 0)
+ {
+ _codecId = kCodecG722;
+ }
+#endif
+ if(_codecId == kCodecNoCodec)
+ {
+ return -1;
+ }
+ memcpy(&codec_info_, &codecInst, sizeof(CodecInst));
+ return 0;
+}
+
+int32_t ModuleFileUtility::FileDurationMs(const char* fileName,
+ const FileFormats fileFormat,
+ const uint32_t freqInHz)
+{
+
+ if(fileName == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id, "filename NULL");
+ return -1;
+ }
+
+ int32_t time_in_ms = -1;
+ struct stat file_size;
+ if(stat(fileName,&file_size) == -1)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "failed to retrieve file size with stat!");
+ return -1;
+ }
+ FileWrapper* inStreamObj = FileWrapper::Create();
+ if(inStreamObj == NULL)
+ {
+ WEBRTC_TRACE(kTraceMemory, kTraceFile, _id,
+ "failed to create InStream object!");
+ return -1;
+ }
+ if(inStreamObj->OpenFile(fileName, true) == -1)
+ {
+ delete inStreamObj;
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "failed to open file %s!", fileName);
+ return -1;
+ }
+
+ switch (fileFormat)
+ {
+ case kFileFormatWavFile:
+ {
+ if(ReadWavHeader(*inStreamObj) == -1)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "failed to read WAV file header!");
+ return -1;
+ }
+ time_in_ms = ((file_size.st_size - 44) /
+ (_wavFormatObj.nAvgBytesPerSec/1000));
+ break;
+ }
+ case kFileFormatPcm16kHzFile:
+ {
+ // 16 samples per ms. 2 bytes per sample.
+ int32_t denominator = 16*2;
+ time_in_ms = (file_size.st_size)/denominator;
+ break;
+ }
+ case kFileFormatPcm8kHzFile:
+ {
+ // 8 samples per ms. 2 bytes per sample.
+ int32_t denominator = 8*2;
+ time_in_ms = (file_size.st_size)/denominator;
+ break;
+ }
+ case kFileFormatCompressedFile:
+ {
+ int32_t cnt = 0;
+ int32_t read_len = 0;
+ char buf[64];
+ do
+ {
+ read_len = inStreamObj->Read(&buf[cnt++], 1);
+ if(read_len != 1)
+ {
+ return -1;
+ }
+ } while ((buf[cnt-1] != '\n') && (64 > cnt));
+
+ if(cnt == 64)
+ {
+ return -1;
+ }
+ else
+ {
+ buf[cnt] = 0;
+ }
+#ifdef WEBRTC_CODEC_ILBC
+ if(!strcmp("#!iLBC20\n", buf))
+ {
+ // 20 ms is 304 bits
+ time_in_ms = ((file_size.st_size)*160)/304;
+ break;
+ }
+ if(!strcmp("#!iLBC30\n", buf))
+ {
+ // 30 ms takes 400 bits.
+ // file size in bytes * 8 / 400 is the number of
+ // 30 ms frames in the file ->
+ // time_in_ms = file size * 8 / 400 * 30
+ time_in_ms = ((file_size.st_size)*240)/400;
+ break;
+ }
+#endif
+ break;
+ }
+ case kFileFormatPreencodedFile:
+ {
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "cannot determine duration of Pre-Encoded file!");
+ break;
+ }
+ default:
+ WEBRTC_TRACE(kTraceError, kTraceFile, _id,
+ "unsupported file format %d!", fileFormat);
+ break;
+ }
+ inStreamObj->CloseFile();
+ delete inStreamObj;
+ return time_in_ms;
+}
+
+uint32_t ModuleFileUtility::PlayoutPositionMs()
+{
+ WEBRTC_TRACE(kTraceStream, kTraceFile, _id,
+ "ModuleFileUtility::PlayoutPosition()");
+
+ if(_reading)
+ {
+ return _playoutPositionMs;
+ }
+ else
+ {
+ return 0;
+ }
+}
+} // namespace webrtc