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/

Reply via email to