1099 lines
30 KiB
C++
1099 lines
30 KiB
C++
/*
|
|
* Copyright (c) 2011 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 <assert.h>
|
|
#include <iostream>
|
|
#include <time.h>
|
|
|
|
#include "functionTest.h"
|
|
#include "event_wrapper.h"
|
|
#include "trace.h"
|
|
#include "thread_wrapper.h"
|
|
#include "webrtc_vad.h"
|
|
|
|
#if (defined(WEBRTC_LINUX) || defined(WEBRTC_MAC))
|
|
#include <sys/stat.h>
|
|
#define MY_PERMISSION_MASK S_IRWXU | S_IRWXG | S_IRWXO
|
|
#define MKDIR(directory) mkdir(directory,MY_PERMISSION_MASK)
|
|
#else // defined(WINDOWS)
|
|
#include <direct.h>
|
|
#define MKDIR(directory) mkdir(directory)
|
|
#endif
|
|
|
|
int main(int /*argc*/, char* /*argv[]*/)
|
|
{
|
|
// Initialize random number generator
|
|
//unsigned int seed = 1220716312; // just a seed that can be used
|
|
unsigned int seed = (unsigned)time( NULL );
|
|
srand(seed);
|
|
std::cout << "Starting function test. Seed = " << seed << std::endl;
|
|
std::cout << "Press enter to continue" << std::endl;
|
|
getchar();
|
|
MixerWrapper* testInstance1 = MixerWrapper::CreateMixerWrapper();
|
|
MixerWrapper* testInstance2 = MixerWrapper::CreateMixerWrapper();
|
|
if((testInstance1 == NULL) ||
|
|
(testInstance2 == NULL))
|
|
{
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
|
|
char versionString[256] = "";
|
|
WebRtc_UWord32 remainingBufferInBytes = 256;
|
|
WebRtc_UWord32 position = 0;
|
|
AudioConferenceMixer::GetVersion(versionString,remainingBufferInBytes,position);
|
|
|
|
int read = 1;
|
|
while(read != 0)
|
|
{
|
|
std::cout << versionString << std::endl;
|
|
std::cout << "--------Menu-----------" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "0. Quit" << std::endl;
|
|
std::cout << "2. StartMixing" << std::endl;
|
|
std::cout << "3. StopMixing" << std::endl;
|
|
std::cout << "4. Create participant(s)" << std::endl;
|
|
std::cout << "5. Delete participant(s)" << std::endl;
|
|
std::cout << "6. List participants " << std::endl;
|
|
std::cout << "7. Print mix status " << std::endl;
|
|
std::cout << "8. Run identical scenario:" << std::endl;
|
|
std::cout << " a. 1 VIP, 3 regular, amount of mixed = 3" << std::endl;
|
|
std::cout << " b. 1 anonymous, 3 regular, amount of mixed = 2" << std::endl;
|
|
scanf("%i",&read);
|
|
getchar();
|
|
MixerParticipant::ParticipantType participantType;
|
|
int option = 0;
|
|
WebRtc_UWord32 id = 0;
|
|
ListItem* item = NULL;
|
|
ListWrapper participants;
|
|
if(read == 0)
|
|
{
|
|
// donothing
|
|
}
|
|
else if(read == 1)
|
|
{
|
|
}
|
|
else if(read == 2)
|
|
{
|
|
testInstance1->StartMixing();
|
|
}
|
|
else if(read == 3)
|
|
{
|
|
testInstance1->StopMixing();
|
|
}
|
|
else if(read == 4)
|
|
{
|
|
while(true)
|
|
{
|
|
std::cout << "VIP(music) = " << MixerParticipant::VIP << std::endl;
|
|
std::cout << "Regular(speech) = " << MixerParticipant::REGULAR << std::endl;
|
|
std::cout << "Anonymous(music) = " << MixerParticipant::MIXED_ANONYMOUS << std::endl;
|
|
std::cout << "Select type of participant: ";
|
|
scanf("%i",&option);
|
|
if(option == MixerParticipant::VIP ||
|
|
option == MixerParticipant::REGULAR ||
|
|
option == MixerParticipant::MIXED_ANONYMOUS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
participantType = (MixerParticipant::ParticipantType)option;
|
|
testInstance1->CreateParticipant(participantType);
|
|
}
|
|
else if(read == 5)
|
|
{
|
|
std::cout << "Select participant to delete: ";
|
|
scanf("%i",&option);
|
|
id = option;
|
|
testInstance1->DeleteParticipant(id);
|
|
break;
|
|
}
|
|
else if(read == 6)
|
|
{
|
|
testInstance1->GetParticipantList(participants);
|
|
item = participants.First();
|
|
std::cout << "The following participants have been created: " << std::endl;
|
|
while(item)
|
|
{
|
|
WebRtc_UWord32 id = item->GetUnsignedItem();
|
|
std::cout << id;
|
|
item = participants.Next(item);
|
|
if(item != NULL)
|
|
{
|
|
std::cout << ", ";
|
|
}
|
|
else
|
|
{
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
}
|
|
else if(read == 7)
|
|
{
|
|
std::cout << "-------------Mixer Status-------------" << std::endl;
|
|
testInstance1->PrintStatus();
|
|
testInstance2->PrintStatus();
|
|
std::cout << "Press enter to continue";
|
|
getchar();
|
|
std::cout << std::endl;
|
|
std::cout << std::endl;
|
|
}
|
|
else if(read == 8)
|
|
{
|
|
const WebRtc_Word32 amountOfParticipants = 4;
|
|
MixerParticipant::ParticipantType instance1Participants[] =
|
|
{MixerParticipant::VIP,
|
|
MixerParticipant::REGULAR,
|
|
MixerParticipant::REGULAR,
|
|
MixerParticipant::REGULAR};
|
|
MixerParticipant::ParticipantType instance2Participants[] =
|
|
{MixerParticipant::MIXED_ANONYMOUS,
|
|
MixerParticipant::REGULAR,
|
|
MixerParticipant::REGULAR,
|
|
MixerParticipant::REGULAR};
|
|
for(WebRtc_Word32 i = 0; i < amountOfParticipants; i++)
|
|
{
|
|
WebRtc_Word32 startPosition = 0;
|
|
GenerateRandomPosition(startPosition);
|
|
testInstance1->CreateParticipant(instance1Participants[i],startPosition);
|
|
testInstance2->CreateParticipant(instance2Participants[i],startPosition);
|
|
}
|
|
bool success = true;
|
|
success = testInstance1->StartMixing();
|
|
assert(success);
|
|
success = testInstance2->StartMixing(2);
|
|
assert(success);
|
|
}
|
|
}
|
|
|
|
std::cout << "Press enter to stop" << std::endl;
|
|
getchar();
|
|
delete testInstance1;
|
|
delete testInstance2;
|
|
return 0;
|
|
}
|
|
|
|
FileWriter::FileWriter()
|
|
:
|
|
_file(NULL)
|
|
{
|
|
}
|
|
|
|
FileWriter::~FileWriter()
|
|
{
|
|
if(_file)
|
|
{
|
|
fclose(_file);
|
|
}
|
|
}
|
|
|
|
bool
|
|
FileWriter::SetFileName(
|
|
const char* fileName)
|
|
{
|
|
if(_file)
|
|
{
|
|
fclose(_file);
|
|
}
|
|
_file = fopen(fileName,"wb");
|
|
return _file != NULL;
|
|
}
|
|
|
|
bool
|
|
FileWriter::WriteToFile(
|
|
const AudioFrame& audioFrame)
|
|
{
|
|
WebRtc_Word32 written = (WebRtc_Word32)fwrite(audioFrame._payloadData,sizeof(WebRtc_Word16),audioFrame._payloadDataLengthInSamples,_file);
|
|
// Do not flush buffers since that will add (a lot of) delay
|
|
return written == audioFrame._payloadDataLengthInSamples;
|
|
}
|
|
|
|
FileReader::FileReader()
|
|
:
|
|
_frequency(kDefaultFrequency),
|
|
_sampleSize((_frequency*kProcessPeriodicityInMs)/1000),
|
|
_timeStamp(0),
|
|
_file(NULL),
|
|
_vadInstr(NULL),
|
|
_automaticVad(false),
|
|
_vad(false)
|
|
{
|
|
if(WebRtcVad_Create(&_vadInstr) == 0)
|
|
{
|
|
if(WebRtcVad_Init(_vadInstr) != 0)
|
|
{
|
|
assert(false);
|
|
WebRtcVad_Free(_vadInstr);
|
|
_vadInstr = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
FileReader::~FileReader()
|
|
{
|
|
if(_file)
|
|
{
|
|
fclose(_file);
|
|
}
|
|
if(_vadInstr)
|
|
{
|
|
WebRtcVad_Free(_vadInstr);
|
|
}
|
|
}
|
|
|
|
bool
|
|
FileReader::SetFileName(
|
|
const char* fileName)
|
|
{
|
|
if(_file)
|
|
{
|
|
fclose(_file);
|
|
}
|
|
_file = fopen(fileName,"rb");
|
|
return _file != NULL;
|
|
}
|
|
|
|
bool
|
|
FileReader::ReadFromFile(
|
|
AudioFrame& audioFrame)
|
|
{
|
|
|
|
WebRtc_Word16 buffer[AudioFrame::kMaxAudioFrameSizeSamples];
|
|
LoopedFileRead(buffer,AudioFrame::kMaxAudioFrameSizeSamples,_sampleSize,_file);
|
|
|
|
bool vad = false;
|
|
GetVAD(buffer,_sampleSize,vad);
|
|
AudioFrame::VADActivity activity = vad ? AudioFrame::kVadActive :
|
|
AudioFrame::kVadPassive;
|
|
|
|
_volumeCalculator.ComputeLevel(buffer,_sampleSize);
|
|
const WebRtc_Word32 level = _volumeCalculator.GetLevel();
|
|
return audioFrame.UpdateFrame( -1,
|
|
_timeStamp,
|
|
buffer,
|
|
_sampleSize,
|
|
_frequency,
|
|
AudioFrame::kNormalSpeech,
|
|
activity,
|
|
0,
|
|
level) == 0;
|
|
|
|
}
|
|
|
|
bool
|
|
FileReader::FastForwardFile(
|
|
const WebRtc_Word32 samples)
|
|
{
|
|
WebRtc_Word16* tempBuffer = new WebRtc_Word16[samples];
|
|
bool success = LoopedFileRead(tempBuffer,samples,samples,_file);
|
|
delete[] tempBuffer;
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
FileReader::EnableAutomaticVAD(
|
|
bool enable,
|
|
int mode)
|
|
{
|
|
if(!_automaticVad &&
|
|
enable)
|
|
{
|
|
if(WebRtcVad_Init(_vadInstr) == -1)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
WebRtcVad_set_mode(_vadInstr,mode);
|
|
_automaticVad = enable;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
FileReader::SetVAD(
|
|
bool vad)
|
|
{
|
|
if(_automaticVad)
|
|
{
|
|
return false;
|
|
}
|
|
_vad = vad;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
FileReader::GetVAD(
|
|
WebRtc_Word16* buffer,
|
|
WebRtc_UWord8 bufferLengthInSamples,
|
|
bool& vad)
|
|
{
|
|
if(_automaticVad)
|
|
{
|
|
WebRtc_Word16 result = WebRtcVad_Process(_vadInstr,_frequency,buffer,bufferLengthInSamples);
|
|
if(result == -1)
|
|
{
|
|
assert(false);
|
|
return false;
|
|
}
|
|
_vad = vad = (result == 1);
|
|
}
|
|
vad = _vad;
|
|
return true;
|
|
}
|
|
|
|
MixerParticipant*
|
|
MixerParticipant::CreateParticipant(
|
|
const WebRtc_UWord32 id,
|
|
ParticipantType participantType,
|
|
const WebRtc_Word32 startPosition,
|
|
char* outputPath)
|
|
{
|
|
if(participantType == RANDOM)
|
|
{
|
|
participantType = (ParticipantType)(rand() % 3);
|
|
}
|
|
MixerParticipant* participant = new MixerParticipant(id,participantType);
|
|
// Randomize the start position so we only need one input file
|
|
// assume file is smaller than 1 minute wideband = 60 * 16000
|
|
// Always start at a multiple of 10ms wideband
|
|
if(!participant->InitializeFileReader(startPosition) ||
|
|
!participant->InitializeFileWriter(outputPath))
|
|
{
|
|
delete participant;
|
|
return NULL;
|
|
}
|
|
return participant;
|
|
}
|
|
|
|
MixerParticipant::MixerParticipant(
|
|
const WebRtc_UWord32 id,
|
|
ParticipantType participantType)
|
|
:
|
|
_id(id),
|
|
_participantType(participantType),
|
|
_fileReader(),
|
|
_fileWriter()
|
|
{
|
|
}
|
|
|
|
MixerParticipant::~MixerParticipant()
|
|
{
|
|
}
|
|
|
|
WebRtc_Word32
|
|
MixerParticipant::GetAudioFrame(
|
|
const WebRtc_Word32 /*id*/,
|
|
AudioFrame& audioFrame)
|
|
{
|
|
if(!_fileReader.ReadFromFile(audioFrame))
|
|
{
|
|
return -1;
|
|
}
|
|
audioFrame._id = _id;
|
|
return 0;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
MixerParticipant::MixedAudioFrame(
|
|
const AudioFrame& audioFrame)
|
|
{
|
|
return _fileWriter.WriteToFile(audioFrame);
|
|
}
|
|
|
|
WebRtc_Word32
|
|
MixerParticipant::GetParticipantType(
|
|
ParticipantType& participantType)
|
|
{
|
|
participantType = _participantType;
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
MixerParticipant::InitializeFileReader(
|
|
const WebRtc_Word32 startPositionInSamples)
|
|
{
|
|
char fileName[128] = "";
|
|
if(_participantType == REGULAR)
|
|
{
|
|
sprintf(fileName,"convFile.pcm");
|
|
}
|
|
else
|
|
{
|
|
sprintf(fileName,"musicFile.pcm");
|
|
}
|
|
if(!_fileReader.SetFileName(fileName))
|
|
{
|
|
return false;
|
|
}
|
|
if(!_fileReader.EnableAutomaticVAD(true,2))
|
|
{
|
|
assert(false);
|
|
}
|
|
return _fileReader.FastForwardFile(startPositionInSamples);
|
|
}
|
|
|
|
bool
|
|
MixerParticipant::InitializeFileWriter(
|
|
char* outputPath)
|
|
{
|
|
const WebRtc_Word32 stringsize = 128;
|
|
char fileName[stringsize] = "";
|
|
strncpy(fileName,outputPath,stringsize);
|
|
fileName[stringsize-1] = '\0';
|
|
|
|
char tempName[stringsize];
|
|
tempName[0] = '\0';
|
|
sprintf(tempName,"outputFile%d.pcm",(int)_id);
|
|
strncat(fileName,tempName,(stringsize - strlen(fileName)));
|
|
fileName[stringsize-1] = '\0';
|
|
|
|
return _fileWriter.SetFileName(fileName);
|
|
}
|
|
|
|
StatusReceiver::StatusReceiver(
|
|
const WebRtc_Word32 id)
|
|
:
|
|
_id(id),
|
|
_mixedParticipants(NULL),
|
|
_mixedParticipantsAmount(0),
|
|
_mixedParticipantsSize(0),
|
|
_vadPositiveParticipants(NULL),
|
|
_vadPositiveParticipantsAmount(0),
|
|
_vadPositiveParticipantsSize(0),
|
|
_mixedAudioLevel(0)
|
|
{
|
|
}
|
|
|
|
StatusReceiver::~StatusReceiver()
|
|
{
|
|
delete[] _mixedParticipants;
|
|
delete[] _vadPositiveParticipants;
|
|
}
|
|
|
|
void
|
|
StatusReceiver::MixedParticipants(
|
|
const WebRtc_Word32 id,
|
|
const ParticipantStatistics* participantStatistics,
|
|
const WebRtc_UWord32 size)
|
|
{
|
|
if(id != _id)
|
|
{
|
|
assert(false);
|
|
}
|
|
if(_mixedParticipantsSize < size)
|
|
{
|
|
delete[] _mixedParticipants;
|
|
_mixedParticipantsSize = size;
|
|
_mixedParticipants = new ParticipantStatistics[size];
|
|
}
|
|
_mixedParticipantsAmount = size;
|
|
memcpy(_mixedParticipants,participantStatistics,sizeof(ParticipantStatistics)*size);
|
|
}
|
|
|
|
void
|
|
StatusReceiver::VADPositiveParticipants(
|
|
const WebRtc_Word32 id,
|
|
const ParticipantStatistics* participantStatistics,
|
|
const WebRtc_UWord32 size)
|
|
{
|
|
if(id != _id)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
if(_vadPositiveParticipantsSize < size)
|
|
{
|
|
delete[] _vadPositiveParticipants;
|
|
_vadPositiveParticipantsSize = size;
|
|
_vadPositiveParticipants = new ParticipantStatistics[size];
|
|
}
|
|
_vadPositiveParticipantsAmount = size;
|
|
memcpy(_vadPositiveParticipants,participantStatistics,sizeof(ParticipantStatistics)*size);
|
|
}
|
|
|
|
void
|
|
StatusReceiver::MixedAudioLevel(
|
|
const WebRtc_Word32 id,
|
|
const WebRtc_UWord32 level)
|
|
{
|
|
if(id != _id)
|
|
{
|
|
assert(false);
|
|
}
|
|
_mixedAudioLevel = level;
|
|
}
|
|
|
|
void
|
|
StatusReceiver::PrintMixedParticipants()
|
|
{
|
|
std::cout << "Mixed participants" << std::endl;
|
|
if(_mixedParticipantsAmount == 0)
|
|
{
|
|
std::cout << "N/A" << std::endl;
|
|
}
|
|
for(WebRtc_UWord16 i = 0; i < _mixedParticipantsAmount; i++)
|
|
{
|
|
std::cout << i + 1 << ". Participant " << _mixedParticipants[i].participant << ": level = " << _mixedParticipants[i].level << std::endl;
|
|
}
|
|
}
|
|
|
|
void
|
|
StatusReceiver::PrintVadPositiveParticipants()
|
|
{
|
|
std::cout << "VAD positive participants" << std::endl;
|
|
if(_mixedParticipantsAmount == 0)
|
|
{
|
|
std::cout << "N/A" << std::endl;
|
|
}
|
|
for(WebRtc_UWord16 i = 0; i < _mixedParticipantsAmount; i++)
|
|
{
|
|
std::cout << i + 1 << ". Participant " << _mixedParticipants[i].participant << ": level = " << _mixedParticipants[i].level << std::endl;
|
|
}
|
|
}
|
|
|
|
void
|
|
StatusReceiver::PrintMixedAudioLevel()
|
|
{
|
|
std::cout << "Mixed audio level = " << _mixedAudioLevel << std::endl;
|
|
}
|
|
|
|
WebRtc_Word32 MixerWrapper::_mixerWrapperIdCounter = 0;
|
|
|
|
MixerWrapper::MixerWrapper()
|
|
:
|
|
_processThread(NULL),
|
|
_threadId(0),
|
|
_firstProcessCall(true),
|
|
_previousTime(),
|
|
_periodicityInTicks(TickTime::MillisecondsToTicks(FileReader::kProcessPeriodicityInMs)),
|
|
_synchronizationEvent(EventWrapper::Create()),
|
|
_freeItemIds(),
|
|
_itemIdCounter(0),
|
|
_mixerParticipants(),
|
|
_mixerWrappererId(_mixerWrapperIdCounter++),
|
|
_instanceOutputPath(),
|
|
_trace(NULL),
|
|
_statusReceiver(_mixerWrappererId),
|
|
_generalAudioWriter()
|
|
{
|
|
sprintf(_instanceOutputPath,"instance%d/",(int)_mixerWrappererId);
|
|
MKDIR(_instanceOutputPath);
|
|
_mixer = AudioConferenceMixer::CreateAudioConferenceMixer(
|
|
_mixerWrappererId);
|
|
if(_mixer != NULL)
|
|
{
|
|
bool success = true;
|
|
|
|
success = _mixer->RegisterMixedStreamCallback(*this) == 0;
|
|
assert(success);
|
|
success = _mixer->RegisterMixedStreamCallback(*this) == -1;
|
|
assert(success);
|
|
success = _mixer->UnRegisterMixedStreamCallback() == 0;
|
|
assert(success);
|
|
success = _mixer->UnRegisterMixedStreamCallback() == -1;
|
|
assert(success);
|
|
success = _mixer->RegisterMixedStreamCallback(*this) == 0;
|
|
assert(success);
|
|
|
|
success = _mixer->RegisterMixerStatusCallback(_statusReceiver,2) == 0;
|
|
assert(success);
|
|
success = _mixer->RegisterMixerStatusCallback(_statusReceiver,1) == -1;
|
|
assert(success);
|
|
success = _mixer->UnRegisterMixerStatusCallback() == 0;
|
|
assert(success);
|
|
success = _mixer->UnRegisterMixerStatusCallback() == -1;
|
|
assert(success);
|
|
success = _mixer->RegisterMixerStatusCallback(_statusReceiver,1) == 0;
|
|
assert(success);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
std::cout << "Failed to create mixer instance";
|
|
}
|
|
}
|
|
|
|
MixerWrapper*
|
|
MixerWrapper::CreateMixerWrapper()
|
|
{
|
|
MixerWrapper* mixerWrapper = new MixerWrapper();
|
|
if(!mixerWrapper->InitializeFileWriter())
|
|
{
|
|
delete mixerWrapper;
|
|
return NULL;
|
|
}
|
|
return mixerWrapper;
|
|
}
|
|
|
|
MixerWrapper::~MixerWrapper()
|
|
{
|
|
StopMixing();
|
|
ClearAllItemIds();
|
|
_synchronizationEvent->StopTimer();
|
|
delete _synchronizationEvent;
|
|
delete _mixer;
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::CreateParticipant(
|
|
MixerParticipant::ParticipantType participantType)
|
|
{
|
|
WebRtc_Word32 startPosition = 0;
|
|
GenerateRandomPosition(startPosition);
|
|
return CreateParticipant(participantType,startPosition);
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::CreateParticipant(
|
|
MixerParticipant::ParticipantType participantType,
|
|
const WebRtc_Word32 startPosition)
|
|
{
|
|
WebRtc_UWord32 id;
|
|
if(!GetFreeItemIds(id))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
MixerParticipant* participant = MixerParticipant::CreateParticipant(id,participantType,startPosition,_instanceOutputPath);
|
|
if(!participant)
|
|
{
|
|
return false;
|
|
}
|
|
if(_mixerParticipants.Insert(id,static_cast<void*>(participant)) != 0)
|
|
{
|
|
delete participant;
|
|
return false;
|
|
}
|
|
if(!StartMixingParticipant(id))
|
|
{
|
|
DeleteParticipant(id);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::DeleteParticipant(
|
|
const WebRtc_UWord32 id)
|
|
{
|
|
bool success = StopMixingParticipant(id);
|
|
if(!success)
|
|
{
|
|
assert(false);
|
|
return false;
|
|
}
|
|
MapItem* item = _mixerParticipants.Find(id);
|
|
if(item == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
MixerParticipant* participant = static_cast<MixerParticipant*>(item->GetItem());
|
|
delete participant;
|
|
_mixerParticipants.Erase(item);
|
|
AddFreeItemIds(id);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::StartMixing(
|
|
const WebRtc_UWord32 mixedParticipants)
|
|
{
|
|
if(_processThread)
|
|
{
|
|
return false;
|
|
}
|
|
if(_mixer->SetAmountOfMixedParticipants(mixedParticipants) != 0)
|
|
{
|
|
assert(false);
|
|
}
|
|
WebRtc_UWord32 mixedParticipantsTest = 0;
|
|
_mixer->AmountOfMixedParticipants(mixedParticipantsTest);
|
|
assert(mixedParticipantsTest == mixedParticipants);
|
|
|
|
if(!_synchronizationEvent->StartTimer(true,10))
|
|
{
|
|
assert(false);
|
|
return false;
|
|
}
|
|
_processThread = ThreadWrapper::CreateThread(Process, this, kLowPriority);
|
|
if(!_processThread->Start(_threadId))
|
|
{
|
|
delete _processThread;
|
|
_processThread = NULL;
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::StopMixing()
|
|
{
|
|
while(_processThread &&
|
|
!_processThread->Stop())
|
|
{}
|
|
_synchronizationEvent->StopTimer();
|
|
|
|
delete _processThread;
|
|
_processThread = NULL;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MixerWrapper::NewMixedAudio(
|
|
const WebRtc_Word32 id,
|
|
const AudioFrame& generalAudioFrame,
|
|
const AudioFrame** uniqueAudioFrames,
|
|
const WebRtc_UWord32 size)
|
|
{
|
|
if(id < 0)
|
|
{
|
|
assert(false);
|
|
}
|
|
// Store the general audio
|
|
_generalAudioWriter.WriteToFile(generalAudioFrame);
|
|
|
|
// Send the unique audio frames to its corresponding participants
|
|
ListWrapper uniqueAudioFrameList;
|
|
for(WebRtc_UWord32 i = 0; i < size; i++)
|
|
{
|
|
WebRtc_UWord32 id = (uniqueAudioFrames[i])->_id;
|
|
MapItem* resultItem = _mixerParticipants.Find(id);
|
|
if(resultItem == NULL)
|
|
{
|
|
assert(false);
|
|
continue;
|
|
}
|
|
MixerParticipant* participant = static_cast<MixerParticipant*>(resultItem->GetItem());
|
|
participant->MixedAudioFrame(*(uniqueAudioFrames[i]));
|
|
uniqueAudioFrameList.PushBack(resultItem->GetItem());
|
|
}
|
|
|
|
// Send the general audio frames to the remaining participants
|
|
MapItem* item = _mixerParticipants.First();
|
|
while(item)
|
|
{
|
|
bool isUnique = false;
|
|
ListItem* compareItem = uniqueAudioFrameList.First();
|
|
while(compareItem)
|
|
{
|
|
if(compareItem->GetItem() == item->GetItem())
|
|
{
|
|
isUnique = true;
|
|
break;
|
|
}
|
|
compareItem = uniqueAudioFrameList.Next(compareItem);
|
|
}
|
|
if(!isUnique)
|
|
{
|
|
MixerParticipant* participant = static_cast<MixerParticipant*>(item->GetItem());
|
|
participant->MixedAudioFrame(generalAudioFrame);
|
|
}
|
|
item = _mixerParticipants.Next(item);
|
|
}
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::GetParticipantList(
|
|
ListWrapper& participants)
|
|
{
|
|
MapItem* item = _mixerParticipants.First();
|
|
while(item)
|
|
{
|
|
participants.PushBack(item->GetId());
|
|
item = _mixerParticipants.Next(item);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MixerWrapper::PrintStatus()
|
|
{
|
|
std::cout << "instance " << _mixerWrappererId << std::endl;
|
|
std::cout << std::endl;
|
|
_statusReceiver.PrintMixedParticipants();
|
|
std::cout << std::endl;
|
|
_statusReceiver.PrintVadPositiveParticipants();
|
|
std::cout << std::endl;
|
|
_statusReceiver.PrintMixedAudioLevel();
|
|
std::cout << "---------------------------------------" << std::endl;
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::InitializeFileWriter()
|
|
{
|
|
const WebRtc_Word32 stringsize = 128;
|
|
char fileName[stringsize] = "";
|
|
strncpy(fileName,_instanceOutputPath,stringsize);
|
|
fileName[stringsize-1] = '\0';
|
|
|
|
strncat(fileName,"generalOutputFile.pcm",(stringsize - strlen(fileName)));
|
|
fileName[stringsize-1] = '\0';
|
|
return _generalAudioWriter.SetFileName(fileName);
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::Process(
|
|
void* instance)
|
|
{
|
|
MixerWrapper* mixerWrapper = static_cast<MixerWrapper*>(instance);
|
|
return mixerWrapper->Process();
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::Process()
|
|
{
|
|
switch(_synchronizationEvent->Wait(1000))
|
|
{
|
|
case kEventSignaled:
|
|
// Normal operation, ~10 ms has passed
|
|
break;
|
|
case kEventError:
|
|
// Error occured end the thread and throw an assertion
|
|
assert(false);
|
|
return false;
|
|
case kEventTimeout:
|
|
// One second has passed without a timeout something is wrong
|
|
// end the thread and throw an assertion
|
|
assert(false);
|
|
return false;
|
|
}
|
|
WebRtc_Word32 processOfset = 0;
|
|
const TickTime currentTime = TickTime::Now();
|
|
if(_firstProcessCall)
|
|
{
|
|
_previousTime = TickTime::Now();
|
|
_firstProcessCall = false;
|
|
}
|
|
else
|
|
{
|
|
TickInterval deltaTime = (currentTime - _previousTime);
|
|
_previousTime += _periodicityInTicks;
|
|
processOfset = (WebRtc_Word32) deltaTime.Milliseconds();
|
|
processOfset -= FileReader::kProcessPeriodicityInMs;
|
|
}
|
|
|
|
_mixer->Process();
|
|
WebRtc_Word32 timeUntilNextProcess = _mixer->TimeUntilNextProcess();
|
|
if(processOfset > FileReader::kProcessPeriodicityInMs)
|
|
{
|
|
std::cout << "Performance Warning: Process running " << processOfset << " too slow" << std::endl;
|
|
_previousTime = currentTime;
|
|
if(timeUntilNextProcess > 0)
|
|
{
|
|
std::cout << "Performance Warning: test performance and module performance missmatch" << std::endl;
|
|
}
|
|
}
|
|
else if(processOfset < -FileReader::kProcessPeriodicityInMs)
|
|
{
|
|
std::cout << "Performance Warning: Process running " << -processOfset << " too fast" << std::endl;
|
|
_previousTime = currentTime;
|
|
if(timeUntilNextProcess < FileReader::kProcessPeriodicityInMs)
|
|
{
|
|
std::cout << "Performance Warning: test performance and module performance missmatch" << std::endl;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
MixerWrapper::StartMixingParticipant(
|
|
const WebRtc_UWord32 id)
|
|
{
|
|
MapItem* item = _mixerParticipants.Find(id);
|
|
if(item == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
MixerParticipant* participant = static_cast<MixerParticipant*>(item->GetItem());
|
|
MixerParticipant::ParticipantType participantType = MixerParticipant::REGULAR;
|
|
participant->GetParticipantType(participantType);
|
|
if(participantType == MixerParticipant::MIXED_ANONYMOUS)
|
|
{
|
|
bool anonymouslyMixed = false;
|
|
bool success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == 0;
|
|
assert(success);
|
|
success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0;
|
|
assert(success);
|
|
assert(anonymouslyMixed);
|
|
success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == -1;
|
|
assert(success);
|
|
success = _mixer->SetAnonymousMixabilityStatus(*participant,false) == 0;
|
|
assert(success);
|
|
success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0;
|
|
assert(success);
|
|
assert(!anonymouslyMixed);
|
|
success = _mixer->SetAnonymousMixabilityStatus(*participant,false) == -1;
|
|
assert(success);
|
|
success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == 0;
|
|
assert(success);
|
|
success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0;
|
|
assert(success);
|
|
assert(anonymouslyMixed);
|
|
return success;
|
|
}
|
|
WebRtc_UWord32 previousAmountOfMixableParticipants = 0;
|
|
bool success = _mixer->AmountOfMixables(previousAmountOfMixableParticipants) == 0;
|
|
assert(success);
|
|
|
|
success = _mixer->SetMixabilityStatus(*participant,true) == 0;
|
|
assert(success);
|
|
success = _mixer->SetMixabilityStatus(*participant,true) == -1;
|
|
assert(success);
|
|
success = _mixer->SetMixabilityStatus(*participant,false) == 0;
|
|
assert(success);
|
|
success = _mixer->SetMixabilityStatus(*participant,false) == -1;
|
|
assert(success);
|
|
success = _mixer->SetMixabilityStatus(*participant,true) == 0;
|
|
assert(success);
|
|
if(!success)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
WebRtc_UWord32 currentAmountOfMixableParticipants = 0;
|
|
success = _mixer->AmountOfMixables(currentAmountOfMixableParticipants) == 0;
|
|
assert(currentAmountOfMixableParticipants == previousAmountOfMixableParticipants + 1);
|
|
|
|
bool mixable = true;
|
|
success = _mixer->MixabilityStatus(*participant,mixable) == 0;
|
|
assert(success);
|
|
assert(mixable);
|
|
if(participantType == MixerParticipant::REGULAR)
|
|
{
|
|
return true;
|
|
}
|
|
bool IsVIP = false;
|
|
success = _mixer->SetVIPStatus(*participant,true) == 0;
|
|
assert(success);
|
|
success = _mixer->VIPStatus(*participant,IsVIP) == 0;
|
|
assert(success);
|
|
assert(IsVIP);
|
|
success = _mixer->SetVIPStatus(*participant,true) == -1;
|
|
assert(success);
|
|
success = _mixer->SetVIPStatus(*participant,false) == 0;
|
|
assert(success);
|
|
success = _mixer->VIPStatus(*participant,IsVIP) == 0;
|
|
assert(success);
|
|
assert(!IsVIP);
|
|
success = _mixer->SetVIPStatus(*participant,false) == -1;
|
|
assert(success);
|
|
success = _mixer->SetVIPStatus(*participant,true) == 0;
|
|
assert(success);
|
|
success = _mixer->VIPStatus(*participant,IsVIP) == 0;
|
|
assert(success);
|
|
assert(IsVIP);
|
|
assert(success);
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::StopMixingParticipant(
|
|
const WebRtc_UWord32 id)
|
|
{
|
|
MapItem* item = _mixerParticipants.Find(id);
|
|
if(item == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
MixerParticipant* participant = static_cast<MixerParticipant*>(item->GetItem());
|
|
bool success = false;
|
|
WebRtc_UWord32 previousAmountOfMixableParticipants = 0;
|
|
success = _mixer->AmountOfMixables(previousAmountOfMixableParticipants) == 0;
|
|
assert(success);
|
|
success = _mixer->SetMixabilityStatus(*participant,false) == 0;
|
|
assert(success);
|
|
WebRtc_UWord32 currentAmountOfMixableParticipants = 0;
|
|
success = _mixer->AmountOfMixables(currentAmountOfMixableParticipants) == 0;
|
|
assert(success);
|
|
assert(success ? currentAmountOfMixableParticipants == previousAmountOfMixableParticipants -1 :
|
|
currentAmountOfMixableParticipants == previousAmountOfMixableParticipants);
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
MixerWrapper::GetFreeItemIds(
|
|
WebRtc_UWord32& itemId)
|
|
{
|
|
if(!_freeItemIds.Empty())
|
|
{
|
|
ListItem* item = _freeItemIds.First();
|
|
WebRtc_UWord32* id = static_cast<WebRtc_UWord32*>(item->GetItem());
|
|
itemId = *id;
|
|
delete id;
|
|
return true;
|
|
}
|
|
if(_itemIdCounter == (WebRtc_UWord32) -1)
|
|
{
|
|
return false;
|
|
}
|
|
itemId = _itemIdCounter++;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MixerWrapper::AddFreeItemIds(
|
|
const WebRtc_UWord32 itemId)
|
|
{
|
|
WebRtc_UWord32* id = new WebRtc_UWord32;
|
|
*id = itemId;
|
|
_freeItemIds.PushBack(static_cast<void*>(id));
|
|
}
|
|
|
|
void
|
|
MixerWrapper::ClearAllItemIds()
|
|
{
|
|
ListItem* item = _freeItemIds.First();
|
|
while(item != NULL)
|
|
{
|
|
WebRtc_UWord32* id = static_cast<WebRtc_UWord32*>(item->GetItem());
|
|
delete id;
|
|
_freeItemIds.Erase(item);
|
|
item = _freeItemIds.First();
|
|
}
|
|
}
|
|
|
|
bool
|
|
LoopedFileRead(
|
|
WebRtc_Word16* buffer,
|
|
WebRtc_UWord32 bufferSizeInSamples,
|
|
WebRtc_UWord32 samplesToRead,
|
|
FILE* file)
|
|
{
|
|
if(bufferSizeInSamples < samplesToRead)
|
|
{
|
|
return false;
|
|
}
|
|
WebRtc_UWord32 gottenSamples = (WebRtc_UWord32)fread(buffer,sizeof(WebRtc_Word16),samplesToRead,file);
|
|
if(gottenSamples != samplesToRead)
|
|
{
|
|
WebRtc_UWord32 missingSamples = samplesToRead - gottenSamples;
|
|
fseek(file,0,0);
|
|
gottenSamples += (WebRtc_UWord32)fread(&buffer[gottenSamples],sizeof(WebRtc_Word16),missingSamples,file);
|
|
}
|
|
if(gottenSamples != samplesToRead)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
GenerateRandomPosition(
|
|
WebRtc_Word32& startPosition)
|
|
{
|
|
startPosition = (rand() % (60*16000/160)) * 160;
|
|
}
|