blob: fad7fe40794f79ea95914a7cf6b73c5575fcbefd [file] [log] [blame]
/*
* 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/include/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