Dual-stream implementation, not including VoE APIs.
Review URL: https://webrtc-codereview.appspot.com/933015 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3230 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
277ec8e3f5
commit
226db898f7
@ -13,8 +13,10 @@
|
|||||||
|
|
||||||
#include "audio_coding_module_typedefs.h"
|
#include "audio_coding_module_typedefs.h"
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
|
// TODO(turajs): If possible, forward declare and remove the following include.
|
||||||
#include "module_common_types.h"
|
#include "module_common_types.h"
|
||||||
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
@ -204,6 +206,10 @@ class AudioCodingModule: public Module {
|
|||||||
// Note: If a stereo codec is registered as send codec, VAD/DTX will
|
// Note: If a stereo codec is registered as send codec, VAD/DTX will
|
||||||
// automatically be turned off, since it is not supported for stereo sending.
|
// automatically be turned off, since it is not supported for stereo sending.
|
||||||
//
|
//
|
||||||
|
// Note: If a secondary encoder is already registered, and the new send-codec
|
||||||
|
// has a sampling rate that does not match the secondary encoder, the
|
||||||
|
// secondary encoder will be unregistered.
|
||||||
|
//
|
||||||
// Input:
|
// Input:
|
||||||
// -sendCodec : Parameters of the codec to be registered, c.f.
|
// -sendCodec : Parameters of the codec to be registered, c.f.
|
||||||
// common_types.h for the definition of
|
// common_types.h for the definition of
|
||||||
@ -215,6 +221,33 @@ class AudioCodingModule: public Module {
|
|||||||
//
|
//
|
||||||
virtual WebRtc_Word32 RegisterSendCodec(const CodecInst& sendCodec) = 0;
|
virtual WebRtc_Word32 RegisterSendCodec(const CodecInst& sendCodec) = 0;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// int RegisterSecondarySendCodec()
|
||||||
|
// Register a secondary encoder to enable dual-streaming. If a secondary
|
||||||
|
// codec is already registered, it will be removed before the new one is
|
||||||
|
// registered.
|
||||||
|
//
|
||||||
|
// Note: The secondary encoder will be unregistered if a primary codec
|
||||||
|
// is set with a sampling rate which does not match that of the existing
|
||||||
|
// secondary codec.
|
||||||
|
//
|
||||||
|
// Input:
|
||||||
|
// -send_codec : Parameters of the codec to be registered, c.f.
|
||||||
|
// common_types.h for the definition of
|
||||||
|
// CodecInst.
|
||||||
|
//
|
||||||
|
// Return value:
|
||||||
|
// -1 if failed to register,
|
||||||
|
// 0 if succeeded.
|
||||||
|
//
|
||||||
|
virtual int RegisterSecondarySendCodec(const CodecInst& send_codec) = 0;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// void UnregisterSecondarySendCodec()
|
||||||
|
// Unregister the secondary encoder to disable dual-streaming.
|
||||||
|
//
|
||||||
|
virtual void UnregisterSecondarySendCodec() = 0;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// WebRtc_Word32 SendCodec()
|
// WebRtc_Word32 SendCodec()
|
||||||
// Get parameters for the codec currently registered as send codec.
|
// Get parameters for the codec currently registered as send codec.
|
||||||
@ -228,6 +261,19 @@ class AudioCodingModule: public Module {
|
|||||||
//
|
//
|
||||||
virtual WebRtc_Word32 SendCodec(CodecInst& currentSendCodec) const = 0;
|
virtual WebRtc_Word32 SendCodec(CodecInst& currentSendCodec) const = 0;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// int SecondarySendCodec()
|
||||||
|
// Get the codec parameters for the current secondary send codec.
|
||||||
|
//
|
||||||
|
// Output:
|
||||||
|
// -secondary_codec : parameters of the secondary send codec.
|
||||||
|
//
|
||||||
|
// Return value:
|
||||||
|
// -1 if failed to get send codec,
|
||||||
|
// 0 if succeeded.
|
||||||
|
//
|
||||||
|
virtual int SecondarySendCodec(CodecInst* secondary_codec) const = 0;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// WebRtc_Word32 SendFrequency()
|
// WebRtc_Word32 SendFrequency()
|
||||||
// Get the sampling frequency of the current encoder in Hertz.
|
// Get the sampling frequency of the current encoder in Hertz.
|
||||||
|
@ -29,7 +29,7 @@ enum {
|
|||||||
|
|
||||||
// Interval for sending new CNG parameters (SID frames) is 100 msec.
|
// Interval for sending new CNG parameters (SID frames) is 100 msec.
|
||||||
enum {
|
enum {
|
||||||
kAcmSidIntervalMsec = 100
|
kCngSidIntervalMsec = 100
|
||||||
};
|
};
|
||||||
|
|
||||||
// We set some of the variables to invalid values as a check point
|
// We set some of the variables to invalid values as a check point
|
||||||
@ -70,7 +70,6 @@ ACMGenericCodec::ACMGenericCodec()
|
|||||||
for (int i = 0; i < MAX_FRAME_SIZE_10MSEC; i++) {
|
for (int i = 0; i < MAX_FRAME_SIZE_10MSEC; i++) {
|
||||||
_vadLabel[i] = 0;
|
_vadLabel[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nullify memory for encoder and decoder, and set payload type to an
|
// Nullify memory for encoder and decoder, and set payload type to an
|
||||||
// invalid value.
|
// invalid value.
|
||||||
memset(&_encoderParams, 0, sizeof(WebRtcACMCodecParams));
|
memset(&_encoderParams, 0, sizeof(WebRtcACMCodecParams));
|
||||||
@ -143,7 +142,7 @@ int32_t ACMGenericCodec::Add10MsDataSafe(const uint32_t timestamp,
|
|||||||
|
|
||||||
_lastTimestamp = timestamp;
|
_lastTimestamp = timestamp;
|
||||||
|
|
||||||
// If the data exceeds the buffer size, we through away the oldest data and
|
// If the data exceeds the buffer size, we throw away the oldest data and
|
||||||
// add the newly received 10 msec at the end.
|
// add the newly received 10 msec at the end.
|
||||||
if ((_inAudioIxWrite + lengthSmpl * audioChannel) > AUDIO_BUFFER_SIZE_W16) {
|
if ((_inAudioIxWrite + lengthSmpl * audioChannel) > AUDIO_BUFFER_SIZE_W16) {
|
||||||
// Get the number of samples to be overwritten.
|
// Get the number of samples to be overwritten.
|
||||||
@ -191,29 +190,27 @@ int32_t ACMGenericCodec::Add10MsDataSafe(const uint32_t timestamp,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ACMGenericCodec::HasFrameToEncode() const {
|
||||||
|
ReadLockScoped lockCodec(_codecWrapperLock);
|
||||||
|
if (_inAudioIxWrite < _frameLenSmpl * _noChannels)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int16_t ACMGenericCodec::Encode(uint8_t* bitStream,
|
int16_t ACMGenericCodec::Encode(uint8_t* bitStream,
|
||||||
int16_t* bitStreamLenByte,
|
int16_t* bitStreamLenByte,
|
||||||
uint32_t* timeStamp,
|
uint32_t* timeStamp,
|
||||||
WebRtcACMEncodingType* encodingType) {
|
WebRtcACMEncodingType* encodingType) {
|
||||||
WriteLockScoped lockCodec(_codecWrapperLock);
|
if (!HasFrameToEncode()) {
|
||||||
ReadLockScoped lockNetEq(*_netEqDecodeLock);
|
// There is not enough audio
|
||||||
return EncodeSafe(bitStream, bitStreamLenByte, timeStamp, encodingType);
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t ACMGenericCodec::EncodeSafe(uint8_t* bitStream,
|
|
||||||
int16_t* bitStreamLenByte,
|
|
||||||
uint32_t* timeStamp,
|
|
||||||
WebRtcACMEncodingType* encodingType) {
|
|
||||||
// Only encode if we have enough data to encode. If not wait until we have a
|
|
||||||
// full frame to encode.
|
|
||||||
if (_inAudioIxWrite < _frameLenSmpl * _noChannels) {
|
|
||||||
// There is not enough audio.
|
|
||||||
*timeStamp = 0;
|
*timeStamp = 0;
|
||||||
*bitStreamLenByte = 0;
|
*bitStreamLenByte = 0;
|
||||||
// Doesn't really matter what this parameter set to.
|
// Doesn't really matter what this parameter set to
|
||||||
*encodingType = kNoEncoding;
|
*encodingType = kNoEncoding;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
WriteLockScoped lockCodec(_codecWrapperLock);
|
||||||
|
ReadLockScoped lockNetEq(*_netEqDecodeLock);
|
||||||
|
|
||||||
// Not all codecs accept the whole frame to be pushed into encoder at once.
|
// Not all codecs accept the whole frame to be pushed into encoder at once.
|
||||||
// Some codecs needs to be feed with a specific number of samples different
|
// Some codecs needs to be feed with a specific number of samples different
|
||||||
@ -230,7 +227,6 @@ int16_t ACMGenericCodec::EncodeSafe(uint8_t* bitStream,
|
|||||||
"EncodeSafe: error, basic coding sample block is negative");
|
"EncodeSafe: error, basic coding sample block is negative");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This makes the internal encoder read from the beginning of the buffer.
|
// This makes the internal encoder read from the beginning of the buffer.
|
||||||
_inAudioIxRead = 0;
|
_inAudioIxRead = 0;
|
||||||
*timeStamp = _inTimestamp[0];
|
*timeStamp = _inTimestamp[0];
|
||||||
@ -932,7 +928,7 @@ int16_t ACMGenericCodec::EnableDTX() {
|
|||||||
}
|
}
|
||||||
uint16_t freqHz;
|
uint16_t freqHz;
|
||||||
EncoderSampFreq(freqHz);
|
EncoderSampFreq(freqHz);
|
||||||
if (WebRtcCng_InitEnc(_ptrDTXInst, freqHz, kAcmSidIntervalMsec,
|
if (WebRtcCng_InitEnc(_ptrDTXInst, freqHz, kCngSidIntervalMsec,
|
||||||
_numLPCParams) < 0) {
|
_numLPCParams) < 0) {
|
||||||
// Couldn't initialize, has to return -1, and free the memory.
|
// Couldn't initialize, has to return -1, and free the memory.
|
||||||
WebRtcCng_FreeEnc(_ptrDTXInst);
|
WebRtcCng_FreeEnc(_ptrDTXInst);
|
||||||
|
@ -711,20 +711,20 @@ class ACMGenericCodec {
|
|||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// REDPayloadISAC()
|
// REDPayloadISAC()
|
||||||
// This is an iSAC-specific function. The function is called to get RED
|
// This is an iSAC-specific function. The function is called to get RED
|
||||||
// paylaod from a default-encoder.
|
// payload from a default-encoder.
|
||||||
//
|
//
|
||||||
// Inputs:
|
// Inputs:
|
||||||
// -isacRate : the target rate of the main payload. A RED
|
// -isacRate : the target rate of the main payload. A RED
|
||||||
// paylaod is generated according to the rate of
|
// payload is generated according to the rate of
|
||||||
// main paylaod. Note that we are not specifying the
|
// main payload. Note that we are not specifying the
|
||||||
// rate of RED payload, but the main payload.
|
// rate of RED payload, but the main payload.
|
||||||
// -isacBwEstimate : bandwidth information should be inserted in
|
// -isacBwEstimate : bandwidth information should be inserted in
|
||||||
// RED payload.
|
// RED payload.
|
||||||
//
|
//
|
||||||
// Output:
|
// Output:
|
||||||
// -payload : pointer to a buffer where the RED paylaod will
|
// -payload : pointer to a buffer where the RED payload will
|
||||||
// written to.
|
// written to.
|
||||||
// -paylaodLenBytes : a place-holder to write the length of the RED
|
// -payloadLenBytes : a place-holder to write the length of the RED
|
||||||
// payload in Bytes.
|
// payload in Bytes.
|
||||||
//
|
//
|
||||||
// Return value:
|
// Return value:
|
||||||
@ -749,6 +749,13 @@ class ACMGenericCodec {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// HasFrameToEncode()
|
||||||
|
// Returns true if there is enough audio buffered for encoding, such that
|
||||||
|
// calling Encode() will return a payload.
|
||||||
|
//
|
||||||
|
bool HasFrameToEncode() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// All the functions with FunctionNameSafe(...) contain the actual
|
// All the functions with FunctionNameSafe(...) contain the actual
|
||||||
@ -758,15 +765,6 @@ class ACMGenericCodec {
|
|||||||
// and return value we refer to FunctionName()
|
// and return value we refer to FunctionName()
|
||||||
//
|
//
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// See Encode() for the description of function, input(s)/output(s) and
|
|
||||||
// return value.
|
|
||||||
//
|
|
||||||
WebRtc_Word16 EncodeSafe(WebRtc_UWord8* bitStream,
|
|
||||||
WebRtc_Word16* bitStreamLenByte,
|
|
||||||
WebRtc_UWord32* timeStamp,
|
|
||||||
WebRtcACMEncodingType* encodingType);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// See Decode() for the description of function, input(s)/output(s) and
|
// See Decode() for the description of function, input(s)/output(s) and
|
||||||
// return value.
|
// return value.
|
||||||
|
@ -119,6 +119,10 @@
|
|||||||
'<(webrtc_root)/test/test.gyp:test_support_main',
|
'<(webrtc_root)/test/test.gyp:test_support_main',
|
||||||
'<(DEPTH)/testing/gtest.gyp:gtest',
|
'<(DEPTH)/testing/gtest.gyp:gtest',
|
||||||
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||||
|
'<(webrtc_root)/modules/modules.gyp:webrtc_utility',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'<(webrtc_root)/common_audio/resampler/include',
|
||||||
],
|
],
|
||||||
'defines': [
|
'defines': [
|
||||||
'<@(audio_coding_defines)',
|
'<@(audio_coding_defines)',
|
||||||
@ -127,6 +131,7 @@
|
|||||||
'../test/ACMTest.cc',
|
'../test/ACMTest.cc',
|
||||||
'../test/APITest.cc',
|
'../test/APITest.cc',
|
||||||
'../test/Channel.cc',
|
'../test/Channel.cc',
|
||||||
|
'../test/dual_stream_unittest.cc',
|
||||||
'../test/EncodeDecodeTest.cc',
|
'../test/EncodeDecodeTest.cc',
|
||||||
'../test/iSACTest.cc',
|
'../test/iSACTest.cc',
|
||||||
'../test/PCMFile.cc',
|
'../test/PCMFile.cc',
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@
|
|||||||
#include "acm_resampler.h"
|
#include "acm_resampler.h"
|
||||||
#include "common_types.h"
|
#include "common_types.h"
|
||||||
#include "engine_configurations.h"
|
#include "engine_configurations.h"
|
||||||
|
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -59,6 +60,17 @@ class AudioCodingModuleImpl : public AudioCodingModule {
|
|||||||
// Can be called multiple times for Codec, CNG, RED.
|
// Can be called multiple times for Codec, CNG, RED.
|
||||||
WebRtc_Word32 RegisterSendCodec(const CodecInst& send_codec);
|
WebRtc_Word32 RegisterSendCodec(const CodecInst& send_codec);
|
||||||
|
|
||||||
|
// Register Secondary codec for dual-streaming. Dual-streaming is activated
|
||||||
|
// right after the secondary codec is registered.
|
||||||
|
int RegisterSecondarySendCodec(const CodecInst& send_codec);
|
||||||
|
|
||||||
|
// Unregister the secondary codec. Dual-streaming is deactivated right after
|
||||||
|
// deregistering secondary codec.
|
||||||
|
void UnregisterSecondarySendCodec();
|
||||||
|
|
||||||
|
// Get the secondary codec.
|
||||||
|
int SecondarySendCodec(CodecInst* secondary_codec) const;
|
||||||
|
|
||||||
// Get current send codec.
|
// Get current send codec.
|
||||||
WebRtc_Word32 SendCodec(CodecInst& current_codec) const;
|
WebRtc_Word32 SendCodec(CodecInst& current_codec) const;
|
||||||
|
|
||||||
@ -248,6 +260,35 @@ class AudioCodingModuleImpl : public AudioCodingModule {
|
|||||||
WebRtc_Word16 mirror_id,
|
WebRtc_Word16 mirror_id,
|
||||||
ACMNetEQ::JB jitter_buffer);
|
ACMNetEQ::JB jitter_buffer);
|
||||||
|
|
||||||
|
// Set VAD/DTX status. This function does not acquire a lock, and it is
|
||||||
|
// created to be called only from inside a critical section.
|
||||||
|
int SetVADSafe(bool enable_dtx, bool enable_vad, ACMVADMode mode);
|
||||||
|
|
||||||
|
// Process buffered audio when dual-streaming is not enabled (When RED is
|
||||||
|
// enabled still this function is used.)
|
||||||
|
int ProcessSingleStream();
|
||||||
|
|
||||||
|
// Process buffered audio when dual-streaming is enabled, i.e. secondary send
|
||||||
|
// codec is registered.
|
||||||
|
int ProcessDualStream();
|
||||||
|
|
||||||
|
// Preprocessing of input audio, including resampling and down-mixing if
|
||||||
|
// required, before pushing audio into encoder'r buffer.
|
||||||
|
//
|
||||||
|
// in_frame: input audio-frame
|
||||||
|
// out_frame: output audio_frame, the output is valid only if a preprocessing
|
||||||
|
// is required.
|
||||||
|
//
|
||||||
|
// Return value:
|
||||||
|
// -1: if encountering an error.
|
||||||
|
// kPreprocessingSuccessful: if a preprocessing successfully performed.
|
||||||
|
// kNoPreprocessingRequired: if there was no need for preprocessing. In
|
||||||
|
// this case |out_frame| is not updated and
|
||||||
|
// |in_frame| has to be used for further
|
||||||
|
// operations.
|
||||||
|
int PreprocessToAddData(const AudioFrame& in_frame,
|
||||||
|
const AudioFrame** ptr_out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Change required states after starting to receive the codec corresponding
|
// Change required states after starting to receive the codec corresponding
|
||||||
// to |index|.
|
// to |index|.
|
||||||
@ -261,15 +302,12 @@ class AudioCodingModuleImpl : public AudioCodingModule {
|
|||||||
// is a stereo codec, RED or CN.
|
// is a stereo codec, RED or CN.
|
||||||
bool IsCodecForSlave(int index) const;
|
bool IsCodecForSlave(int index) const;
|
||||||
|
|
||||||
// Returns true if the |codec| is RED.
|
int EncodeFragmentation(int fragmentation_index, int payload_type,
|
||||||
bool IsCodecRED(const CodecInst* codec) const;
|
uint32_t current_timestamp,
|
||||||
// ...or if its |index| is RED.
|
ACMGenericCodec* _secondary_encoder,
|
||||||
bool IsCodecRED(int index) const;
|
uint8_t* stream);
|
||||||
|
|
||||||
// Returns true if the |codec| is CN.
|
void ResetFragmentation(int vector_size);
|
||||||
bool IsCodecCN(int index) const;
|
|
||||||
// ...or if its |index| is CN.
|
|
||||||
bool IsCodecCN(const CodecInst* codec) const;
|
|
||||||
|
|
||||||
AudioPacketizationCallback* _packetizationCallback;
|
AudioPacketizationCallback* _packetizationCallback;
|
||||||
WebRtc_Word32 _id;
|
WebRtc_Word32 _id;
|
||||||
@ -305,8 +343,15 @@ class AudioCodingModuleImpl : public AudioCodingModule {
|
|||||||
// RED/FEC.
|
// RED/FEC.
|
||||||
bool _isFirstRED;
|
bool _isFirstRED;
|
||||||
bool _fecEnabled;
|
bool _fecEnabled;
|
||||||
|
// TODO(turajs): |_redBuffer| is allocated in constructor, why having them
|
||||||
|
// as pointers and not an array. If concerned about the memory, then make a
|
||||||
|
// set-up function to allocate them only when they are going to be used, i.e.
|
||||||
|
// FEC or Dual-streaming is enabled.
|
||||||
WebRtc_UWord8* _redBuffer;
|
WebRtc_UWord8* _redBuffer;
|
||||||
RTPFragmentationHeader* _fragmentation;
|
// TODO(turajs): we actually don't need |_fragmentation| as a member variable.
|
||||||
|
// It is sufficient to keep the length & payload type of previous payload in
|
||||||
|
// member variables.
|
||||||
|
RTPFragmentationHeader _fragmentation;
|
||||||
WebRtc_UWord32 _lastFECTimestamp;
|
WebRtc_UWord32 _lastFECTimestamp;
|
||||||
// If no RED is registered as receive codec this
|
// If no RED is registered as receive codec this
|
||||||
// will have an invalid value.
|
// will have an invalid value.
|
||||||
@ -334,7 +379,9 @@ class AudioCodingModuleImpl : public AudioCodingModule {
|
|||||||
CriticalSectionWrapper* _callbackCritSect;
|
CriticalSectionWrapper* _callbackCritSect;
|
||||||
|
|
||||||
AudioFrame _audioFrame;
|
AudioFrame _audioFrame;
|
||||||
|
AudioFrame _preprocess_frame;
|
||||||
|
CodecInst _secondarySendCodecInst;
|
||||||
|
scoped_ptr<ACMGenericCodec> _secondaryEncoder;
|
||||||
#ifdef ACM_QA_TEST
|
#ifdef ACM_QA_TEST
|
||||||
FILE* _outgoingPL;
|
FILE* _outgoingPL;
|
||||||
FILE* _incomingPL;
|
FILE* _incomingPL;
|
||||||
|
515
webrtc/modules/audio_coding/main/test/dual_stream_unittest.cc
Normal file
515
webrtc/modules/audio_coding/main/test/dual_stream_unittest.cc
Normal file
@ -0,0 +1,515 @@
|
|||||||
|
/*
|
||||||
|
* 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 "../source/acm_common_defs.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "audio_coding_module.h"
|
||||||
|
#include "PCMFile.h"
|
||||||
|
#include "module_common_types.h"
|
||||||
|
#include "scoped_ptr.h"
|
||||||
|
#include "testsupport/fileutils.h"
|
||||||
|
#include "typedefs.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class DualStreamTest : public AudioPacketizationCallback,
|
||||||
|
public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
DualStreamTest();
|
||||||
|
~DualStreamTest();
|
||||||
|
|
||||||
|
WebRtc_Word32 SendData(FrameType frameType, WebRtc_UWord8 payload_type,
|
||||||
|
WebRtc_UWord32 timestamp,
|
||||||
|
const WebRtc_UWord8* payload_data,
|
||||||
|
WebRtc_UWord16 payload_size,
|
||||||
|
const RTPFragmentationHeader* fragmentation);
|
||||||
|
|
||||||
|
void Perform(bool start_in_sync, int num_channels_input);
|
||||||
|
|
||||||
|
void InitializeSender(int frame_size_primary_samples,
|
||||||
|
int num_channels_primary,
|
||||||
|
int sampling_rate);
|
||||||
|
|
||||||
|
void PopulateCodecInstances(int frame_size_primary_ms,
|
||||||
|
int num_channels_primary,
|
||||||
|
int sampling_rate);
|
||||||
|
|
||||||
|
void Validate(bool start_in_sync, int tolerance);
|
||||||
|
bool EqualTimestamp(int stream, int position);
|
||||||
|
int EqualPayloadLength(int stream, int position);
|
||||||
|
bool EqualPayloadData(int stream, int position);
|
||||||
|
|
||||||
|
static const int kMaxNumStoredPayloads = 2;
|
||||||
|
|
||||||
|
enum {kPrimary = 0, kSecondary, kMaxNumStreams};
|
||||||
|
|
||||||
|
AudioCodingModule* acm_dual_stream_;
|
||||||
|
AudioCodingModule* acm_ref_primary_;
|
||||||
|
AudioCodingModule* acm_ref_secondary_;
|
||||||
|
|
||||||
|
CodecInst primary_encoder_;
|
||||||
|
CodecInst secondary_encoder_;
|
||||||
|
|
||||||
|
CodecInst red_encoder_;
|
||||||
|
|
||||||
|
int payload_ref_is_stored_[kMaxNumStreams][kMaxNumStoredPayloads];
|
||||||
|
int payload_dual_is_stored_[kMaxNumStreams][kMaxNumStoredPayloads];
|
||||||
|
|
||||||
|
uint32_t timestamp_ref_[kMaxNumStreams][kMaxNumStoredPayloads];
|
||||||
|
uint32_t timestamp_dual_[kMaxNumStreams][kMaxNumStoredPayloads];
|
||||||
|
|
||||||
|
int payload_len_ref_[kMaxNumStreams][kMaxNumStoredPayloads];
|
||||||
|
int payload_len_dual_[kMaxNumStreams][kMaxNumStoredPayloads];
|
||||||
|
|
||||||
|
uint8_t payload_data_ref_[kMaxNumStreams]
|
||||||
|
[MAX_PAYLOAD_SIZE_BYTE * kMaxNumStoredPayloads];
|
||||||
|
uint8_t payload_data_dual_[kMaxNumStreams]
|
||||||
|
[MAX_PAYLOAD_SIZE_BYTE * kMaxNumStoredPayloads];
|
||||||
|
int num_received_payloads_dual_[kMaxNumStreams];
|
||||||
|
int num_received_payloads_ref_[kMaxNumStreams];
|
||||||
|
|
||||||
|
int num_compared_payloads_[kMaxNumStreams];
|
||||||
|
uint32_t last_timestamp_[kMaxNumStreams];
|
||||||
|
bool received_payload_[kMaxNumStreams];
|
||||||
|
};
|
||||||
|
|
||||||
|
DualStreamTest::DualStreamTest()
|
||||||
|
: acm_dual_stream_(AudioCodingModule::Create(0)),
|
||||||
|
acm_ref_primary_(AudioCodingModule::Create(1)),
|
||||||
|
acm_ref_secondary_(AudioCodingModule::Create(2)),
|
||||||
|
payload_ref_is_stored_(),
|
||||||
|
payload_dual_is_stored_(),
|
||||||
|
timestamp_ref_(),
|
||||||
|
num_received_payloads_dual_(),
|
||||||
|
num_received_payloads_ref_(),
|
||||||
|
num_compared_payloads_(),
|
||||||
|
last_timestamp_(),
|
||||||
|
received_payload_() {}
|
||||||
|
|
||||||
|
DualStreamTest::~DualStreamTest() {
|
||||||
|
AudioCodingModule::Destroy(acm_dual_stream_);
|
||||||
|
AudioCodingModule::Destroy(acm_ref_primary_);
|
||||||
|
AudioCodingModule::Destroy(acm_ref_secondary_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DualStreamTest::PopulateCodecInstances(int frame_size_primary_ms,
|
||||||
|
int num_channels_primary,
|
||||||
|
int sampling_rate) {
|
||||||
|
CodecInst my_codec;
|
||||||
|
|
||||||
|
// Invalid values. To check later on if the codec are found in the database.
|
||||||
|
primary_encoder_.pltype = -1;
|
||||||
|
secondary_encoder_.pltype = -1;
|
||||||
|
red_encoder_.pltype = -1;
|
||||||
|
|
||||||
|
for (int n = 0; n < AudioCodingModule::NumberOfCodecs(); n++) {
|
||||||
|
AudioCodingModule::Codec(n, my_codec);
|
||||||
|
if (strcmp(my_codec.plname, "ISAC") == 0 &&
|
||||||
|
my_codec.plfreq == sampling_rate) {
|
||||||
|
my_codec.rate = 32000;
|
||||||
|
my_codec.pacsize = 30 * sampling_rate / 1000;
|
||||||
|
memcpy(&secondary_encoder_, &my_codec, sizeof(my_codec));
|
||||||
|
} else if (strcmp(my_codec.plname, "L16") == 0 &&
|
||||||
|
my_codec.channels == num_channels_primary &&
|
||||||
|
my_codec.plfreq == sampling_rate) {
|
||||||
|
my_codec.pacsize = frame_size_primary_ms * sampling_rate / 1000;
|
||||||
|
memcpy(&primary_encoder_, &my_codec, sizeof(my_codec));
|
||||||
|
} else if (strcmp(my_codec.plname, "red") == 0) {
|
||||||
|
memcpy(&red_encoder_, &my_codec, sizeof(my_codec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_GE(primary_encoder_.pltype, 0);
|
||||||
|
ASSERT_GE(secondary_encoder_.pltype, 0);
|
||||||
|
ASSERT_GE(red_encoder_.pltype, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DualStreamTest::InitializeSender(int frame_size_primary_samples,
|
||||||
|
int num_channels_primary,
|
||||||
|
int sampling_rate) {
|
||||||
|
ASSERT_TRUE(acm_dual_stream_ != NULL);
|
||||||
|
ASSERT_TRUE(acm_ref_primary_ != NULL);
|
||||||
|
ASSERT_TRUE(acm_ref_secondary_ != NULL);
|
||||||
|
|
||||||
|
ASSERT_EQ(0, acm_dual_stream_->InitializeSender());
|
||||||
|
ASSERT_EQ(0, acm_ref_primary_->InitializeSender());
|
||||||
|
ASSERT_EQ(0, acm_ref_secondary_->InitializeSender());
|
||||||
|
|
||||||
|
PopulateCodecInstances(frame_size_primary_samples, num_channels_primary,
|
||||||
|
sampling_rate);
|
||||||
|
|
||||||
|
ASSERT_EQ(0, acm_ref_primary_->RegisterSendCodec(primary_encoder_));
|
||||||
|
ASSERT_EQ(0, acm_ref_secondary_->RegisterSendCodec(secondary_encoder_));
|
||||||
|
ASSERT_EQ(0, acm_dual_stream_->RegisterSendCodec(primary_encoder_));
|
||||||
|
ASSERT_EQ(0,
|
||||||
|
acm_dual_stream_->RegisterSecondarySendCodec(secondary_encoder_));
|
||||||
|
|
||||||
|
ASSERT_EQ(0, acm_ref_primary_->RegisterTransportCallback(this));
|
||||||
|
ASSERT_EQ(0, acm_ref_secondary_->RegisterTransportCallback(this));
|
||||||
|
ASSERT_EQ(0, acm_dual_stream_->RegisterTransportCallback(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DualStreamTest::Perform(bool start_in_sync, int num_channels_input) {
|
||||||
|
PCMFile pcm_file;
|
||||||
|
std::string file_name = test::ResourcePath(
|
||||||
|
(num_channels_input == 1) ? "audio_coding/testfile32kHz" :
|
||||||
|
"audio_coding/teststereo32kHz", "pcm");
|
||||||
|
pcm_file.Open(file_name, 32000, "rb");
|
||||||
|
pcm_file.ReadStereo(num_channels_input == 2);
|
||||||
|
AudioFrame audio_frame;
|
||||||
|
|
||||||
|
int tolerance = 0;
|
||||||
|
if (num_channels_input == 2 && primary_encoder_.channels == 2 &&
|
||||||
|
secondary_encoder_.channels == 1) {
|
||||||
|
tolerance = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!start_in_sync) {
|
||||||
|
pcm_file.Read10MsData(audio_frame);
|
||||||
|
// Unregister secondary codec and feed only the primary
|
||||||
|
acm_dual_stream_->UnregisterSecondarySendCodec();
|
||||||
|
EXPECT_EQ(0, acm_dual_stream_->Add10MsData(audio_frame));
|
||||||
|
EXPECT_EQ(0, acm_ref_primary_->Add10MsData(audio_frame));
|
||||||
|
ASSERT_EQ(0,
|
||||||
|
acm_dual_stream_->RegisterSecondarySendCodec(secondary_encoder_));
|
||||||
|
}
|
||||||
|
|
||||||
|
const int kNumFramesToProcess = 100;
|
||||||
|
int frame_cntr = 0;
|
||||||
|
while (!pcm_file.EndOfFile() && frame_cntr < kNumFramesToProcess) {
|
||||||
|
pcm_file.Read10MsData(audio_frame);
|
||||||
|
frame_cntr++;
|
||||||
|
EXPECT_EQ(0, acm_dual_stream_->Add10MsData(audio_frame));
|
||||||
|
EXPECT_EQ(0, acm_ref_primary_->Add10MsData(audio_frame));
|
||||||
|
EXPECT_EQ(0, acm_ref_secondary_->Add10MsData(audio_frame));
|
||||||
|
|
||||||
|
EXPECT_GE(acm_dual_stream_->Process(), 0);
|
||||||
|
EXPECT_GE(acm_ref_primary_->Process(), 0);
|
||||||
|
EXPECT_GE(acm_ref_secondary_->Process(), 0);
|
||||||
|
|
||||||
|
if (start_in_sync || frame_cntr > 7) {
|
||||||
|
// If we haven't started in sync the first few audio frames might
|
||||||
|
// slightly differ due to the difference in the state of the resamplers
|
||||||
|
// of dual-ACM and reference-ACM.
|
||||||
|
Validate(start_in_sync, tolerance);
|
||||||
|
} else {
|
||||||
|
// SendData stores the payloads, if we are not comparing we have to free
|
||||||
|
// the space by resetting these flags.
|
||||||
|
memset(payload_ref_is_stored_, 0, sizeof(payload_ref_is_stored_));
|
||||||
|
memset(payload_dual_is_stored_, 0, sizeof(payload_dual_is_stored_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pcm_file.Close();
|
||||||
|
|
||||||
|
// Make sure that number of received payloads match. In case of secondary
|
||||||
|
// encoder, the dual-stream might deliver one lesser payload. The reason is
|
||||||
|
// that some secondary payloads are stored to be sent with a payload generated
|
||||||
|
// later and the input file may end before the "next" payload .
|
||||||
|
EXPECT_EQ(num_received_payloads_ref_[kPrimary],
|
||||||
|
num_received_payloads_dual_[kPrimary]);
|
||||||
|
EXPECT_TRUE(num_received_payloads_ref_[kSecondary] ==
|
||||||
|
num_received_payloads_dual_[kSecondary] ||
|
||||||
|
num_received_payloads_ref_[kSecondary] ==
|
||||||
|
(num_received_payloads_dual_[kSecondary] + 1));
|
||||||
|
|
||||||
|
// Make sure all received payloads are compared.
|
||||||
|
if (start_in_sync) {
|
||||||
|
EXPECT_EQ(num_received_payloads_dual_[kPrimary],
|
||||||
|
num_compared_payloads_[kPrimary]);
|
||||||
|
EXPECT_EQ(num_received_payloads_dual_[kSecondary],
|
||||||
|
num_compared_payloads_[kSecondary]);
|
||||||
|
} else {
|
||||||
|
// In asynchronous test we don't compare couple of first frames, so we
|
||||||
|
// should account for them in our counting.
|
||||||
|
EXPECT_GE(num_compared_payloads_[kPrimary],
|
||||||
|
num_received_payloads_dual_[kPrimary] - 4);
|
||||||
|
EXPECT_GE(num_compared_payloads_[kSecondary],
|
||||||
|
num_received_payloads_dual_[kSecondary] - 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DualStreamTest::EqualTimestamp(int stream_index, int position) {
|
||||||
|
if (timestamp_dual_[stream_index][position] !=
|
||||||
|
timestamp_ref_[stream_index][position]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DualStreamTest::EqualPayloadLength(int stream_index, int position) {
|
||||||
|
return abs(payload_len_dual_[stream_index][position] -
|
||||||
|
payload_len_ref_[stream_index][position]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DualStreamTest::EqualPayloadData(int stream_index, int position) {
|
||||||
|
assert(payload_len_dual_[stream_index][position] ==
|
||||||
|
payload_len_ref_[stream_index][position]);
|
||||||
|
int offset = position * MAX_PAYLOAD_SIZE_BYTE;
|
||||||
|
for (int n = 0; n < payload_len_dual_[stream_index][position]; n++) {
|
||||||
|
if (payload_data_dual_[stream_index][offset + n] !=
|
||||||
|
payload_data_ref_[stream_index][offset + n]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DualStreamTest::Validate(bool start_in_sync, int tolerance) {
|
||||||
|
for (int stream_index = 0; stream_index < kMaxNumStreams; stream_index++) {
|
||||||
|
int my_tolerance = stream_index == kPrimary ? 0 : tolerance;
|
||||||
|
for (int position = 0; position < kMaxNumStoredPayloads; position++) {
|
||||||
|
if (payload_ref_is_stored_[stream_index][position] == 1 &&
|
||||||
|
payload_dual_is_stored_[stream_index][position] == 1) {
|
||||||
|
// Check timestamps only if codecs started in sync or it is primary.
|
||||||
|
if (start_in_sync || stream_index == 0)
|
||||||
|
EXPECT_TRUE(EqualTimestamp(stream_index, position));
|
||||||
|
EXPECT_LE(EqualPayloadLength(stream_index, position), my_tolerance);
|
||||||
|
if (my_tolerance == 0)
|
||||||
|
EXPECT_TRUE(EqualPayloadData(stream_index, position));
|
||||||
|
num_compared_payloads_[stream_index]++;
|
||||||
|
payload_ref_is_stored_[stream_index][position] = 0;
|
||||||
|
payload_dual_is_stored_[stream_index][position] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebRtc_Word32 DualStreamTest::SendData(
|
||||||
|
FrameType frameType, WebRtc_UWord8 payload_type, WebRtc_UWord32 timestamp,
|
||||||
|
const WebRtc_UWord8* payload_data, WebRtc_UWord16 payload_size,
|
||||||
|
const RTPFragmentationHeader* fragmentation) {
|
||||||
|
int position;
|
||||||
|
int stream_index;
|
||||||
|
|
||||||
|
if (payload_type == red_encoder_.pltype) {
|
||||||
|
if (fragmentation == NULL) {
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// As the oldest payloads are in the higher indices of fragmentation,
|
||||||
|
// to be able to check the increment of timestamps are correct we loop
|
||||||
|
// backward.
|
||||||
|
for (int n = fragmentation->fragmentationVectorSize - 1; n >= 0 ; --n) {
|
||||||
|
if (fragmentation->fragmentationPlType[n] == primary_encoder_.pltype) {
|
||||||
|
// Received primary payload from dual stream.
|
||||||
|
stream_index = kPrimary;
|
||||||
|
} else if (fragmentation->fragmentationPlType[n] ==
|
||||||
|
secondary_encoder_.pltype) {
|
||||||
|
// Received secondary payload from dual stream.
|
||||||
|
stream_index = kSecondary;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
num_received_payloads_dual_[stream_index]++;
|
||||||
|
if (payload_dual_is_stored_[stream_index][0] == 0) {
|
||||||
|
position = 0;
|
||||||
|
} else if (payload_dual_is_stored_[stream_index][1] == 0) {
|
||||||
|
position = 1;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
timestamp_dual_[stream_index][position] = timestamp -
|
||||||
|
fragmentation->fragmentationTimeDiff[n];
|
||||||
|
payload_len_dual_[stream_index][position] =
|
||||||
|
fragmentation->fragmentationLength[n];
|
||||||
|
memcpy(
|
||||||
|
&payload_data_dual_[stream_index][position * MAX_PAYLOAD_SIZE_BYTE],
|
||||||
|
&payload_data[fragmentation->fragmentationOffset[n]],
|
||||||
|
fragmentation->fragmentationLength[n]);
|
||||||
|
payload_dual_is_stored_[stream_index][position] = 1;
|
||||||
|
// Check if timestamps are incremented correctly.
|
||||||
|
if (received_payload_[stream_index]) {
|
||||||
|
int t = timestamp_dual_[stream_index][position] -
|
||||||
|
last_timestamp_[stream_index];
|
||||||
|
if ((stream_index == kPrimary) && (t != primary_encoder_.pacsize)) {
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((stream_index == kSecondary) && (t != secondary_encoder_.pacsize)) {
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
received_payload_[stream_index] = true;
|
||||||
|
}
|
||||||
|
last_timestamp_[stream_index] = timestamp_dual_[stream_index][position];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fragmentation != NULL) {
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (payload_type == primary_encoder_.pltype) {
|
||||||
|
stream_index = kPrimary;
|
||||||
|
} else if (payload_type == secondary_encoder_.pltype) {
|
||||||
|
stream_index = kSecondary;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
num_received_payloads_ref_[stream_index]++;
|
||||||
|
if (payload_ref_is_stored_[stream_index][0] == 0) {
|
||||||
|
position = 0;
|
||||||
|
} else if (payload_ref_is_stored_[stream_index][1] == 0) {
|
||||||
|
position = 1;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
timestamp_ref_[stream_index][position] = timestamp;
|
||||||
|
payload_len_ref_[stream_index][position] = payload_size;
|
||||||
|
memcpy(&payload_data_ref_[stream_index][position * MAX_PAYLOAD_SIZE_BYTE],
|
||||||
|
payload_data, payload_size);
|
||||||
|
payload_ref_is_stored_[stream_index][position] = 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono input, mono primary WB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncMonoInputMonoPrimaryWb20Ms) {
|
||||||
|
InitializeSender(20, 1, 16000);
|
||||||
|
Perform(true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono input, stereo primary WB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncMonoInput_StereoPrimaryWb20Ms) {
|
||||||
|
InitializeSender(20, 2, 16000);
|
||||||
|
Perform(true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono input, mono primary SWB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncMonoInputMonoPrimarySwb20Ms) {
|
||||||
|
InitializeSender(20, 1, 32000);
|
||||||
|
Perform(true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono input, stereo primary SWB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncMonoInputStereoPrimarySwb20Ms) {
|
||||||
|
InitializeSender(20, 2, 32000);
|
||||||
|
Perform(true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono input, mono primary WB 40 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncMonoInputMonoPrimaryWb40Ms) {
|
||||||
|
InitializeSender(40, 1, 16000);
|
||||||
|
Perform(true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono input, stereo primary WB 40 ms frame
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncMonoInputStereoPrimaryWb40Ms) {
|
||||||
|
InitializeSender(40, 2, 16000);
|
||||||
|
Perform(true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stereo input, mono primary WB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncStereoInputMonoPrimaryWb20Ms) {
|
||||||
|
InitializeSender(20, 1, 16000);
|
||||||
|
Perform(true, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stereo input, stereo primary WB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncStereoInputStereoPrimaryWb20Ms) {
|
||||||
|
InitializeSender(20, 2, 16000);
|
||||||
|
Perform(true, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stereo input, mono primary SWB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncStereoInputMonoPrimarySwb20Ms) {
|
||||||
|
InitializeSender(20, 1, 32000);
|
||||||
|
Perform(true, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stereo input, stereo primary SWB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncStereoInputStereoPrimarySwb20Ms) {
|
||||||
|
InitializeSender(20, 2, 32000);
|
||||||
|
Perform(true, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stereo input, mono primary WB 40 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncStereoInputMonoPrimaryWb40Ms) {
|
||||||
|
InitializeSender(40, 1, 16000);
|
||||||
|
Perform(true, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stereo input, stereo primary WB 40 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactSyncStereoInputStereoPrimaryWb40Ms) {
|
||||||
|
InitializeSender(40, 2, 16000);
|
||||||
|
Perform(true, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asynchronous test, ACM is fed with data then secondary coder is registered.
|
||||||
|
// Mono input, mono primary WB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactAsyncMonoInputMonoPrimaryWb20Ms) {
|
||||||
|
InitializeSender(20, 1, 16000);
|
||||||
|
Perform(false, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono input, mono primary WB 20 ms frame.
|
||||||
|
TEST_F(DualStreamTest, BitExactAsyncMonoInputMonoPrimaryWb40Ms) {
|
||||||
|
InitializeSender(40, 1, 16000);
|
||||||
|
Perform(false, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(DualStreamTest, Api) {
|
||||||
|
PopulateCodecInstances(20, 1, 16000);
|
||||||
|
CodecInst my_codec;
|
||||||
|
ASSERT_EQ(0, acm_dual_stream_->InitializeSender());
|
||||||
|
ASSERT_EQ(-1, acm_dual_stream_->SecondarySendCodec(&my_codec));
|
||||||
|
|
||||||
|
// Not allowed to register secondary codec if primary is not registered yet.
|
||||||
|
ASSERT_EQ(-1,
|
||||||
|
acm_dual_stream_->RegisterSecondarySendCodec(secondary_encoder_));
|
||||||
|
ASSERT_EQ(-1, acm_dual_stream_->SecondarySendCodec(&my_codec));
|
||||||
|
|
||||||
|
ASSERT_EQ(0, acm_dual_stream_->RegisterSendCodec(primary_encoder_));
|
||||||
|
|
||||||
|
ASSERT_EQ(0, acm_dual_stream_->SetVAD(true, true, VADNormal));
|
||||||
|
|
||||||
|
// Make sure vad is activated.
|
||||||
|
bool vad_status;
|
||||||
|
bool dtx_status;
|
||||||
|
ACMVADMode vad_mode;
|
||||||
|
EXPECT_EQ(0, acm_dual_stream_->VAD(vad_status, dtx_status, vad_mode));
|
||||||
|
EXPECT_TRUE(vad_status);
|
||||||
|
EXPECT_TRUE(dtx_status);
|
||||||
|
EXPECT_EQ(VADNormal, vad_mode);
|
||||||
|
|
||||||
|
ASSERT_EQ(0,
|
||||||
|
acm_dual_stream_->RegisterSecondarySendCodec(secondary_encoder_));
|
||||||
|
|
||||||
|
ASSERT_EQ(0, acm_dual_stream_->SecondarySendCodec(&my_codec));
|
||||||
|
ASSERT_EQ(0, memcmp(&my_codec, &secondary_encoder_, sizeof(my_codec)));
|
||||||
|
|
||||||
|
// Test if VAD get disabled after registering secondary codec.
|
||||||
|
EXPECT_EQ(0, acm_dual_stream_->VAD(vad_status, dtx_status, vad_mode));
|
||||||
|
EXPECT_FALSE(vad_status);
|
||||||
|
EXPECT_FALSE(dtx_status);
|
||||||
|
|
||||||
|
// Activating VAD should fail.
|
||||||
|
ASSERT_EQ(-1, acm_dual_stream_->SetVAD(true, true, VADNormal));
|
||||||
|
|
||||||
|
// Unregister secondary encoder and it should be possible to activate VAD.
|
||||||
|
acm_dual_stream_->UnregisterSecondarySendCodec();
|
||||||
|
// Should fail.
|
||||||
|
ASSERT_EQ(-1, acm_dual_stream_->SecondarySendCodec(&my_codec));
|
||||||
|
|
||||||
|
ASSERT_EQ(0, acm_dual_stream_->SetVAD(true, true, VADVeryAggr));
|
||||||
|
// Make sure VAD is activated.
|
||||||
|
EXPECT_EQ(0, acm_dual_stream_->VAD(vad_status, dtx_status, vad_mode));
|
||||||
|
EXPECT_TRUE(vad_status);
|
||||||
|
EXPECT_TRUE(dtx_status);
|
||||||
|
EXPECT_EQ(VADVeryAggr, vad_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
Loading…
x
Reference in New Issue
Block a user