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/