[EMAIL PROTECTED] wrote:
I'm sending my call recording patch.
It adds 2 splitters to flowgraph, one between MprMixer and MprBridge
on the mic side and one on the speaker side. 2nd output from these
splitters goes into new MprMixer which will enable us to hear input
from our mic, our dtmf, any files we play to other side and sound from
the other party. After this mixer I plug a MprRecorder[RECORDER_CALL].
These resources are initially disabled, so they pass input to output 0
(original path). They are enabled when we start recording and disabled
when we stop under normal condition (closeRecorders is called in
MpCallFlowGraph).
They are not stopped if MprRecorder is disabled internally for example
due to DTMF event or long silence. This will have to be handled by
another patch. I would like the MprRecorder to send a message to flow
graph when it is enabled or disabled, and here we could also enable
and disable splitters used for call recording and send media event to
sipxtapi informing user that call recording started or stopped. This
will be done in another patch.
------------------------------------------------------------------------
diff -ru
old/sipXmediaAdapterLib/sipXmediaMediaProcessing/src/CpPhoneMediaInterface.cpp
new/sipXmediaAdapterLib/sipXmediaMediaProcessing/src/CpPhoneMediaInterface.cpp
---
old/sipXmediaAdapterLib/sipXmediaMediaProcessing/src/CpPhoneMediaInterface.cpp
Mon Mar 5 11:57:41 2007
+++
new/sipXmediaAdapterLib/sipXmediaMediaProcessing/src/CpPhoneMediaInterface.cpp
Mon Mar 5 01:28:25 2007
@@ -1207,14 +1207,15 @@
// TODO:: This API is designed to record the audio from a single channel.
// If the connectionId is -1, record all.
- //
- // Also -- not sure of the if the spkr is the correct place to record --
- // this probably doesn't include mic data...
- ///
-
- double duration = 0 ;
+ double duration = 0 ; // this means it will record for very long time
int dtmf = 0 ;
- return mpFlowGraph->record(1, -1, NULL, NULL, szFile) ;
+
+ /* use new call recorder
+ from now on, call recorder records both mic, speaker and local dtmf
+ we don't want raw pcm, but wav pcm, raw pcm should be passed to a callback
+ meant for recording, for example for conversion to mp3 or other format
*/
+ return mpFlowGraph->record(0, -1, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, szFile, 0, 0, NULL,
MprRecorder::WAV_PCM_16) ;
}
OsStatus CpPhoneMediaInterface::stopRecordChannelAudio(int connectionId)
diff -ru old/sipXmediaLib/include/mp/MpCallFlowGraph.h new/sipXmediaLib/include/mp/MpCallFlowGraph.h
--- old/sipXmediaLib/include/mp/MpCallFlowGraph.h Mon Mar 5 11:56:12 2007
+++ new/sipXmediaLib/include/mp/MpCallFlowGraph.h Sun Mar 4 22:34:53 2007
@@ -113,6 +113,7 @@
RECORDER_SPKR32K,
RECORDER_ECHO_IN32,
#endif // HIGH_SAMPLERATE_AUDIO ]
+ RECORDER_CALL,
MAX_RECORDERS = 10
};
@@ -170,14 +171,7 @@
OsProtectedEvent* recordEvent = NULL);
- OsStatus record(int timeMS, int silenceLength, const char* micName = NULL,
- const char* echoOutName = NULL, const char* spkrName = NULL,
- const char* mic32Name = NULL, const char* spkr32Name = NULL,
- const char* echoIn8Name = NULL, const char* echoIn32Name =
NULL,
- const char* playName = NULL,
- int toneOptions = 0,
- int repeat = 0, OsNotification* completion = NULL,
- MprRecorder::RecordFileFormat format = MprRecorder::RAW_PCM_16);
+ OsStatus record(int timeMS, int silenceLength, const char* micName = NULL,
const char* echoOutName = NULL, const char* spkrName = NULL, const char*
mic32Name = NULL, const char* spkr32Name = NULL, const char* echoIn8Name =
NULL, const char* echoIn32Name = NULL, const char* playName = NULL, const
char* callName = NULL, int toneOptions = 0, int repeat = 0,
OsNotification* completion = NULL, MprRecorder::RecordFileFormat format =
MprRecorder::RAW_PCM_16);
OsStatus startRecording(const char* audioFileName, UtlBoolean repeat,
@@ -469,6 +463,9 @@
#endif
MprMixer* mpTFsMicMixer;
MprMixer* mpTFsBridgeMixer;
+ MprMixer* mpCallrecMixer;
+ MprSplitter* mpMicCallrecSplitter;
+ MprSplitter* mpSpeakerCallrecSplitter;
MprSplitter* mpToneFileSplitter;
MprToSpkr* mpToSpkr;
MprToneGen* mpToneGen;
diff -ru old/sipXmediaLib/src/mp/MpCallFlowGraph.cpp
new/sipXmediaLib/src/mp/MpCallFlowGraph.cpp
--- old/sipXmediaLib/src/mp/MpCallFlowGraph.cpp Mon Mar 5 11:56:40 2007
+++ new/sipXmediaLib/src/mp/MpCallFlowGraph.cpp Mon Mar 5 01:58:48 2007
@@ -165,8 +165,14 @@
samplesPerFrame, samplesPerSec);
mpTFsBridgeMixer = new MprMixer("TFsBridgeMixer", 2,
samplesPerFrame, samplesPerSec);
+ mpCallrecMixer = new MprMixer("CallrecMixer", 2,
+ samplesPerFrame, samplesPerSec);
mpToneFileSplitter = new MprSplitter("ToneFileSplitter", 2,
samplesPerFrame, samplesPerSec);
+ mpMicCallrecSplitter = new MprSplitter("MicCallrecSplitter", 2,
+ samplesPerFrame, samplesPerSec);
+ mpSpeakerCallrecSplitter = new MprSplitter("SpeakerCallrecSplitter", 2,
+ samplesPerFrame, samplesPerSec);
#ifndef DISABLE_LOCAL_AUDIO // [
mpToSpkr = new MprToSpkr("ToSpkr",
samplesPerFrame, samplesPerSec,
@@ -195,7 +201,10 @@
#endif // DISABLE_LOCAL_AUDIO ]
res = addResource(*mpTFsMicMixer); assert(res == OS_SUCCESS);
res = addResource(*mpTFsBridgeMixer); assert(res == OS_SUCCESS);
+ res = addResource(*mpCallrecMixer); assert(res == OS_SUCCESS);
res = addResource(*mpToneFileSplitter); assert(res == OS_SUCCESS);
+ res = addResource(*mpMicCallrecSplitter); assert(res == OS_SUCCESS);
+ res = addResource(*mpSpeakerCallrecSplitter); assert(res == OS_SUCCESS);
#ifndef DISABLE_LOCAL_AUDIO
res = addResource(*mpToSpkr); assert(res == OS_SUCCESS);
#endif
@@ -233,14 +242,29 @@
assert(res == OS_SUCCESS);
#endif
- // connect TFsMicMixer -> Bridge
- res = addLink(*mpTFsMicMixer, 0, *mpBridge, 0);
+ // connect TFsMicMixer -> mpMicCallrecSplitter
+ res = addLink(*mpTFsMicMixer, 0, *mpMicCallrecSplitter, 0);
+ assert(res == OS_SUCCESS);
+
+ // connect mpMicCallrecSplitter -> Bridge
+ res = addLink(*mpMicCallrecSplitter, 0, *mpBridge, 0);
assert(res == OS_SUCCESS);
//////////////////////////////////////////////////////////////////////////
- // connect Bridge -> TFsBridgeMixer
+ // connect Bridge -> TFsBridgeMixer through mpSpeakerCallrecSplitter
+
+ res = addLink(*mpBridge, 0, *mpSpeakerCallrecSplitter, 0);
+ assert(res == OS_SUCCESS);
- res = addLink(*mpBridge, 0, *mpTFsBridgeMixer, 1);
+ res = addLink(*mpSpeakerCallrecSplitter, 0, *mpTFsBridgeMixer, 1);
+ assert(res == OS_SUCCESS);
+
+ //////////////////////////////////////////////////////////////////////////
+ // connect mpSpeakerCallrecSplitter and mpMicCallrecSplitter into
mpCallrecMixer
+ res = addLink(*mpMicCallrecSplitter, 1, *mpCallrecMixer, 0);
+ assert(res == OS_SUCCESS);
+
+ res = addLink(*mpSpeakerCallrecSplitter, 1, *mpCallrecMixer, 1);
assert(res == OS_SUCCESS);
//////////////////////////////////////////////////////////////////////////
@@ -282,6 +306,11 @@
// disable the from file
boolRes = mpFromFile->disable(); assert(boolRes);
+ // disable mpCallrecMixer and splitters, they are enabled when we want to start recording
+ boolRes = mpCallrecMixer->disable(); assert(boolRes);
+ boolRes = mpMicCallrecSplitter->disable(); assert(boolRes);
+ boolRes = mpSpeakerCallrecSplitter->disable(); assert(boolRes);
+
#ifndef DISABLE_LOCAL_AUDIO // [
// disable the FromMic, EchoCancel, PreProcess and ToSpkr -- we cannot have
focus yet...
boolRes = mpFromMic->disable(); assert(boolRes);
@@ -303,6 +332,12 @@
boolRes = mpTFsBridgeMixer->setWeight(0, 0); assert(boolRes);
boolRes = mpTFsBridgeMixer->setWeight(1, 1); assert(boolRes);
+ // set up weights for callrec mixer as they are zeroed in constructor
+ // input 0 is from mic
+ boolRes = mpCallrecMixer->setWeight(1, 0); assert(boolRes);
+ // input 1 is speaker
+ boolRes = mpCallrecMixer->setWeight(1, 1); assert(boolRes);
+
#ifdef INCLUDE_RTCP /* [ */
// All the Media Resource seemed to have been started successfully.
// Let's now create an RTCP Session so that we may be prepared to
@@ -361,6 +396,14 @@
*mpEchoCancel, 1);
assert(res == OS_SUCCESS);
+ // create Call recorder and connect it to mpCallrecMixer
+ mpRecorders[RECORDER_CALL] =
+ new MprRecorder("RecordCall", samplesPerFrame, samplesPerSec);
+ res = addResource(*(mpRecorders[RECORDER_CALL]));
+ assert(res == OS_SUCCESS);
+ res = addLink(*mpCallrecMixer, 0, *(mpRecorders[RECORDER_CALL]), 0);
+ assert(res == OS_SUCCESS);
+
#ifdef HIGH_SAMPLERATE_AUDIO // [
mpRecorders[RECORDER_ECHO_IN32] =
new MprRecorder("RecordEchoIn32", samplesPerFrame, samplesPerSec);
@@ -479,6 +522,13 @@
res = removeLink(*mpToneFileSplitter, 0); assert(res == OS_SUCCESS);
res = removeLink(*mpToneFileSplitter, 1); assert(res == OS_SUCCESS);
+ // remove links of call recording
+ res = removeLink(*mpCallrecMixer, 0); assert(res == OS_SUCCESS);
+ res = removeLink(*mpMicCallrecSplitter, 0); assert(res == OS_SUCCESS);
+ res = removeLink(*mpMicCallrecSplitter, 1); assert(res == OS_SUCCESS);
+ res = removeLink(*mpSpeakerCallrecSplitter, 0); assert(res == OS_SUCCESS);
+ res = removeLink(*mpSpeakerCallrecSplitter, 1); assert(res == OS_SUCCESS);
+
// now remove (and destroy) the resources
#ifndef DISABLE_LOCAL_AUDIO
res = removeResource(*mpFromMic);
@@ -523,6 +573,19 @@
assert(res == OS_SUCCESS);
delete mpFromFile;
+ // kill call recording resources
+ res = removeResource(*mpMicCallrecSplitter);
+ assert(res == OS_SUCCESS);
+ delete mpMicCallrecSplitter;
+
+ res = removeResource(*mpSpeakerCallrecSplitter);
+ assert(res == OS_SUCCESS);
+ delete mpSpeakerCallrecSplitter;
+
+ res = removeResource(*mpCallrecMixer);
+ assert(res == OS_SUCCESS);
+ delete mpCallrecMixer;
+
for (i=0; i<MAX_RECORDERS; i++) {
if (NULL != mpRecorders[i]) {
res = removeResource(*mpRecorders[i]);
@@ -719,6 +782,11 @@
ret++;
}
}
+ // also disable mpCallrecMixer and splitters
+ mpCallrecMixer->disable();
+ mpMicCallrecSplitter->disable();
+ mpSpeakerCallrecSplitter->disable();
+
return ret;
}
@@ -850,7 +918,7 @@
res = record(ms, 999999, created_micNamePtr, created_echoOutNamePtr,
created_spkrNamePtr, created_mic32NamePtr, created_spkr32NamePtr,
created_echoIn8NamePtr, created_echoIn32NamePtr,
- playFilename, 0, 0, NULL);
+ playFilename, NULL, 0, 0, NULL);
playIndex++;
strcpy(saved_playFilename,playFilename);
@@ -890,7 +958,7 @@
}
return record(ms, silenceLength, NULL, NULL, fileName,
- NULL, NULL, NULL, NULL, NULL, 0, 0, recordEvent, format);
+ NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, recordEvent,
format);
}
@@ -910,7 +978,7 @@
OsTime maxEventTime(timeoutSecs, 0);
record(ms, silenceLength, fileName, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, 0, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL, 0, 0,
NULL, MprRecorder::WAV_PCM_16);
// Wait until the call sets the number of connections
@@ -960,7 +1028,7 @@
record(ms, silenceLength, NULL, NULL, fileName,
- NULL, NULL, NULL, NULL, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL, 0,
0, recordEvent,format);
if (dtmfTerm)
@@ -1004,55 +1072,60 @@
return ret;
}
-OsStatus MpCallFlowGraph::record(int ms, int silenceLength, const char* micName,
- const char* echoOutName, const char* spkrName,
- const char* mic32Name, const char* spkr32Name,
- const char* echoIn8Name, const char* echoIn32Name,
- const char* playName, int toneOptions,
- int repeat, OsNotification* completion,
- MprRecorder::RecordFileFormat format)
+OsStatus MpCallFlowGraph::record(int timeMS, int silenceLength, const char*
micName /*= NULL*/, const char* echoOutName /*= NULL*/, const char* spkrName
/*= NULL*/, const char* mic32Name /*= NULL*/, const char* spkr32Name /*=
NULL*/, const char* echoIn8Name /*= NULL*/, const char* echoIn32Name /*=
NULL*/, const char* playName /*= NULL*/, const char* callName /*= NULL*/,
int toneOptions /*= 0*/, int repeat /*= 0*/, OsNotification* completion /*=
NULL*/, MprRecorder::RecordFileFormat format /*= MprRecorder::RAW_PCM_16*/)
{
if (NULL == this) {
MpMediaTask* pMT = MpMediaTask::getMediaTask(0);
MpCallFlowGraph* pIF = (MpCallFlowGraph*) pMT->getFocus();
if (NULL != pIF) {
- return pIF-> record(ms, silenceLength, micName, echoOutName, spkrName,
+ return pIF-> record(timeMS, silenceLength, micName, echoOutName,
spkrName,
mic32Name, spkr32Name, echoIn8Name, echoIn32Name,
- playName, toneOptions, repeat, completion);
+ playName, callName, toneOptions, repeat, completion);
}
return OS_INVALID;
}
if (NULL != micName) {
setupRecorder(RECORDER_MIC, micName,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != echoOutName) {
setupRecorder(RECORDER_ECHO_OUT, echoOutName,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != spkrName) {
setupRecorder(RECORDER_SPKR, spkrName,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != echoIn8Name) {
setupRecorder(RECORDER_ECHO_IN8, echoIn8Name,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
#ifdef HIGH_SAMPLERATE_AUDIO // [
if (NULL != mic32Name) {
setupRecorder(RECORDER_MIC32K, mic32Name,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != spkr32Name) {
setupRecorder(RECORDER_SPKR32K,spkr32Name,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != echoIn32Name) {
setupRecorder(RECORDER_ECHO_IN32,
- echoIn32Name, ms, silenceLength, completion, format);
+ echoIn32Name, timeMS, silenceLength, completion, format);
}
#endif // HIGH_SAMPLERATE_AUDIO ]
+ // set up call recorder
+ if (NULL != callName) {
+ // also enable callrec mixer
+ if(setupRecorder(RECORDER_CALL, callName,
+ timeMS, silenceLength, completion, format))
+ {
+ mpCallrecMixer->enable();
+ mpMicCallrecSplitter->enable();
+ mpSpeakerCallrecSplitter->enable();
+ }
+ }
return startRecording(playName, repeat, toneOptions, completion);
}
@@ -1100,6 +1173,12 @@
}
res = mpRecorders[which]->setup(file, format, timeMS, silenceLength, (OsEvent*)event);
+ }
+ else
+ {
+ OsSysLog::add(FAC_AUDIO, PRI_ERR,
+ "setupRecorder failed to open file %s, error code is %i",
+ audioFileName, errno);
}
return (file != -1);
}
------------------------------------------------------------------------
_______________________________________________
sipxtapi-dev mailing list
[email protected]
List Archive: http://list.sipfoundry.org/archive/sipxtapi-dev/
Here is 2nd version of call recording patch (btw original recording
without patch doesnt work at all, it has a bug). This patch solves
problem with disabling splitters and mixer when recorder is disabled for
some other reason than user action. This can be considered final.
I originally wanted to add new media events to be sent to sipxtapi, but
found out that that we don't have even indirect knowledge of
SipConnection or Connection (which could be used to fire events) in the
flow graph or MpAudioConnection (since it is a library it shouldnt have
direct knowledge, so the design of using an interface is right).
IMediaEventListener.exists for this purpose, it is passed to
CpPhoneMediaInterface::createConnection but there we don't do anything
with it. We could pass it to MpCallFlowGraph::createConnection and from
there into MpAudioConnection (and store pointer to IMediaEventListener
here). Then we could send any new events that occur in flowgraph or
audio connection from medialib.
But even if we did this, then events for call recording still wouldnt
work right, as you can add an existing call into a conference which
means its old MpAudioConnection will be destroyed and new one will be
created for the right flow graph.This also means call recording cant
work if you add recorded call to a conference. Conference recording must
be started after the call has been added to the conference, and should
be started for single call in conference only. This is due to the nature
flowgraphs and audio connections are used. So no recording events for now.
I also noticed a design flaw in Connection::prepareForJoin and
Connection::prepareForSplit.
prepareForJoin doesn't call mpMediaInterface->createConnection with all
parameters it should, so something will not work right after call is
added to conference. Perhaps these 2 should be made abstract and moved
to SipConnection?
Jaroslav Libak
diff -ru
old/sipXmediaAdapterLib/sipXmediaMediaProcessing/src/CpPhoneMediaInterface.cpp
new/sipXmediaAdapterLib/sipXmediaMediaProcessing/src/CpPhoneMediaInterface.cpp
---
old/sipXmediaAdapterLib/sipXmediaMediaProcessing/src/CpPhoneMediaInterface.cpp
Mon Mar 5 11:57:41 2007
+++
new/sipXmediaAdapterLib/sipXmediaMediaProcessing/src/CpPhoneMediaInterface.cpp
Mon Mar 5 21:28:22 2007
@@ -1207,14 +1207,15 @@
// TODO:: This API is designed to record the audio from a single channel.
// If the connectionId is -1, record all.
- //
- // Also -- not sure of the if the spkr is the correct place to record --
- // this probably doesn't include mic data...
- ///
-
- double duration = 0 ;
+ double duration = 0 ; // this means it will record for very long time
int dtmf = 0 ;
- return mpFlowGraph->record(1, -1, NULL, NULL, szFile) ;
+
+ /* use new call recorder
+ from now on, call recorder records both mic, speaker and local dtmf
+ we don't want raw pcm, but wav pcm, raw pcm should be passed to a
callback
+ meant for recording, for example for conversion to mp3 or other format
*/
+ return mpFlowGraph->record(0, -1, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, szFile, 0, 0, NULL,
MprRecorder::WAV_PCM_16) ;
}
OsStatus CpPhoneMediaInterface::stopRecordChannelAudio(int connectionId)
diff -ru old/sipXmediaLib/include/mp/MpCallFlowGraph.h
new/sipXmediaLib/include/mp/MpCallFlowGraph.h
--- old/sipXmediaLib/include/mp/MpCallFlowGraph.h Mon Mar 5 11:56:12 2007
+++ new/sipXmediaLib/include/mp/MpCallFlowGraph.h Mon Mar 5 21:32:51 2007
@@ -113,6 +113,7 @@
RECORDER_SPKR32K,
RECORDER_ECHO_IN32,
#endif // HIGH_SAMPLERATE_AUDIO ]
+ RECORDER_CALL,
MAX_RECORDERS = 10
};
@@ -170,14 +171,7 @@
OsProtectedEvent* recordEvent = NULL);
- OsStatus record(int timeMS, int silenceLength, const char* micName = NULL,
- const char* echoOutName = NULL, const char* spkrName = NULL,
- const char* mic32Name = NULL, const char* spkr32Name = NULL,
- const char* echoIn8Name = NULL, const char* echoIn32Name =
NULL,
- const char* playName = NULL,
- int toneOptions = 0,
- int repeat = 0, OsNotification* completion = NULL,
- MprRecorder::RecordFileFormat format =
MprRecorder::RAW_PCM_16);
+ OsStatus record(int timeMS, int silenceLength, const char* micName = NULL,
const char* echoOutName = NULL, const char* spkrName = NULL, const char*
mic32Name = NULL, const char* spkr32Name = NULL, const char* echoIn8Name =
NULL, const char* echoIn32Name = NULL, const char* playName = NULL, const
char* callName = NULL, int toneOptions = 0, int repeat = 0,
OsNotification* completion = NULL, MprRecorder::RecordFileFormat format =
MprRecorder::RAW_PCM_16);
OsStatus startRecording(const char* audioFileName, UtlBoolean repeat,
@@ -469,6 +463,9 @@
#endif
MprMixer* mpTFsMicMixer;
MprMixer* mpTFsBridgeMixer;
+ MprMixer* mpCallrecMixer;
+ MprSplitter* mpMicCallrecSplitter;
+ MprSplitter* mpSpeakerCallrecSplitter;
MprSplitter* mpToneFileSplitter;
MprToSpkr* mpToSpkr;
MprToneGen* mpToneGen;
@@ -541,6 +538,20 @@
* @returns <b>TRUE</b> if the message was handled
* @returns <b>FALSE</b> otherwise.
*/
+
+ /// Handle the ON_MPRRECORDER_ENABLED message.
+ UtlBoolean handleOnMprRecorderEnabled(MpFlowGraphMsg& rMsg);
+ /**<
+ * @returns <b>TRUE</b> if the message was handled
+ * @returns <b>FALSE</b> otherwise.
+ */
+
+ /// Handle the ON_MPRRECORDER_ENABLED message.
+ UtlBoolean handleOnMprRecorderDisabled(MpFlowGraphMsg& rMsg);
+ /**<
+ * @returns <b>TRUE</b> if the message was handled
+ * @returns <b>FALSE</b> otherwise.
+ */
#ifdef DEBUG_POSTPONE /* [ */
/// sends a message requesting a delay for race condition detection...
diff -ru old/sipXmediaLib/src/mp/MpCallFlowGraph.cpp
new/sipXmediaLib/src/mp/MpCallFlowGraph.cpp
--- old/sipXmediaLib/src/mp/MpCallFlowGraph.cpp Mon Mar 5 11:56:40 2007
+++ new/sipXmediaLib/src/mp/MpCallFlowGraph.cpp Mon Mar 5 21:28:36 2007
@@ -165,8 +165,14 @@
samplesPerFrame, samplesPerSec);
mpTFsBridgeMixer = new MprMixer("TFsBridgeMixer", 2,
samplesPerFrame, samplesPerSec);
+ mpCallrecMixer = new MprMixer("CallrecMixer", 2,
+ samplesPerFrame, samplesPerSec);
mpToneFileSplitter = new MprSplitter("ToneFileSplitter", 2,
samplesPerFrame, samplesPerSec);
+ mpMicCallrecSplitter = new MprSplitter("MicCallrecSplitter", 2,
+ samplesPerFrame, samplesPerSec);
+ mpSpeakerCallrecSplitter = new MprSplitter("SpeakerCallrecSplitter", 2,
+ samplesPerFrame, samplesPerSec);
#ifndef DISABLE_LOCAL_AUDIO // [
mpToSpkr = new MprToSpkr("ToSpkr",
samplesPerFrame, samplesPerSec,
@@ -195,7 +201,10 @@
#endif // DISABLE_LOCAL_AUDIO ]
res = addResource(*mpTFsMicMixer); assert(res == OS_SUCCESS);
res = addResource(*mpTFsBridgeMixer); assert(res == OS_SUCCESS);
+ res = addResource(*mpCallrecMixer); assert(res == OS_SUCCESS);
res = addResource(*mpToneFileSplitter); assert(res == OS_SUCCESS);
+ res = addResource(*mpMicCallrecSplitter); assert(res == OS_SUCCESS);
+ res = addResource(*mpSpeakerCallrecSplitter); assert(res == OS_SUCCESS);
#ifndef DISABLE_LOCAL_AUDIO
res = addResource(*mpToSpkr); assert(res == OS_SUCCESS);
#endif
@@ -233,14 +242,29 @@
assert(res == OS_SUCCESS);
#endif
- // connect TFsMicMixer -> Bridge
- res = addLink(*mpTFsMicMixer, 0, *mpBridge, 0);
+ // connect TFsMicMixer -> mpMicCallrecSplitter
+ res = addLink(*mpTFsMicMixer, 0, *mpMicCallrecSplitter, 0);
+ assert(res == OS_SUCCESS);
+
+ // connect mpMicCallrecSplitter -> Bridge
+ res = addLink(*mpMicCallrecSplitter, 0, *mpBridge, 0);
+ assert(res == OS_SUCCESS);
+
+ //////////////////////////////////////////////////////////////////////////
+ // connect Bridge -> TFsBridgeMixer through mpSpeakerCallrecSplitter
+
+ res = addLink(*mpBridge, 0, *mpSpeakerCallrecSplitter, 0);
+ assert(res == OS_SUCCESS);
+
+ res = addLink(*mpSpeakerCallrecSplitter, 0, *mpTFsBridgeMixer, 1);
assert(res == OS_SUCCESS);
//////////////////////////////////////////////////////////////////////////
- // connect Bridge -> TFsBridgeMixer
+ // connect mpSpeakerCallrecSplitter and mpMicCallrecSplitter into
mpCallrecMixer
+ res = addLink(*mpMicCallrecSplitter, 1, *mpCallrecMixer, 0);
+ assert(res == OS_SUCCESS);
- res = addLink(*mpBridge, 0, *mpTFsBridgeMixer, 1);
+ res = addLink(*mpSpeakerCallrecSplitter, 1, *mpCallrecMixer, 1);
assert(res == OS_SUCCESS);
//////////////////////////////////////////////////////////////////////////
@@ -282,6 +306,11 @@
// disable the from file
boolRes = mpFromFile->disable(); assert(boolRes);
+ // disable mpCallrecMixer and splitters, they are enabled when we want to
start recording
+ boolRes = mpCallrecMixer->disable(); assert(boolRes);
+ boolRes = mpMicCallrecSplitter->disable(); assert(boolRes);
+ boolRes = mpSpeakerCallrecSplitter->disable(); assert(boolRes);
+
#ifndef DISABLE_LOCAL_AUDIO // [
// disable the FromMic, EchoCancel, PreProcess and ToSpkr -- we cannot have
focus yet...
boolRes = mpFromMic->disable(); assert(boolRes);
@@ -303,6 +332,12 @@
boolRes = mpTFsBridgeMixer->setWeight(0, 0); assert(boolRes);
boolRes = mpTFsBridgeMixer->setWeight(1, 1); assert(boolRes);
+ // set up weights for callrec mixer as they are zeroed in constructor
+ // input 0 is from mic
+ boolRes = mpCallrecMixer->setWeight(1, 0); assert(boolRes);
+ // input 1 is speaker
+ boolRes = mpCallrecMixer->setWeight(1, 1); assert(boolRes);
+
#ifdef INCLUDE_RTCP /* [ */
// All the Media Resource seemed to have been started successfully.
// Let's now create an RTCP Session so that we may be prepared to
@@ -361,6 +396,14 @@
*mpEchoCancel, 1);
assert(res == OS_SUCCESS);
+ // create Call recorder and connect it to mpCallrecMixer
+ mpRecorders[RECORDER_CALL] =
+ new MprRecorder("RecordCall", samplesPerFrame, samplesPerSec);
+ res = addResource(*(mpRecorders[RECORDER_CALL]));
+ assert(res == OS_SUCCESS);
+ res = addLink(*mpCallrecMixer, 0, *(mpRecorders[RECORDER_CALL]), 0);
+ assert(res == OS_SUCCESS);
+
#ifdef HIGH_SAMPLERATE_AUDIO // [
mpRecorders[RECORDER_ECHO_IN32] =
new MprRecorder("RecordEchoIn32", samplesPerFrame, samplesPerSec);
@@ -479,6 +522,13 @@
res = removeLink(*mpToneFileSplitter, 0); assert(res == OS_SUCCESS);
res = removeLink(*mpToneFileSplitter, 1); assert(res == OS_SUCCESS);
+ // remove links of call recording
+ res = removeLink(*mpCallrecMixer, 0); assert(res == OS_SUCCESS);
+ res = removeLink(*mpMicCallrecSplitter, 0); assert(res == OS_SUCCESS);
+ res = removeLink(*mpMicCallrecSplitter, 1); assert(res == OS_SUCCESS);
+ res = removeLink(*mpSpeakerCallrecSplitter, 0); assert(res == OS_SUCCESS);
+ res = removeLink(*mpSpeakerCallrecSplitter, 1); assert(res == OS_SUCCESS);
+
// now remove (and destroy) the resources
#ifndef DISABLE_LOCAL_AUDIO
res = removeResource(*mpFromMic);
@@ -523,6 +573,19 @@
assert(res == OS_SUCCESS);
delete mpFromFile;
+ // kill call recording resources
+ res = removeResource(*mpMicCallrecSplitter);
+ assert(res == OS_SUCCESS);
+ delete mpMicCallrecSplitter;
+
+ res = removeResource(*mpSpeakerCallrecSplitter);
+ assert(res == OS_SUCCESS);
+ delete mpSpeakerCallrecSplitter;
+
+ res = removeResource(*mpCallrecMixer);
+ assert(res == OS_SUCCESS);
+ delete mpCallrecMixer;
+
for (i=0; i<MAX_RECORDERS; i++) {
if (NULL != mpRecorders[i]) {
res = removeResource(*mpRecorders[i]);
@@ -850,7 +913,7 @@
res = record(ms, 999999, created_micNamePtr, created_echoOutNamePtr,
created_spkrNamePtr, created_mic32NamePtr, created_spkr32NamePtr,
created_echoIn8NamePtr, created_echoIn32NamePtr,
- playFilename, 0, 0, NULL);
+ playFilename, NULL, 0, 0, NULL);
playIndex++;
strcpy(saved_playFilename,playFilename);
@@ -890,7 +953,7 @@
}
return record(ms, silenceLength, NULL, NULL, fileName,
- NULL, NULL, NULL, NULL, NULL, 0, 0, recordEvent, format);
+ NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, recordEvent,
format);
}
@@ -910,7 +973,7 @@
OsTime maxEventTime(timeoutSecs, 0);
record(ms, silenceLength, fileName, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, 0, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL, 0, 0,
NULL, MprRecorder::WAV_PCM_16);
// Wait until the call sets the number of connections
@@ -960,7 +1023,7 @@
record(ms, silenceLength, NULL, NULL, fileName,
- NULL, NULL, NULL, NULL, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL, NULL, 0,
0, recordEvent,format);
if (dtmfTerm)
@@ -1004,55 +1067,54 @@
return ret;
}
-OsStatus MpCallFlowGraph::record(int ms, int silenceLength, const char*
micName,
- const char* echoOutName, const char* spkrName,
- const char* mic32Name, const char* spkr32Name,
- const char* echoIn8Name, const char* echoIn32Name,
- const char* playName, int toneOptions,
- int repeat, OsNotification* completion,
- MprRecorder::RecordFileFormat format)
+OsStatus MpCallFlowGraph::record(int timeMS, int silenceLength, const char*
micName /*= NULL*/, const char* echoOutName /*= NULL*/, const char* spkrName
/*= NULL*/, const char* mic32Name /*= NULL*/, const char* spkr32Name /*=
NULL*/, const char* echoIn8Name /*= NULL*/, const char* echoIn32Name /*=
NULL*/, const char* playName /*= NULL*/, const char* callName /*= NULL*/,
int toneOptions /*= 0*/, int repeat /*= 0*/, OsNotification* completion /*=
NULL*/, MprRecorder::RecordFileFormat format /*= MprRecorder::RAW_PCM_16*/)
{
if (NULL == this) {
MpMediaTask* pMT = MpMediaTask::getMediaTask(0);
MpCallFlowGraph* pIF = (MpCallFlowGraph*) pMT->getFocus();
if (NULL != pIF) {
- return pIF-> record(ms, silenceLength, micName, echoOutName, spkrName,
+ return pIF-> record(timeMS, silenceLength, micName, echoOutName,
spkrName,
mic32Name, spkr32Name, echoIn8Name, echoIn32Name,
- playName, toneOptions, repeat, completion);
+ playName, callName, toneOptions, repeat, completion);
}
return OS_INVALID;
}
if (NULL != micName) {
setupRecorder(RECORDER_MIC, micName,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != echoOutName) {
setupRecorder(RECORDER_ECHO_OUT, echoOutName,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != spkrName) {
setupRecorder(RECORDER_SPKR, spkrName,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != echoIn8Name) {
setupRecorder(RECORDER_ECHO_IN8, echoIn8Name,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
#ifdef HIGH_SAMPLERATE_AUDIO // [
if (NULL != mic32Name) {
setupRecorder(RECORDER_MIC32K, mic32Name,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != spkr32Name) {
setupRecorder(RECORDER_SPKR32K,spkr32Name,
- ms, silenceLength, completion, format);
+ timeMS, silenceLength, completion, format);
}
if (NULL != echoIn32Name) {
setupRecorder(RECORDER_ECHO_IN32,
- echoIn32Name, ms, silenceLength, completion, format);
+ echoIn32Name, timeMS, silenceLength, completion, format);
}
#endif // HIGH_SAMPLERATE_AUDIO ]
+ // set up call recorder
+ if (NULL != callName) {
+ setupRecorder(RECORDER_CALL, callName,
+ timeMS, silenceLength, completion, format);
+ }
return startRecording(playName, repeat, toneOptions, completion);
}
@@ -1101,6 +1163,12 @@
res = mpRecorders[which]->setup(file, format, timeMS, silenceLength,
(OsEvent*)event);
}
+ else
+ {
+ OsSysLog::add(FAC_AUDIO, PRI_ERR,
+ "setupRecorder failed to open file %s, error code is %i",
+ audioFileName, errno);
+ }
return (file != -1);
}
@@ -1727,6 +1795,12 @@
case MpFlowGraphMsg::FLOWGRAPH_SET_DTMF_NOTIFY:
retCode = handleSetDtmfNotify(*pMsg);
break;
+ case MpFlowGraphMsg::ON_MPRRECORDER_ENABLED:
+ retCode = handleOnMprRecorderEnabled(*pMsg);
+ break;
+ case MpFlowGraphMsg::ON_MPRRECORDER_DISABLED:
+ retCode = handleOnMprRecorderDisabled(*pMsg);
+ break;
default:
retCode = MpFlowGraphBase::handleMessage(*pMsg);
break;
@@ -2071,6 +2145,61 @@
mpFromStream->destroy(handle) ;
return TRUE ;
+}
+
+UtlBoolean MpCallFlowGraph::handleOnMprRecorderEnabled(MpFlowGraphMsg& rMsg)
+{
+ UtlBoolean boolRes;
+ int status = rMsg.getInt1();
+ MprRecorder* pRecorder = (MprRecorder*) rMsg.getPtr1() ;
+
+ // if this call recorder, also enable required resources
+ if (pRecorder && pRecorder == mpRecorders[RECORDER_CALL])
+ {
+ boolRes = mpCallrecMixer->enable();
+ assert(boolRes);
+
+ boolRes = mpMicCallrecSplitter->enable();
+ assert(boolRes);
+
+ boolRes = mpSpeakerCallrecSplitter->enable();
+ assert(boolRes);
+ }
+
+ return TRUE;
+}
+
+UtlBoolean MpCallFlowGraph::handleOnMprRecorderDisabled(MpFlowGraphMsg& rMsg)
+{
+ UtlBoolean boolRes;
+ int status = rMsg.getInt1();
+ MprRecorder* pRecorder = (MprRecorder*) rMsg.getPtr1() ;
+
+ if (pRecorder && pRecorder == mpRecorders[RECORDER_CALL])
+ {
+ // also disable mpCallrecMixer and splitters
+
+ /* checks just in case this gets called when
+ the resources have already been deleted
+ */
+ if (mpCallrecMixer)
+ {
+ boolRes = mpCallrecMixer->disable();
+ assert(boolRes);
+ }
+ if (mpMicCallrecSplitter)
+ {
+ boolRes = mpMicCallrecSplitter->disable();
+ assert(boolRes);
+ }
+ if (mpSpeakerCallrecSplitter)
+ {
+ boolRes = mpSpeakerCallrecSplitter->disable();
+ assert(boolRes);
+ }
+ }
+
+ return TRUE;
}
/* ============================ FUNCTIONS ================================= */
_______________________________________________
sipxtapi-dev mailing list
[email protected]
List Archive: http://list.sipfoundry.org/archive/sipxtapi-dev/