summaryrefslogtreecommitdiff
path: root/orkaudio/filters
diff options
context:
space:
mode:
authorHenri Herscher <henri@oreka.org>2006-01-20 22:38:18 +0000
committerHenri Herscher <henri@oreka.org>2006-01-20 22:38:18 +0000
commite07f34274b8912f773993ed96624242115440a3b (patch)
tree6b2f35398ceb7ff5c6a5afc9991f9a3670cb3a27 /orkaudio/filters
parent4ab4f3d3b2f0c3c51922eaeea49df162a9c892cd (diff)
VoIP mixing and decoding does now happen in the batch processing thread instead of immediately. Additionally, the system now supports filter plugins such as codecs.
git-svn-id: https://oreka.svn.sourceforge.net/svnroot/oreka/trunk@120 09dcff7a-b715-0410-9601-b79a96267cd0
Diffstat (limited to 'orkaudio/filters')
-rw-r--r--orkaudio/filters/VoIpMixer/VoIpMixer.cpp353
-rw-r--r--orkaudio/filters/VoIpMixer/VoIpMixer.dsp162
2 files changed, 515 insertions, 0 deletions
diff --git a/orkaudio/filters/VoIpMixer/VoIpMixer.cpp b/orkaudio/filters/VoIpMixer/VoIpMixer.cpp
new file mode 100644
index 0000000..b2a7925
--- /dev/null
+++ b/orkaudio/filters/VoIpMixer/VoIpMixer.cpp
@@ -0,0 +1,353 @@
+/*
+ * Oreka -- A media capture and retrieval platform
+ *
+ * Copyright (C) 2005, orecx LLC
+ *
+ * http://www.orecx.com
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License.
+ * Please refer to http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#pragma warning( disable: 4786 )
+
+#ifdef WIN32
+#define __CDECL__ __cdecl
+#else
+#define __CDECL__
+#endif
+#ifdef WIN32
+ #define DLL_EXPORT __declspec( dllexport )
+#else
+ #define DLL_EXPORT
+#endif
+
+#include <queue>
+#include "Filter.h"
+#include "AudioCapture.h"
+extern "C"
+{
+#include "g711.h"
+}
+
+#define NUM_SAMPLES_CIRCULAR_BUFFER 8000
+#define NUM_SAMPLES_TRIGGER 4000 // when we have this number of available samples make a shipment
+#define NUM_SAMPLES_SHIPMENT_HOLDOFF 2000 // when shipping, ship everything but this number of samples
+
+
+class VoIpMixer : public Filter
+{
+public:
+ VoIpMixer();
+
+ FilterRef __CDECL__ Instanciate();
+ void __CDECL__ AudioChunkIn(AudioChunkRef& chunk);
+ void __CDECL__ AudioChunkOut(AudioChunkRef& chunk);
+ AudioEncodingEnum __CDECL__ GetInputAudioEncoding();
+ AudioEncodingEnum __CDECL__ GetOutputAudioEncoding();
+ CStdString __CDECL__ GetName();
+ int __CDECL__ GetInputRtpPayloadType();
+
+private:
+ //AudioChunkRef m_outputAudioChunk;
+ std::queue<AudioChunkRef> m_outputQueue;
+
+ void StoreRtpPacket(AudioChunkRef& chunk);
+ void CreateShipment(size_t silenceSize);
+ unsigned int FreeSpace();
+ unsigned int UsedSpace();
+ short* GetHoldOffPtr();
+ short* CircularPointerAddOffset(short *ptr, size_t offset);
+ short* CicularPointerSubtractOffset(short *ptr, size_t offset);
+
+ short* m_writePtr; // pointer after newest RTP data we've received
+ short* m_readPtr; // where to read from next
+ unsigned int m_readTimestamp; // timestamp that the next shipment will have
+ unsigned int m_writeTimestamp; // timestamp that the next RTP buffer should have
+ short* m_bufferEnd;
+ short m_buffer[NUM_SAMPLES_CIRCULAR_BUFFER];
+ unsigned int m_shippedSamples;
+
+};
+
+VoIpMixer::VoIpMixer()
+{
+ m_writePtr = m_buffer;
+ m_readPtr = m_buffer;
+ m_bufferEnd = m_buffer + NUM_SAMPLES_CIRCULAR_BUFFER;
+ m_writeTimestamp = 0;
+ m_readTimestamp = 0;
+ //m_log = Logger::getLogger("rtpringbuffer");
+ m_shippedSamples = 0;
+}
+
+FilterRef VoIpMixer::Instanciate()
+{
+ FilterRef Filter(new VoIpMixer());
+ return Filter;
+}
+
+void VoIpMixer::AudioChunkIn(AudioChunkRef& chunk)
+{
+ AudioChunkDetails* details = chunk->GetDetails();
+ if(details->m_encoding != PcmAudio)
+ {
+ throw (CStdString("VoIpMixer input audio must be PCM !"));
+ }
+
+ unsigned int rtpEndTimestamp = details->m_timestamp + chunk->GetNumSamples(); // GetNumSamples() #############################
+
+ if(m_writeTimestamp == 0)
+ {
+ // First RTP packet of the session
+ //LOG4CXX_DEBUG(m_log, m_capturePort + " first packet");
+ m_writeTimestamp = details->m_timestamp;
+ m_readTimestamp = m_writeTimestamp;
+ StoreRtpPacket(chunk);
+ }
+ else if (details->m_timestamp >= m_readTimestamp) // drop packets that are older than last shipment
+ {
+ if( (int)(rtpEndTimestamp - m_writeTimestamp) <= (int)FreeSpace())
+ {
+ // RTP packet fits into current buffer
+ StoreRtpPacket(chunk);
+
+ if(UsedSpace() > NUM_SAMPLES_TRIGGER)
+ {
+ // We have enough stuff, make a shipment
+ CreateShipment(0);
+ }
+ }
+ else
+ {
+ // RTP packet does not fit into current buffer
+ // work out how much silence we need to add to the current buffer when shipping
+ size_t silenceSize = details->m_timestamp - m_writeTimestamp;
+ CreateShipment(silenceSize);
+
+ // reset buffer
+ m_writePtr = m_buffer;
+ m_readPtr = m_buffer;
+ m_writeTimestamp = details->m_timestamp;
+ m_readTimestamp = m_writeTimestamp;
+
+ // Store new packet
+ StoreRtpPacket(chunk);
+ }
+ }
+ else
+ {
+ //LOG4CXX_DEBUG(m_log, m_capturePort + " packet too old, dropped");
+ }
+ CStdString debug;
+ debug.Format("free:%u used:%u wr:%x rd:%x wrts:%u rdts:%d", FreeSpace(), UsedSpace(), m_writePtr, m_readPtr, m_writeTimestamp, m_readTimestamp);
+ //LOG4CXX_DEBUG(m_log, debug);
+}
+
+void VoIpMixer::AudioChunkOut(AudioChunkRef& chunk)
+{
+ if(m_outputQueue.size() > 0)
+ {
+ chunk = m_outputQueue.front();
+ m_outputQueue.pop();
+ }
+ else
+ {
+ chunk.reset();
+ }
+}
+
+AudioEncodingEnum VoIpMixer::GetInputAudioEncoding()
+{
+ return PcmAudio;
+}
+
+AudioEncodingEnum VoIpMixer::GetOutputAudioEncoding()
+{
+ return PcmAudio;
+}
+
+CStdString VoIpMixer::GetName()
+{
+ return "VoIpMixer";
+}
+
+
+int VoIpMixer::GetInputRtpPayloadType(void) // does not link if not defined here ?
+{
+ return -1;
+}
+
+// Writes to the internal buffer without any size verification
+void VoIpMixer::StoreRtpPacket(AudioChunkRef& audioChunk)
+{
+ CStdString debug;
+ AudioChunkDetails* details = audioChunk->GetDetails();
+
+ // 1. Silence from write pointer until end of RTP packet
+ unsigned int endRtpTimestamp = details->m_timestamp + audioChunk->GetNumSamples(); // GetNumSamples() #############################
+ if (endRtpTimestamp > m_writeTimestamp)
+ {
+ for(int i=0; i<(endRtpTimestamp - m_writeTimestamp); i++)
+ {
+ *m_writePtr = 0;
+ m_writePtr++;
+ if(m_writePtr >= m_bufferEnd)
+ {
+ m_writePtr = m_buffer;
+ }
+ }
+ int silenceSize = endRtpTimestamp - m_writeTimestamp;
+ m_writeTimestamp = endRtpTimestamp;
+ debug.Format("Zeroed %d samples, wr:%x wrts:%u", silenceSize, m_writePtr, m_writeTimestamp);
+ //LOG4CXX_DEBUG(m_log, debug);
+ }
+
+ // 2. Mix in the latest samples from this RTP packet
+ unsigned int timestampDelta = m_writeTimestamp - details->m_timestamp;
+ ASSERT(timestampDelta>=0);
+ short* tempWritePtr = CicularPointerSubtractOffset(m_writePtr, timestampDelta);
+ short* payload = (short *)audioChunk->m_pBuffer; // payload should be short* #################################################
+
+ for(int i=0; i<audioChunk->GetNumSamples() ; i++) //#################################################
+ {
+ *tempWritePtr += payload[i];
+ tempWritePtr++;
+ if(tempWritePtr >= m_bufferEnd)
+ {
+ tempWritePtr = m_buffer;
+ }
+ }
+ debug.Format("Copied %d samples, tmpwr:%x", audioChunk->GetNumSamples(), tempWritePtr); //#################################################
+ //LOG4CXX_DEBUG(m_log, debug);
+}
+
+short* VoIpMixer::CircularPointerAddOffset(short *ptr, size_t offset)
+{
+ if((ptr + offset) >= m_bufferEnd)
+ {
+ return m_buffer + offset - (m_bufferEnd-ptr);
+ }
+ else
+ {
+ return ptr + offset;
+ }
+}
+
+short* VoIpMixer::CicularPointerSubtractOffset(short *ptr, size_t offset)
+{
+ if((ptr-offset) < m_buffer)
+ {
+ return m_bufferEnd - offset + (ptr-m_buffer);
+ }
+ else
+ {
+ return ptr - offset;
+ }
+}
+
+void VoIpMixer::CreateShipment(size_t silenceSize)
+{
+ // 1. ship from readPtr until stop pointer or until end of buffer if wrapped
+ bool bufferWrapped = false;
+ short* stopPtr = NULL;
+ short* wrappedStopPtr = NULL;
+ if (silenceSize)
+ {
+ // There is additional silence to ship, do not take holdoff into account
+ stopPtr = m_writePtr;
+ }
+ else
+ {
+ stopPtr = CicularPointerSubtractOffset(m_writePtr, NUM_SAMPLES_SHIPMENT_HOLDOFF);
+ }
+
+ if (stopPtr < m_readPtr)
+ {
+ wrappedStopPtr = stopPtr;
+ stopPtr = m_bufferEnd;
+ bufferWrapped = true;
+ }
+ size_t shortSize = stopPtr-m_readPtr;
+ size_t byteSize = shortSize*2;
+ AudioChunkRef chunk(new AudioChunk());
+ AudioChunkDetails details;
+ details.m_encoding = PcmAudio;
+ chunk->SetBuffer((void*)m_readPtr, byteSize, details);
+ m_outputQueue.push(chunk);
+ m_shippedSamples += shortSize;
+ m_readPtr = CircularPointerAddOffset(m_readPtr ,shortSize);
+ m_readTimestamp += shortSize;
+
+ CStdString debug;
+ debug.Format("Ship %d samples, rd:%x rdts:%u", shortSize, m_readPtr, m_readTimestamp);
+ //LOG4CXX_DEBUG(m_log, debug);
+
+
+ // 2. ship from beginning of buffer until stop ptr
+ if(bufferWrapped)
+ {
+ shortSize = wrappedStopPtr - m_buffer;
+ byteSize = shortSize*2;
+ chunk.reset(new AudioChunk());
+ AudioChunkDetails details;
+ details.m_encoding = PcmAudio;
+ chunk->SetBuffer((void*)m_buffer, byteSize, details);
+ m_outputQueue.push(chunk);
+ m_shippedSamples += shortSize;
+ m_readPtr = CircularPointerAddOffset(m_readPtr ,shortSize);
+ m_readTimestamp += shortSize;
+ debug.Format("Ship wrapped %d samples, rd:%x rdts:%u", shortSize, m_readPtr, m_readTimestamp);
+ //LOG4CXX_DEBUG(m_log, debug);
+ }
+
+ // 3. ship silence
+ if (silenceSize)
+ {
+ byteSize = silenceSize*2;
+ char* silenceBuffer = (char*)calloc(byteSize, 1);
+ if (silenceBuffer)
+ {
+ AudioChunkRef chunk(new AudioChunk());
+ AudioChunkDetails details;
+ details.m_encoding = PcmAudio;
+ chunk->SetBuffer((void*)silenceBuffer, byteSize, details);
+ m_outputQueue.push(chunk);
+ m_shippedSamples += silenceSize;
+ m_readPtr = CircularPointerAddOffset(m_readPtr ,silenceSize);
+ m_readTimestamp += silenceSize;
+ }
+ debug.Format("Ship %d silence samples, rd:%x rdts:%u", silenceSize, m_readPtr, m_readTimestamp);
+ //LOG4CXX_DEBUG(m_log, debug);
+ }
+}
+
+
+unsigned int VoIpMixer::UsedSpace()
+{
+ if(m_writePtr >= m_readPtr)
+ {
+ return m_writePtr - m_readPtr;
+ }
+ return NUM_SAMPLES_CIRCULAR_BUFFER + m_writePtr - m_readPtr;
+}
+
+
+unsigned int VoIpMixer::FreeSpace()
+{
+ return NUM_SAMPLES_CIRCULAR_BUFFER - UsedSpace();
+}
+
+
+//=====================================================================
+
+
+extern "C"
+{
+ DLL_EXPORT void __CDECL__ Initialize()
+ {
+ FilterRef filter(new VoIpMixer());
+ FilterRegistry::instance()->RegisterFilter(filter);
+ }
+} \ No newline at end of file
diff --git a/orkaudio/filters/VoIpMixer/VoIpMixer.dsp b/orkaudio/filters/VoIpMixer/VoIpMixer.dsp
new file mode 100644
index 0000000..2d43dd8
--- /dev/null
+++ b/orkaudio/filters/VoIpMixer/VoIpMixer.dsp
@@ -0,0 +1,162 @@
+# Microsoft Developer Studio Project File - Name="VoIpMixer" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=VoIpMixer - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "VoIpMixer.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "VoIpMixer.mak" CFG="VoIpMixer - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "VoIpMixer - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "VoIpMixer - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "VoIpMixer - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VoIpMixer_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "C:\devExt\winpcap\WpdPack\Include" /I "../.." /I "../../../OrkBaseCxx" /I "C:\devExt\boost\boost_1_32_0" /I "C:\devExt\ACE_wrappers" /I "C:\devExt\xerces++\xerces-c_2_6_0-windows_nt-msvc_60\include" /I "../Common" /I "C:\devExt\log4cxx\log4cxx-0.9.7\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VoIpMixer_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 packet.lib wpcap.lib wininet.lib OrkBase.lib ace.lib log4cxx.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"C:\devExt\winpcap\WpdPack\Lib" /libpath:"../../../OrkBaseCxx/Release" /libpath:"C:\devExt\log4cxx\log4cxx-0.9.7\msvc\Lib\Release" /libpath:"C:\devExt\ACE_wrappers\lib"
+
+!ELSEIF "$(CFG)" == "VoIpMixer - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VoIpMixer_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "C:\devExt\winpcap\WpdPack\Include" /I "../.." /I "../../../OrkBaseCxx" /I "C:\devExt\boost\boost_1_32_0" /I "C:\devExt\ACE_wrappers" /I "C:\devExt\xerces++\xerces-c_2_6_0-windows_nt-msvc_60\include" /I "../Common" /I "C:\devExt\log4cxx\log4cxx-0.9.7\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VoIpMixer_EXPORTS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 packet.lib wpcap.lib wininet.lib OrkBaseD.lib aced.lib log4cxx.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"C:\devExt\winpcap\WpdPack\Lib" /libpath:"../../../OrkBaseCxx/Debug" /libpath:"C:\devExt\log4cxx\log4cxx-0.9.7\msvc\Lib\Debug" /libpath:"C:\devExt\ACE_wrappers\lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "VoIpMixer - Win32 Release"
+# Name "VoIpMixer - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\..\AudioCapturePlugin.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Common\AudioCapturePluginCommon.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Common\AudioCapturePluginCommon.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\ConfigManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\g711.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\g711.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PacketHeaderDefs.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\PacketHeaderDefs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Rtp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Rtp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RtpSession.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RtpSession.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VoIpMixer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VoIpMixerConfig.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VoIpMixerConfig.h
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project