[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/

Reply via email to