2011-07-07 08:21:25 +00:00
|
|
|
/*
|
2012-02-20 08:37:49 +00:00
|
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
2011-07-07 08:21:25 +00:00
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2017-09-15 06:47:31 +02:00
|
|
|
#include "modules/media_file/media_file_utility.h"
|
2013-11-18 11:45:11 +00:00
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
Use size_t more consistently for packet/payload lengths.
See design doc at https://docs.google.com/a/chromium.org/document/d/1I6nmE9D_BmCY-IoV6MDPY2V6WYpEI-dg2apWXTfZyUI/edit?usp=sharing for more information.
This CL was reviewed and approved in pieces in the following CLs:
https://webrtc-codereview.appspot.com/24209004/
https://webrtc-codereview.appspot.com/24229004/
https://webrtc-codereview.appspot.com/24259004/
https://webrtc-codereview.appspot.com/25109004/
https://webrtc-codereview.appspot.com/26099004/
https://webrtc-codereview.appspot.com/27069004/
https://webrtc-codereview.appspot.com/27969004/
https://webrtc-codereview.appspot.com/27989004/
https://webrtc-codereview.appspot.com/29009004/
https://webrtc-codereview.appspot.com/30929004/
https://webrtc-codereview.appspot.com/30939004/
https://webrtc-codereview.appspot.com/31999004/
Committing as TBR to the original reviewers.
BUG=chromium:81439
TEST=none
TBR=pthatcher,henrik.lundin,tina.legrand,stefan,tkchin,glaznev,kjellander,perkj,mflodman,henrika,asapersson,niklas.enbom
Review URL: https://webrtc-codereview.appspot.com/23129004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@7726 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-11-20 22:28:14 +00:00
|
|
|
#include <limits>
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-09-15 06:47:31 +02:00
|
|
|
#include "common_audio/wav_header.h"
|
2017-09-15 13:58:09 +02:00
|
|
|
#include "common_types.h" // NOLINT(build/include)
|
2017-09-15 06:47:31 +02:00
|
|
|
#include "modules/include/module_common_types.h"
|
|
|
|
|
#include "rtc_base/format_macros.h"
|
|
|
|
|
#include "rtc_base/logging.h"
|
|
|
|
|
#include "system_wrappers/include/file_wrapper.h"
|
2017-09-15 13:58:09 +02:00
|
|
|
#include "typedefs.h" // NOLINT(build/include)
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// First 16 bytes the WAVE header. ckID should be "RIFF", wave_ckID should be
|
|
|
|
|
// "WAVE" and ckSize is the chunk size (4 + n)
|
2017-11-09 09:33:23 +01:00
|
|
|
struct WAVE_RIFF_header {
|
|
|
|
|
int8_t ckID[4];
|
|
|
|
|
int32_t ckSize;
|
|
|
|
|
int8_t wave_ckID[4];
|
2011-07-07 08:21:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// First 8 byte of the format chunk. fmt_ckID should be "fmt ". fmt_ckSize is
|
|
|
|
|
// the chunk size (16, 18 or 40 byte)
|
2017-11-09 09:33:23 +01:00
|
|
|
struct WAVE_CHUNK_header {
|
|
|
|
|
int8_t fmt_ckID[4];
|
|
|
|
|
uint32_t fmt_ckSize;
|
2011-07-07 08:21:25 +00:00
|
|
|
};
|
2013-07-03 15:12:26 +00:00
|
|
|
} // unnamed namespace
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
namespace webrtc {
|
2017-08-22 13:44:09 +02:00
|
|
|
ModuleFileUtility::ModuleFileUtility()
|
2011-07-07 08:21:25 +00:00
|
|
|
: _wavFormatObj(),
|
|
|
|
|
_dataSize(0),
|
|
|
|
|
_readSizeBytes(0),
|
|
|
|
|
_stopPointInMs(0),
|
|
|
|
|
_startPointInMs(0),
|
|
|
|
|
_playoutPositionMs(0),
|
|
|
|
|
_bytesWritten(0),
|
|
|
|
|
codec_info_(),
|
|
|
|
|
_codecId(kCodecNoCodec),
|
|
|
|
|
_bytesPerSample(0),
|
|
|
|
|
_readPos(0),
|
|
|
|
|
_reading(false),
|
|
|
|
|
_writing(false),
|
2015-03-02 13:07:02 +00:00
|
|
|
_tempData() {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_INFO) << "ModuleFileUtility::ModuleFileUtility()";
|
2017-11-09 09:33:23 +01:00
|
|
|
memset(&codec_info_, 0, sizeof(CodecInst));
|
|
|
|
|
codec_info_.pltype = -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
ModuleFileUtility::~ModuleFileUtility() {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_INFO) << "ModuleFileUtility::~ModuleFileUtility()";
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
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];
|
|
|
|
|
size_t i;
|
|
|
|
|
bool dataFound = false;
|
|
|
|
|
bool fmtFound = false;
|
|
|
|
|
int8_t dummyRead;
|
|
|
|
|
|
|
|
|
|
_dataSize = 0;
|
|
|
|
|
int len = wav.Read(&RIFFheaderObj, sizeof(WAVE_RIFF_header));
|
|
|
|
|
if (len != static_cast<int>(sizeof(WAVE_RIFF_header))) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "Not a wave file (too short)";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
|
tmpStr[i] = RIFFheaderObj.ckID[i];
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(tmpStr, "RIFF") != 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "Not a wave file (does not have RIFF)";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
|
tmpStr[i] = RIFFheaderObj.wave_ckID[i];
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(tmpStr, "WAVE") != 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "Not a wave file (does not have WAVE)";
|
2017-11-09 09:33:23 +01:00
|
|
|
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 =
|
|
|
|
|
(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 == static_cast<int>(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));
|
|
|
|
|
|
|
|
|
|
if (CHUNKheaderObj.fmt_ckSize < sizeof(WAVE_FMTINFO_header)) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "Chunk size is too small";
|
2011-07-07 08:21:25 +00:00
|
|
|
return -1;
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
for (i = 0; i < CHUNKheaderObj.fmt_ckSize - sizeof(WAVE_FMTINFO_header);
|
|
|
|
|
i++) {
|
|
|
|
|
len = wav.Read(&dummyRead, 1);
|
|
|
|
|
if (len != 1) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "File corrupted, reached EOF (reading fmt)";
|
2017-11-09 09:33:23 +01:00
|
|
|
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) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "File corrupted, reached EOF (reading other)";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = wav.Read(&CHUNKheaderObj, sizeof(WAVE_CHUNK_header));
|
|
|
|
|
|
|
|
|
|
memcpy(tmpStr2, &CHUNKheaderObj.fmt_ckSize, 4);
|
|
|
|
|
CHUNKheaderObj.fmt_ckSize =
|
Misc. small cleanups.
* Better param names
* Avoid using negative values for (bogus) placeholder channel counts (mostly in tests). Since channels will be changing to size_t, negative values will be illegal; it's sufficient to use 0 in these cases.
* Use arraysize()
* Use size_t for counting frames, samples, blocks, buffers, and bytes -- most of these are already size_t in most places, this just fixes some stragglers
* reinterpret_cast<int64_t>(void*) is not necessarily safe; use uintptr_t instead
* Remove unnecessary code, e.g. dead code, needlessly long/repetitive code, or function overrides that exactly match the base definition
* Fix indenting
* Use uint32_t for timestamps (matching how it's already a uint32_t in most places)
* Spelling
* RTC_CHECK_EQ(expected, actual)
* Rewrap
* Use .empty()
* Be more pedantic about matching int/int32_t/
* Remove pointless consts on input parameters to functions
* Add missing sanity checks
All this was found in the course of constructing https://codereview.webrtc.org/1316523002/ , and is being landed separately first.
BUG=none
TEST=none
Review URL: https://codereview.webrtc.org/1534193008
Cr-Commit-Position: refs/heads/master@{#11191}
2016-01-08 13:50:27 -08:00
|
|
|
(uint32_t)tmpStr2[0] + (((uint32_t)tmpStr2[1]) << 8) +
|
|
|
|
|
(((uint32_t)tmpStr2[2]) << 16) + (((uint32_t)tmpStr2[3]) << 24);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
memcpy(tmpStr, CHUNKheaderObj.fmt_ckID, 4);
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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)) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "Coding formatTag value=" << _wavFormatObj.formatTag
|
|
|
|
|
<< " not supported!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if ((_wavFormatObj.nChannels < 1) || (_wavFormatObj.nChannels > 2)) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "nChannels value=" << _wavFormatObj.nChannels
|
|
|
|
|
<< " not supported!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if ((_wavFormatObj.nBitsPerSample != 8) &&
|
|
|
|
|
(_wavFormatObj.nBitsPerSample != 16)) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "nBitsPerSample value=" << _wavFormatObj.nBitsPerSample
|
|
|
|
|
<< " not supported!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate the number of bytes that 10 ms of audio data correspond to.
|
|
|
|
|
size_t samples_per_10ms =
|
|
|
|
|
((_wavFormatObj.formatTag == kWavFormatPcm) &&
|
|
|
|
|
(_wavFormatObj.nSamplesPerSec == 44100))
|
|
|
|
|
? 440
|
|
|
|
|
: static_cast<size_t>(_wavFormatObj.nSamplesPerSec / 100);
|
|
|
|
|
_readSizeBytes = samples_per_10ms * _wavFormatObj.nChannels *
|
|
|
|
|
(_wavFormatObj.nBitsPerSample / 8);
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::InitWavCodec(uint32_t samplesPerSec,
|
Convert channel counts to size_t.
IIRC, this was originally requested by ajm during review of the other size_t conversions I did over the past year, and I agreed it made sense, but wanted to do it separately since those changes were already gargantuan.
BUG=chromium:81439
TEST=none
R=henrik.lundin@webrtc.org, henrika@webrtc.org, kjellander@webrtc.org, minyue@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1316523002 .
Cr-Commit-Position: refs/heads/master@{#11229}
2016-01-12 16:26:35 -08:00
|
|
|
size_t channels,
|
2013-04-09 13:31:37 +00:00
|
|
|
uint32_t bitsPerSample,
|
2017-11-09 09:33:23 +01:00
|
|
|
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) {
|
New utility class for easy debug dumping to WAV files
There are currently a number of places in the code where we dump audio
data in various stages of processing for debug purposes. Currently
these all write raw, uncompressed PCM files, which isn't supported by
the most common audio players, and requires the user to supply
metadata such as sample rate, sample size and endianness, etc.
This patch adds a simple class that makes it easy to write WAV files
instead. WAV files still contain the same uncompressed PCM data, but
they have a small header that contains all the requisite metadata, and
are supported by virtually all audio players.
Since some of the debug code that will be writing WAV files is written
in plain C, a C API is included as well.
R=andrew@webrtc.org, bjornv@webrtc.org, henrike@webrtc.org, tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/16809004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@6932 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-20 07:42:46 +00:00
|
|
|
case kWavFormatALaw:
|
2017-11-09 09:33:23 +01:00
|
|
|
strcpy(codec_info_.plname, "PCMA");
|
|
|
|
|
_codecId = kCodecPcma;
|
|
|
|
|
codec_info_.pltype = 8;
|
|
|
|
|
codec_info_.pacsize = codec_info_.plfreq / 100;
|
|
|
|
|
break;
|
New utility class for easy debug dumping to WAV files
There are currently a number of places in the code where we dump audio
data in various stages of processing for debug purposes. Currently
these all write raw, uncompressed PCM files, which isn't supported by
the most common audio players, and requires the user to supply
metadata such as sample rate, sample size and endianness, etc.
This patch adds a simple class that makes it easy to write WAV files
instead. WAV files still contain the same uncompressed PCM data, but
they have a small header that contains all the requisite metadata, and
are supported by virtually all audio players.
Since some of the debug code that will be writing WAV files is written
in plain C, a C API is included as well.
R=andrew@webrtc.org, bjornv@webrtc.org, henrike@webrtc.org, tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/16809004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@6932 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-20 07:42:46 +00:00
|
|
|
case kWavFormatMuLaw:
|
2017-11-09 09:33:23 +01:00
|
|
|
strcpy(codec_info_.plname, "PCMU");
|
|
|
|
|
_codecId = kCodecPcmu;
|
|
|
|
|
codec_info_.pltype = 0;
|
|
|
|
|
codec_info_.pacsize = codec_info_.plfreq / 100;
|
|
|
|
|
break;
|
New utility class for easy debug dumping to WAV files
There are currently a number of places in the code where we dump audio
data in various stages of processing for debug purposes. Currently
these all write raw, uncompressed PCM files, which isn't supported by
the most common audio players, and requires the user to supply
metadata such as sample rate, sample size and endianness, etc.
This patch adds a simple class that makes it easy to write WAV files
instead. WAV files still contain the same uncompressed PCM data, but
they have a small header that contains all the requisite metadata, and
are supported by virtually all audio players.
Since some of the debug code that will be writing WAV files is written
in plain C, a C API is included as well.
R=andrew@webrtc.org, bjornv@webrtc.org, henrike@webrtc.org, tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/16809004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@6932 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-20 07:42:46 +00:00
|
|
|
case kWavFormatPcm:
|
2017-11-09 09:33:23 +01:00
|
|
|
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 {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "Unsupported PCM frequency!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "unknown WAV format TAG!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::InitWavReading(InStream& wav,
|
|
|
|
|
const uint32_t start,
|
2017-11-09 09:33:23 +01:00
|
|
|
const uint32_t stop) {
|
|
|
|
|
_reading = false;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (ReadWavHeader(wav) == -1) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "failed to read WAV header!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_playoutPositionMs = 0;
|
|
|
|
|
_readPos = 0;
|
|
|
|
|
|
|
|
|
|
if (start > 0) {
|
|
|
|
|
uint8_t dummy[WAV_MAX_BUFFER_SIZE];
|
|
|
|
|
int readLength;
|
|
|
|
|
if (_readSizeBytes <= WAV_MAX_BUFFER_SIZE) {
|
|
|
|
|
while (_playoutPositionMs < start) {
|
|
|
|
|
readLength = wav.Read(dummy, _readSizeBytes);
|
|
|
|
|
if (readLength == static_cast<int>(_readSizeBytes)) {
|
|
|
|
|
_readPos += _readSizeBytes;
|
|
|
|
|
_playoutPositionMs += 10;
|
|
|
|
|
} else // Must have reached EOF before start position!
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "InitWavReading(), EOF before start position";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
if (InitWavCodec(_wavFormatObj.nSamplesPerSec, _wavFormatObj.nChannels,
|
|
|
|
|
_wavFormatObj.nBitsPerSample,
|
|
|
|
|
_wavFormatObj.formatTag) != 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_bytesPerSample = static_cast<size_t>(_wavFormatObj.nBitsPerSample / 8);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
_startPointInMs = start;
|
|
|
|
|
_stopPointInMs = stop;
|
|
|
|
|
_reading = true;
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::ReadWavDataAsMono(InStream& wav,
|
|
|
|
|
int8_t* outData,
|
|
|
|
|
const size_t bufferSize) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadWavDataAsMono(wav= " << &wav
|
|
|
|
|
<< ", outData= " << static_cast<void*>(outData)
|
|
|
|
|
<< ", bufSize= " << bufferSize << ")";
|
2017-11-09 09:33:23 +01:00
|
|
|
|
|
|
|
|
// The number of bytes that should be read from file.
|
|
|
|
|
const size_t totalBytesNeeded = _readSizeBytes;
|
|
|
|
|
// The number of bytes that will be written to outData.
|
|
|
|
|
const size_t bytesRequested =
|
|
|
|
|
(codec_info_.channels == 2) ? totalBytesNeeded >> 1 : totalBytesNeeded;
|
|
|
|
|
if (bufferSize < bytesRequested) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadWavDataAsMono: output buffer is too short!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (outData == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadWavDataAsMono: output buffer NULL!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (!_reading) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadWavDataAsMono: no longer reading file.";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t bytesRead = ReadWavData(
|
|
|
|
|
wav, (codec_info_.channels == 2) ? _tempData : (uint8_t*)outData,
|
|
|
|
|
totalBytesNeeded);
|
|
|
|
|
if (bytesRead == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (bytesRead < 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR)
|
|
|
|
|
<< "ReadWavDataAsMono: failed to read data from WAV file.";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
// Output data is should be mono.
|
|
|
|
|
if (codec_info_.channels == 2) {
|
|
|
|
|
for (size_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 static_cast<int32_t>(bytesRequested);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::ReadWavDataAsStereo(InStream& wav,
|
|
|
|
|
int8_t* outDataLeft,
|
|
|
|
|
int8_t* outDataRight,
|
|
|
|
|
const size_t bufferSize) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadWavDataAsStereo(wav= " << &wav
|
|
|
|
|
<< ", outLeft= " << static_cast<void*>(outDataLeft)
|
|
|
|
|
<< ", outRight= " << static_cast<void*>(outDataRight)
|
|
|
|
|
<< ", bufSize= " << bufferSize << ")";
|
2017-11-09 09:33:23 +01:00
|
|
|
|
|
|
|
|
if ((outDataLeft == NULL) || (outDataRight == NULL)) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadWavDataAsStereo: an input buffer is NULL!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (codec_info_.channels != 2) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR)
|
2017-11-09 09:33:23 +01:00
|
|
|
<< "ReadWavDataAsStereo: WAV file does not contain stereo data!";
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!_reading) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadWavDataAsStereo: no longer reading file.";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The number of bytes that should be read from file.
|
|
|
|
|
const size_t totalBytesNeeded = _readSizeBytes;
|
|
|
|
|
// The number of bytes that will be written to the left and the right
|
|
|
|
|
// buffers.
|
|
|
|
|
const size_t bytesRequested = totalBytesNeeded >> 1;
|
|
|
|
|
if (bufferSize < bytesRequested) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadWavDataAsStereo: Output buffers are too short!";
|
2017-11-09 09:33:23 +01:00
|
|
|
assert(false);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t bytesRead = ReadWavData(wav, _tempData, totalBytesNeeded);
|
|
|
|
|
if (bytesRead <= 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR)
|
|
|
|
|
<< "ReadWavDataAsStereo: failed to read data from WAV file.";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Turn interleaved audio to left and right buffer. Note samples can be
|
|
|
|
|
// either 1 or 2 bytes
|
|
|
|
|
if (_bytesPerSample == 1) {
|
|
|
|
|
for (size_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.
|
|
|
|
|
size_t sampleCount = bytesRequested >> 1;
|
|
|
|
|
for (size_t i = 0; i < sampleCount; i++) {
|
|
|
|
|
outLeft[i] = sampleData[2 * i];
|
|
|
|
|
outRight[i] = sampleData[(2 * i) + 1];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadWavStereoData: unsupported sample size "
|
|
|
|
|
<< _bytesPerSample << "!";
|
2017-11-09 09:33:23 +01:00
|
|
|
assert(false);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return static_cast<int32_t>(bytesRequested);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
Misc. small cleanups.
* Better param names
* Avoid using negative values for (bogus) placeholder channel counts (mostly in tests). Since channels will be changing to size_t, negative values will be illegal; it's sufficient to use 0 in these cases.
* Use arraysize()
* Use size_t for counting frames, samples, blocks, buffers, and bytes -- most of these are already size_t in most places, this just fixes some stragglers
* reinterpret_cast<int64_t>(void*) is not necessarily safe; use uintptr_t instead
* Remove unnecessary code, e.g. dead code, needlessly long/repetitive code, or function overrides that exactly match the base definition
* Fix indenting
* Use uint32_t for timestamps (matching how it's already a uint32_t in most places)
* Spelling
* RTC_CHECK_EQ(expected, actual)
* Rewrap
* Use .empty()
* Be more pedantic about matching int/int32_t/
* Remove pointless consts on input parameters to functions
* Add missing sanity checks
All this was found in the course of constructing https://codereview.webrtc.org/1316523002/ , and is being landed separately first.
BUG=none
TEST=none
Review URL: https://codereview.webrtc.org/1534193008
Cr-Commit-Position: refs/heads/master@{#11191}
2016-01-08 13:50:27 -08:00
|
|
|
int32_t ModuleFileUtility::ReadWavData(InStream& wav,
|
|
|
|
|
uint8_t* buffer,
|
2017-11-09 09:33:23 +01:00
|
|
|
size_t dataLengthInBytes) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadWavData(wav= " << &wav
|
|
|
|
|
<< ", buffer= " << static_cast<void*>(buffer)
|
|
|
|
|
<< ", dataLen= " << dataLengthInBytes << ")";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (buffer == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadWavDataAsMono: output buffer NULL!";
|
2017-11-09 09:33:23 +01:00
|
|
|
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 + 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;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
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) {
|
2011-07-07 08:21:25 +00:00
|
|
|
_reading = false;
|
|
|
|
|
return -1;
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
_readPos += bytesRead;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
// 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;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
return bytesRead;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::InitWavWriting(OutStream& wav,
|
2017-11-09 09:33:23 +01:00
|
|
|
const CodecInst& codecInst) {
|
|
|
|
|
if (set_codec_info(codecInst) != 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "codecInst identifies unsupported codec!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_writing = false;
|
|
|
|
|
size_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;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
} else if (STR_CASE_CMP(codecInst.plname, "PCMA") == 0) {
|
|
|
|
|
_bytesPerSample = 1;
|
|
|
|
|
if (WriteWavHeader(wav, 8000, _bytesPerSample, channels, kWavFormatALaw,
|
|
|
|
|
0) == -1) {
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
} else if (STR_CASE_CMP(codecInst.plname, "L16") == 0) {
|
|
|
|
|
_bytesPerSample = 2;
|
|
|
|
|
if (WriteWavHeader(wav, codecInst.plfreq, _bytesPerSample, channels,
|
|
|
|
|
kWavFormatPcm, 0) == -1) {
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
} else {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "codecInst identifies unsupported codec for WAV file!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_writing = true;
|
|
|
|
|
_bytesWritten = 0;
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::WriteWavData(OutStream& out,
|
2017-11-09 09:33:23 +01:00
|
|
|
const int8_t* buffer,
|
|
|
|
|
const size_t dataLength) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::WriteWavData(out= " << &out
|
|
|
|
|
<< ", buf= " << static_cast<const void*>(buffer)
|
|
|
|
|
<< ", dataLen= " << dataLength << ")";
|
2017-11-09 09:33:23 +01:00
|
|
|
|
|
|
|
|
if (buffer == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "WriteWavData: input buffer NULL!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (!out.Write(buffer, dataLength)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_bytesWritten += dataLength;
|
|
|
|
|
return static_cast<int32_t>(dataLength);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::WriteWavHeader(OutStream& wav,
|
|
|
|
|
uint32_t freq,
|
|
|
|
|
size_t bytesPerSample,
|
|
|
|
|
size_t channels,
|
|
|
|
|
uint32_t format,
|
|
|
|
|
size_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 size_t frameSize = (freq / 100) * channels;
|
|
|
|
|
|
|
|
|
|
// Calculate the number of full frames that the wave file contain.
|
|
|
|
|
const size_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;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::UpdateWavHeader(OutStream& wav) {
|
|
|
|
|
int32_t res = -1;
|
|
|
|
|
if (wav.Rewind() == -1) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
size_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;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::InitPreEncodedReading(InStream& in,
|
2017-11-09 09:33:23 +01:00
|
|
|
const CodecInst& cinst) {
|
|
|
|
|
uint8_t preEncodedID;
|
|
|
|
|
in.Read(&preEncodedID, 1);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
MediaFileUtility_CodecType codecType =
|
|
|
|
|
(MediaFileUtility_CodecType)preEncodedID;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (set_codec_info(cinst) != 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "Pre-encoded file send codec mismatch!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (codecType != _codecId) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "Pre-encoded file format codec mismatch!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
memcpy(&codec_info_, &cinst, sizeof(CodecInst));
|
|
|
|
|
_reading = true;
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::ReadPreEncodedData(InStream& in,
|
|
|
|
|
int8_t* outData,
|
|
|
|
|
const size_t bufferSize) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadPreEncodedData(in= " << &in
|
|
|
|
|
<< ", outData= " << static_cast<void*>(outData)
|
|
|
|
|
<< ", bufferSize= " << bufferSize << ")";
|
2017-11-09 09:33:23 +01:00
|
|
|
|
|
|
|
|
if (outData == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "output buffer NULL";
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_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;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
frameLen = buf[0] + buf[1] * 256;
|
|
|
|
|
if (bufferSize < frameLen) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "buffer not large enough to read " << frameLen
|
|
|
|
|
<< " bytes of pre-encoded data!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return in.Read(outData, frameLen);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::InitPreEncodedWriting(OutStream& out,
|
|
|
|
|
const CodecInst& codecInst) {
|
|
|
|
|
if (set_codec_info(codecInst) != 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "CodecInst not recognized!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_writing = true;
|
|
|
|
|
_bytesWritten = 1;
|
|
|
|
|
out.Write(&_codecId, 1);
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::WritePreEncodedData(OutStream& out,
|
|
|
|
|
const int8_t* buffer,
|
|
|
|
|
const size_t dataLength) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::WritePreEncodedData(out= " << &out
|
|
|
|
|
<< " , inData= " << static_cast<const void*>(buffer)
|
|
|
|
|
<< ", dataLen= " << dataLength << ")";
|
2017-11-09 09:33:23 +01:00
|
|
|
|
|
|
|
|
if (buffer == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "buffer NULL";
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (!out.Write(buffer, dataLength)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
bytesWritten += dataLength;
|
|
|
|
|
return static_cast<int32_t>(bytesWritten);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::InitCompressedReading(InStream& in,
|
|
|
|
|
const uint32_t start,
|
|
|
|
|
const uint32_t stop) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::InitCompressedReading(in= " << &in
|
|
|
|
|
<< ", start= " << start << ", stop= " << stop << ")";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2015-02-23 09:25:40 +00:00
|
|
|
#if defined(WEBRTC_CODEC_ILBC)
|
2017-11-09 09:33:23 +01:00
|
|
|
int16_t read_len = 0;
|
2011-12-09 09:58:39 +00:00
|
|
|
#endif
|
2017-11-09 09:33:23 +01:00
|
|
|
_codecId = kCodecNoCodec;
|
|
|
|
|
_playoutPositionMs = 0;
|
|
|
|
|
_reading = false;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
_startPointInMs = start;
|
|
|
|
|
_stopPointInMs = stop;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
// Read the codec name
|
|
|
|
|
int32_t cnt = 0;
|
|
|
|
|
char buf[64];
|
|
|
|
|
do {
|
|
|
|
|
in.Read(&buf[cnt++], 1);
|
|
|
|
|
} while ((buf[cnt - 1] != '\n') && (64 > cnt));
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (cnt == 64) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
buf[cnt] = 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
#ifdef WEBRTC_CODEC_ILBC
|
2017-11-09 09:33:23 +01:00
|
|
|
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) {
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
_playoutPositionMs += 20;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
_playoutPositionMs += 20;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
#endif
|
2017-11-09 09:33:23 +01:00
|
|
|
if (_codecId == kCodecNoCodec) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_reading = true;
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::ReadCompressedData(InStream& in,
|
|
|
|
|
int8_t* outData,
|
2017-11-09 09:33:23 +01:00
|
|
|
size_t bufferSize) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadCompressedData(in=" << &in
|
|
|
|
|
<< ", outData=" << static_cast<void*>(outData)
|
|
|
|
|
<< ", bytes=" << bufferSize << ")";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int bytesRead = 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (!_reading) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "not currently reading!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
#ifdef WEBRTC_CODEC_ILBC
|
2017-11-09 09:33:23 +01:00
|
|
|
if ((_codecId == kCodecIlbc20Ms) || (_codecId == kCodecIlbc30Ms)) {
|
|
|
|
|
size_t byteSize = 0;
|
|
|
|
|
if (_codecId == kCodecIlbc30Ms) {
|
|
|
|
|
byteSize = 50;
|
|
|
|
|
}
|
|
|
|
|
if (_codecId == kCodecIlbc20Ms) {
|
|
|
|
|
byteSize = 38;
|
|
|
|
|
}
|
|
|
|
|
if (bufferSize < byteSize) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR)
|
2017-11-09 09:33:23 +01:00
|
|
|
<< "output buffer is too short to read ILBC compressed data.";
|
|
|
|
|
assert(false);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
bytesRead = in.Read(outData, byteSize);
|
|
|
|
|
if (bytesRead != static_cast<int>(byteSize)) {
|
|
|
|
|
if (!in.Rewind()) {
|
|
|
|
|
InitCompressedReading(in, _startPointInMs, _stopPointInMs);
|
2011-07-07 08:21:25 +00:00
|
|
|
bytesRead = in.Read(outData, byteSize);
|
2017-11-09 09:33:23 +01:00
|
|
|
if (bytesRead != static_cast<int>(byteSize)) {
|
|
|
|
|
_reading = false;
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
} else {
|
|
|
|
|
_reading = false;
|
2011-07-07 08:21:25 +00:00
|
|
|
return -1;
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (bytesRead == 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR)
|
|
|
|
|
<< "ReadCompressedData() no bytes read, codec not supported";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
_playoutPositionMs += 20;
|
|
|
|
|
if ((_stopPointInMs > 0) && (_playoutPositionMs >= _stopPointInMs)) {
|
|
|
|
|
if (!in.Rewind()) {
|
|
|
|
|
InitCompressedReading(in, _startPointInMs, _stopPointInMs);
|
|
|
|
|
} else {
|
|
|
|
|
_reading = false;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
return bytesRead;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::InitCompressedWriting(OutStream& out,
|
|
|
|
|
const CodecInst& codecInst) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::InitCompressedWriting(out= "
|
|
|
|
|
<< &out << ", codecName= " << codecInst.plname << ")";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
_writing = false;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
#ifdef WEBRTC_CODEC_ILBC
|
2017-11-09 09:33:23 +01:00
|
|
|
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 {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "codecInst defines unsupported compression codec!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
memcpy(&codec_info_, &codecInst, sizeof(CodecInst));
|
|
|
|
|
_writing = true;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
#endif
|
|
|
|
|
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "codecInst defines unsupported compression codec!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::WriteCompressedData(OutStream& out,
|
|
|
|
|
const int8_t* buffer,
|
|
|
|
|
const size_t dataLength) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::WriteCompressedData(out= " << &out
|
|
|
|
|
<< ", buf= " << static_cast<const void*>(buffer)
|
|
|
|
|
<< ", dataLen= " << dataLength << ")";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (buffer == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "buffer NULL";
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!out.Write(buffer, dataLength)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return static_cast<int32_t>(dataLength);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::InitPCMReading(InStream& pcm,
|
|
|
|
|
const uint32_t start,
|
|
|
|
|
const uint32_t stop,
|
2017-11-09 09:33:23 +01:00
|
|
|
uint32_t freq) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::InitPCMReading(pcm= " << &pcm
|
|
|
|
|
<< ", start=" << start << ", stop=" << stop
|
|
|
|
|
<< ", freq=" << freq << ")";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int8_t dummy[320];
|
|
|
|
|
int read_len;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
_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;
|
|
|
|
|
} else if (freq == 48000) {
|
|
|
|
|
strcpy(codec_info_.plname, "L16");
|
|
|
|
|
codec_info_.pltype = -1;
|
|
|
|
|
codec_info_.plfreq = 48000;
|
|
|
|
|
codec_info_.pacsize = 480;
|
|
|
|
|
codec_info_.channels = 1;
|
|
|
|
|
codec_info_.rate = 768000;
|
|
|
|
|
_codecId = kCodecL16_48Khz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 != static_cast<int>(_readSizeBytes)) {
|
|
|
|
|
return -1; // Must have reached EOF before start position!
|
|
|
|
|
}
|
|
|
|
|
_playoutPositionMs += 10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_reading = true;
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::ReadPCMData(InStream& pcm,
|
|
|
|
|
int8_t* outData,
|
2017-11-09 09:33:23 +01:00
|
|
|
size_t bufferSize) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::ReadPCMData(pcm= " << &pcm
|
|
|
|
|
<< ", outData= " << static_cast<void*>(outData)
|
|
|
|
|
<< ", bufSize= " << bufferSize << ")";
|
2017-11-09 09:33:23 +01:00
|
|
|
|
|
|
|
|
if (outData == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "buffer NULL";
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Readsize for 10ms of audio data (2 bytes per sample).
|
|
|
|
|
size_t bytesRequested = static_cast<size_t>(2 * codec_info_.plfreq / 100);
|
|
|
|
|
if (bufferSize < bytesRequested) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR)
|
|
|
|
|
<< "ReadPCMData: buffer not long enough for a 10ms frame.";
|
2017-11-09 09:33:23 +01:00
|
|
|
assert(false);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int bytesRead = pcm.Read(outData, bytesRequested);
|
|
|
|
|
if (bytesRead < static_cast<int>(bytesRequested)) {
|
|
|
|
|
if (pcm.Rewind() == -1) {
|
|
|
|
|
_reading = false;
|
|
|
|
|
} else {
|
|
|
|
|
if (InitPCMReading(pcm, _startPointInMs, _stopPointInMs,
|
|
|
|
|
codec_info_.plfreq) == -1) {
|
|
|
|
|
_reading = false;
|
|
|
|
|
} else {
|
|
|
|
|
size_t rest = bytesRequested - bytesRead;
|
|
|
|
|
int len = pcm.Read(&(outData[bytesRead]), rest);
|
|
|
|
|
if (len == static_cast<int>(rest)) {
|
|
|
|
|
bytesRead += len;
|
|
|
|
|
} else {
|
|
|
|
|
_reading = false;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
if (bytesRead <= 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "ReadPCMData: Failed to rewind audio file.";
|
2011-07-07 08:21:25 +00:00
|
|
|
return -1;
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (bytesRead <= 0) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ReadPCMData: end of file";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_playoutPositionMs += 10;
|
|
|
|
|
if (_stopPointInMs && _playoutPositionMs >= _stopPointInMs) {
|
|
|
|
|
if (!pcm.Rewind()) {
|
|
|
|
|
if (InitPCMReading(pcm, _startPointInMs, _stopPointInMs,
|
|
|
|
|
codec_info_.plfreq) == -1) {
|
|
|
|
|
_reading = false;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
return bytesRead;
|
|
|
|
|
}
|
2017-09-01 14:36:33 +02:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
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;
|
|
|
|
|
} else if (freq == 48000) {
|
|
|
|
|
strcpy(codec_info_.plname, "L16");
|
|
|
|
|
codec_info_.pltype = -1;
|
|
|
|
|
codec_info_.plfreq = 48000;
|
|
|
|
|
codec_info_.pacsize = 480;
|
|
|
|
|
codec_info_.channels = 1;
|
|
|
|
|
codec_info_.rate = 768000;
|
|
|
|
|
|
|
|
|
|
_codecId = kCodecL16_48Khz;
|
|
|
|
|
}
|
|
|
|
|
if ((_codecId != kCodecL16_8Khz) && (_codecId != kCodecL16_16kHz) &&
|
|
|
|
|
(_codecId != kCodecL16_32Khz) && (_codecId != kCodecL16_48Khz)) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "CodecInst is not 8KHz, 16KHz, 32kHz or 48kHz PCM!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_writing = true;
|
|
|
|
|
_bytesWritten = 0;
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::WritePCMData(OutStream& out,
|
2017-11-09 09:33:23 +01:00
|
|
|
const int8_t* buffer,
|
|
|
|
|
const size_t dataLength) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::WritePCMData(out= " << &out
|
|
|
|
|
<< ", buf= " << static_cast<const void*>(buffer)
|
|
|
|
|
<< ", dataLen= " << dataLength << ")";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (buffer == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "buffer NULL";
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (!out.Write(buffer, dataLength)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_bytesWritten += dataLength;
|
|
|
|
|
return static_cast<int32_t>(dataLength);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t ModuleFileUtility::codec_info(CodecInst& codecInst) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::codec_info(codecInst= "
|
|
|
|
|
<< &codecInst << ")";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
if (!_reading && !_writing) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "CodecInst: not currently reading audio file!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
memcpy(&codecInst, &codec_info_, sizeof(CodecInst));
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
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;
|
|
|
|
|
} else if (codecInst.plfreq == 48000) {
|
|
|
|
|
_codecId = kCodecL16_48Khz;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
#ifdef WEBRTC_CODEC_ILBC
|
2017-11-09 09:33:23 +01:00
|
|
|
else if (STR_CASE_CMP(codecInst.plname, "ilbc") == 0) {
|
|
|
|
|
if (codecInst.pacsize == 160) {
|
|
|
|
|
_codecId = kCodecIlbc20Ms;
|
|
|
|
|
} else if (codecInst.pacsize == 240) {
|
|
|
|
|
_codecId = kCodecIlbc30Ms;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
#endif
|
2017-11-09 09:33:23 +01:00
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
#endif
|
2017-11-09 09:33:23 +01:00
|
|
|
else if (STR_CASE_CMP(codecInst.plname, "G722") == 0) {
|
|
|
|
|
_codecId = kCodecG722;
|
|
|
|
|
}
|
|
|
|
|
if (_codecId == kCodecNoCodec) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
memcpy(&codec_info_, &codecInst, sizeof(CodecInst));
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 13:31:37 +00:00
|
|
|
int32_t ModuleFileUtility::FileDurationMs(const char* fileName,
|
|
|
|
|
const FileFormats fileFormat,
|
2017-11-09 09:33:23 +01:00
|
|
|
const uint32_t freqInHz) {
|
|
|
|
|
if (fileName == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "filename NULL";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
int32_t time_in_ms = -1;
|
|
|
|
|
struct stat file_size;
|
|
|
|
|
if (stat(fileName, &file_size) == -1) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "failed to retrieve file size with stat!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
FileWrapper* inStreamObj = FileWrapper::Create();
|
|
|
|
|
if (inStreamObj == NULL) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_INFO) << "failed to create InStream object!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!inStreamObj->OpenFile(fileName, true)) {
|
|
|
|
|
delete inStreamObj;
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "failed to open file " << fileName << "!";
|
2017-11-09 09:33:23 +01:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
switch (fileFormat) {
|
|
|
|
|
case kFileFormatWavFile: {
|
|
|
|
|
if (ReadWavHeader(*inStreamObj) == -1) {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "failed to read WAV file header!";
|
2011-07-07 08:21:25 +00:00
|
|
|
return -1;
|
2017-11-09 09:33:23 +01:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
int read_len = 0;
|
|
|
|
|
char buf[64];
|
|
|
|
|
do {
|
|
|
|
|
read_len = inStreamObj->Read(&buf[cnt++], 1);
|
|
|
|
|
if (read_len != 1) {
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2017-11-09 09:33:23 +01:00
|
|
|
} while ((buf[cnt - 1] != '\n') && (64 > cnt));
|
|
|
|
|
|
|
|
|
|
if (cnt == 64) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
buf[cnt] = 0;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
#ifdef WEBRTC_CODEC_ILBC
|
2017-11-09 09:33:23 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
#endif
|
2017-11-09 09:33:23 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case kFileFormatPreencodedFile: {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "cannot determine duration of Pre-Encoded file!";
|
2017-11-09 09:33:23 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_ERROR) << "unsupported file format " << fileFormat << "!";
|
2017-11-09 09:33:23 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
inStreamObj->CloseFile();
|
|
|
|
|
delete inStreamObj;
|
|
|
|
|
return time_in_ms;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
uint32_t ModuleFileUtility::PlayoutPositionMs() {
|
2017-11-09 11:09:25 +01:00
|
|
|
RTC_LOG(LS_VERBOSE) << "ModuleFileUtility::PlayoutPosition()";
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-11-09 09:33:23 +01:00
|
|
|
return _reading ? _playoutPositionMs : 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2013-07-03 15:12:26 +00:00
|
|
|
} // namespace webrtc
|