]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Adding bugs to the core
authorAnthony Minessale <anthony.minessale@gmail.com>
Sat, 9 Sep 2006 03:39:28 +0000 (03:39 +0000)
committerAnthony Minessale <anthony.minessale@gmail.com>
Sat, 9 Sep 2006 03:39:28 +0000 (03:39 +0000)
This is the primary commit to add bugs to the core (media bugs that is)
Media bugs are kind of like what ChanSpy is in Asterisk only cooler (I wrote ChanSpy too so I can say that)

Here is an example of using them to record a call by the higher level switch_ivr functionality passed
up to the dialplan via mod_playback.

The call will be recorded while the some.wav plays then stop for the rest of the call (when some_other.wav plays)

The bugs may have bugs since this is 1 day's work so happy hunting ......

<extension name="42">
  <condition field="destination_number" expression="^42$">
   <action application="set" data="RECORD_TITLE=recording test"/>
   <action application="set" data="RECORD_ARTIST=FreeSWITCH"/>
    <action application="record_session" data="/tmp/rtest.wav"/>
    <action application="playback" data="/tmp/some.wav"/>
    <action application="stop_record_session" data="/tmp/rtest.wav"/>
    <action application="playback" data="/tmp/some_other.wav"/>
  </condition>
</extension>

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@2588 d0543943-73ff-0310-b7d9-9358b9ac24b2

src/include/switch_core.h
src/include/switch_ivr.h
src/include/switch_types.h
src/include/switch_utils.h
src/mod/applications/mod_conference/mod_conference.c
src/mod/applications/mod_playback/mod_playback.c
src/switch_core.c
src/switch_ivr.c

index 2ab0a9b9f5d0f24379958b61316690a8855fb36d..bb71b3a7e7d80a87861ab0b4d9c84d79cf7fd1db 100644 (file)
@@ -107,6 +107,50 @@ struct switch_core_port_allocator;
   \{ 
 */
 
+
+///\defgroup pa1 Media Bugs
+///\ingroup core1
+///\{
+
+/*!
+  \brief Add a media bug to the session
+  \param session the session to add the bug to
+  \param callback a callback for events
+  \param user_data arbitrary user data
+  \param new_bug pointer to assign new bug to
+  \return SWITCH_STATUS_SUCCESS if the operation was a success
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t *session,
+                                                                                                                 switch_media_bug_callback_t callback,
+                                                                                                                 void *user_data,
+                                                                                                                 switch_media_bug_t **new_bug);
+
+
+/*!
+  \brief Remove a media bug from the session
+  \param session the session to remove the bug from
+  \param bug bug to remove
+  \return SWITCH_STATUS_SUCCESS if the operation was a success
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove(switch_core_session_t *session, switch_media_bug_t **bug);
+
+/*!
+  \brief Remove all media bugs from the session
+  \param session the session to remove the bugs from
+  \return SWITCH_STATUS_SUCCESS if the operation was a success
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove_all(switch_core_session_t *session);
+
+/*!
+  \brief Read a frame from the bug
+  \param bug the bug to read from
+  \param frame the frame to write the data to
+  \return the amount of data 
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *bug, switch_frame_t *frame);
+
+///\}
+
 ///\defgroup pa1 Port Allocation
 ///\ingroup core1
 ///\{
@@ -119,7 +163,10 @@ struct switch_core_port_allocator;
   \param new pointer for the return value
   \return SWITCH_STATUS_SUCCESS if the operation was a success
 */
-SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start, switch_port_t end, uint8_t inc, switch_core_port_allocator_t **new_allocator);
+SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start,
+                                                                                                                          switch_port_t end,
+                                                                                                                          uint8_t inc,
+                                                                                                                          switch_core_port_allocator_t **new_allocator);
 
 /*!
   \brief Get a port from the port allocator
index 6e0275257208380c466352baef8cdfcf27d18bbd..7d0ff8f226851a50d2fd2e8588e2d166d9dfda76 100644 (file)
@@ -94,7 +94,24 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_count(switch_core_sess
                                                                                                                                const char *terminators,
                                                                                                                                char *terminator,
                                                                                                                                unsigned int timeout);
-       
+
+/*!
+  \brief Record a session to disk
+  \param session the session to record
+  \param file the path to the file
+  \param fh file handle to use (NULL for builtin one)
+  \return SWITCH_STATUS_SUCCESS if all is well
+*/
+SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t *session, char *file,  switch_file_handle_t *fh);
+
+/*!
+  \brief Stop Recording a session
+  \param session the session to stop recording
+  \param file the path to the file
+  \return SWITCH_STATUS_SUCCESS if all is well
+*/
+SWITCH_DECLARE(switch_status_t) switch_ivr_stop_record_session(switch_core_session_t *session, char *file);
+
 /*!
   \brief play a file from the disk to the session
   \param session the session to play the file too
index 9b4dd87bfd603474a42c6de382a108effabb6371..1b7897db3b099ff3b108bda33ed45f88873811c3 100644 (file)
@@ -80,6 +80,12 @@ typedef enum {
        SWITCH_BITPACK_MODE_AAL2
 } switch_bitpack_mode_t;
 
+typedef enum {
+       SWITCH_ABC_TYPE_INIT,
+       SWITCH_ABC_TYPE_READ,
+       SWITCH_ABC_TYPE_WRITE,
+       SWITCH_ABC_TYPE_CLOSE,
+} switch_abc_type_t;
 
 typedef struct {
        switch_byte_t *buf;
@@ -739,9 +745,8 @@ typedef struct switch_file_interface switch_file_interface_t;
 typedef struct switch_speech_interface switch_speech_interface_t;
 typedef struct switch_directory_interface switch_directory_interface_t;
 typedef struct switch_core_port_allocator switch_core_port_allocator_t;
-typedef struct switch_audio_bug switch_audio_bug_t;
-typedef void (*switch_audio_bug_read_callback_t)(switch_audio_bug_t *);
-typedef void (*switch_audio_bug_write_callback_t)(switch_audio_bug_t *);
+typedef struct switch_media_bug switch_media_bug_t;
+typedef void (*switch_media_bug_callback_t)(switch_media_bug_t *, void *, switch_abc_type_t);
 typedef void (*switch_application_function_t)(switch_core_session_t *, char *);
 typedef void (*switch_event_callback_t)(switch_event_t *);
 typedef switch_caller_extension_t *(*switch_dialplan_hunt_function_t)(switch_core_session_t *);
@@ -787,7 +792,7 @@ struct switch_channel;
 /*! \brief A core session representing a call and all of it's resources */
 struct switch_core_session;
 /*! \brief An audio bug */
-struct switch_audio_bug;
+struct switch_media_bug;
 
 SWITCH_END_EXTERN_C
 
index b008c62ad288591578b971e8232c400bbb6830df..00cd5de496d45e4b7ce0e4d0c9a2972e701a9ea7 100644 (file)
@@ -50,6 +50,12 @@ SWITCH_BEGIN_EXTERN_C
 #endif
 
 
+#define SWITCH_SMAX 32767
+#define SWITCH_SMIN -32768
+#define switch_normalize_to_16bit(n) if (n > SWITCH_SMAX) n = SWITCH_SMAX / 2; else if (n < SWITCH_SMIN) n = SWITCH_SMIN / 2;
+
+
+
 /*!
   \brief Evaluate the truthfullness of a string expression
   \param expr a string expression
index 2aa596a3312dfa1cdcde0ceb48861663c2b7e104..6d2eba8e372767b021c2ed85a4f185229d5e57d5 100644 (file)
@@ -228,10 +228,6 @@ static uint32_t next_member_id(void)
 }
 
 
-#define SMAX 32767
-#define SMIN -32768
-#define normalize_to_16bit(n) if (n > SMAX) n = SMAX; else if (n < SMIN) n = SMIN;
-
 static void switch_change_sln_volume(int16_t *data, uint32_t samples, int32_t vol)
 {
        int16_t *p = data;
@@ -249,7 +245,7 @@ static void switch_change_sln_volume(int16_t *data, uint32_t samples, int32_t vo
 
        for (x = 0; x < samples; x++) {
                b = (int32_t)((double)p[x] * mult);
-               normalize_to_16bit(b);
+               switch_normalize_to_16bit(b);
                p[x] = (int16_t) b;
        }
 }
@@ -600,7 +596,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
                                
                                                for (x = 0; x < imember->read / 2; x++) {
                                                        int32_t z = muxed[x] + bptr[x];
-                                                       normalize_to_16bit(z);
+                                                       switch_normalize_to_16bit(z);
                                                        muxed[x] = (int16_t)z;
                                                }
                                                
index cc167aae3a8b65266122d052e67abf157bd8a3e2..50e963b02352ae4bdf4fef05e82e28985442a852 100644 (file)
@@ -133,6 +133,26 @@ static void record_function(switch_core_session_t *session, char *data)
 }
 
 
+static void record_session_function(switch_core_session_t *session, char *data)
+{
+       switch_channel_t *channel;
+       channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+
+       switch_ivr_record_session(session, data, NULL); 
+}
+
+
+static void stop_record_session_function(switch_core_session_t *session, char *data)
+{
+       switch_channel_t *channel;
+       channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+
+       switch_ivr_stop_record_session(session, data);  
+}
+
+
 static const switch_application_interface_t speak_application_interface = {
        /*.interface_name */ "speak",
        /*.application_function */ speak_function
@@ -145,11 +165,27 @@ static const switch_application_interface_t record_application_interface = {
        &speak_application_interface
 };
 
+
+static const switch_application_interface_t record_session_application_interface = {
+       /*.interface_name */ "record_session",
+       /*.application_function */ record_session_function,
+       NULL,NULL,NULL,
+       &record_application_interface
+};
+
+
+static const switch_application_interface_t stop_record_session_application_interface = {
+       /*.interface_name */ "stop_record_session",
+       /*.application_function */ stop_record_session_function,
+       NULL,NULL,NULL,
+       &record_session_application_interface
+};
+
 static const switch_application_interface_t playback_application_interface = {
        /*.interface_name */ "playback",
        /*.application_function */ playback_function,
        NULL,NULL,NULL,
-       /*.next*/                                 &record_application_interface
+       /*.next*/                                 &stop_record_session_application_interface
 };
 
 static const switch_loadable_module_interface_t mod_playback_module_interface = {
index 95ff1364bd3746c523d7f2a352b9a2b1ce1d21b7..b389393155398932a41e52c6db87cc959c74bf36 100644 (file)
 #define SWITCH_SQL_QUEUE_LEN 2000
 
 
-struct switch_audio_bug {
-       switch_codec_t *read_codec;
-    switch_codec_t *write_codec;
+struct switch_media_bug {
        switch_buffer_t *raw_write_buffer;
        switch_buffer_t *raw_read_buffer;
-       uint8_t data[SWITCH_RECCOMMENDED_BUFFER_SIZE];
-       switch_audio_bug_read_callback_t read_callback;
-       switch_audio_bug_read_callback_t write_callback;
-       struct switch_audio_bug *next;
+       switch_media_bug_callback_t callback;
+       switch_mutex_t *read_mutex;
+       switch_mutex_t *write_mutex;
+       switch_core_session_t *session;
+       void *user_data;
+       struct switch_media_bug *next;
 };
        
 struct switch_core_session {
@@ -101,6 +101,8 @@ struct switch_core_session {
        void *private_info;
        switch_queue_t *event_queue;
        switch_queue_t *private_event_queue;
+       switch_thread_rwlock_t *bug_rwlock;
+       switch_media_bug_t *bugs;
 };
 
 SWITCH_DECLARE_DATA switch_directories SWITCH_GLOBAL_dirs;
@@ -149,6 +151,175 @@ static void db_pick_path(char *dbname, char *buf, switch_size_t size)
        }
 }
 
+static void switch_core_media_bug_destroy(switch_media_bug_t *bug)
+{
+       switch_buffer_destroy(&bug->raw_read_buffer);
+       switch_buffer_destroy(&bug->raw_write_buffer);
+}
+
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *bug, switch_frame_t *frame)
+{
+       uint32_t bytes = 0;
+       uint8_t data[SWITCH_RECCOMMENDED_BUFFER_SIZE] = {0};
+       uint32_t datalen = 0;
+       int16_t *dp, *fp;
+       uint32_t x;
+       uint32_t rlen = switch_buffer_inuse(bug->raw_read_buffer);
+       uint32_t wlen = switch_buffer_inuse(bug->raw_write_buffer);
+       uint32_t blen;
+       uint32_t rdlen = 0;
+       uint32_t maxlen;
+
+       if (!rlen && !wlen) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       maxlen = sizeof(data) > frame->buflen ? frame->buflen :  sizeof(data);
+       if ((rdlen = rlen > wlen ? wlen : rlen) > maxlen) {
+               rdlen = maxlen;
+       }
+       
+       frame->datalen = 0;
+
+       if (rlen) {
+               switch_mutex_lock(bug->read_mutex);
+               
+               frame->datalen = (uint32_t) switch_buffer_read(bug->raw_read_buffer,
+                                                                                                          frame->data,
+                                                                                                          rdlen);
+               switch_mutex_unlock(bug->read_mutex);
+       }
+
+       if (wlen) {
+               switch_mutex_lock(bug->write_mutex);
+               datalen = (uint32_t) switch_buffer_read(bug->raw_write_buffer,
+                                                                                               data,
+                                                                                               rdlen);
+               switch_mutex_unlock(bug->write_mutex);
+       }
+
+       bytes = (datalen > frame->datalen) ? datalen : frame->datalen;
+
+       if (bytes) {
+               dp = (int16_t *) data;
+               fp = (int16_t *) frame->data;
+               
+               rlen = frame->datalen / 2;
+               wlen = datalen / 2;
+               blen = bytes / 2;
+               
+               for(x = 0; x < blen; x++) {
+                       int32_t z = 0;
+
+                       if (x < rlen) {
+                               z += (int32_t) *(fp+x);
+                       }
+                       if (x < wlen) {
+                               z += (int32_t)*(dp+x);
+                       }
+                       switch_normalize_to_16bit(z);
+                       *(fp+x) = (int16_t) z;
+               }
+
+               frame->datalen = bytes;
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       return SWITCH_STATUS_FALSE;
+}
+
+#define MAX_BUG_BUFFER 1024 * 512
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t *session,
+                                                                                                                 switch_media_bug_callback_t callback,
+                                                                                                                 void *user_data,
+                                                                                                                 switch_media_bug_t **new_bug)
+
+{
+       switch_media_bug_t *bug;
+       switch_size_t bytes;
+
+       if (!(bug = switch_core_session_alloc(session, sizeof(*bug)))) {
+               return SWITCH_STATUS_MEMERR;
+       }
+
+       bug->callback = callback;
+       bug->user_data = user_data;
+       bug->session = session;
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Attaching BUG to %s\n", switch_channel_get_name(session->channel));
+       bytes = session->read_codec->implementation->bytes_per_frame * 2;
+       switch_buffer_create_dynamic(&bug->raw_read_buffer, bytes, bytes, MAX_BUG_BUFFER);
+       bytes = session->write_codec->implementation->bytes_per_frame * 2;
+       switch_buffer_create_dynamic(&bug->raw_write_buffer, bytes, bytes, MAX_BUG_BUFFER);
+       switch_mutex_init(&bug->read_mutex, SWITCH_MUTEX_NESTED, session->pool);
+       switch_mutex_init(&bug->write_mutex, SWITCH_MUTEX_NESTED, session->pool);
+
+       switch_thread_rwlock_wrlock(session->bug_rwlock);
+       bug->next = session->bugs;
+       session->bugs = bug;
+       switch_thread_rwlock_unlock(session->bug_rwlock);
+       *new_bug = bug;
+
+       if (bug->callback) {
+               bug->callback(bug, bug->user_data, SWITCH_ABC_TYPE_INIT);
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove_all(switch_core_session_t *session)
+{
+       switch_media_bug_t *bp;
+
+       if (session->bugs) {
+               switch_thread_rwlock_wrlock(session->bug_rwlock);
+               for (bp = session->bugs; bp; bp = bp->next) {
+                       if (bp->callback) {
+                               bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_CLOSE);
+                       }
+                       switch_core_media_bug_destroy(bp);
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing BUG from %s\n", switch_channel_get_name(session->channel));
+               }
+               switch_thread_rwlock_unlock(session->bug_rwlock);
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       return SWITCH_STATUS_FALSE;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove(switch_core_session_t *session, switch_media_bug_t **bug)
+{
+       switch_media_bug_t *bp = NULL, *last = NULL;
+
+       if (session->bugs) {
+               switch_thread_rwlock_wrlock(session->bug_rwlock);
+               for (bp = session->bugs; bp; bp = bp->next) {
+                       if (bp == *bug) {
+                               if (last) {
+                                       last->next = bp->next;
+                               } else {
+                                       session->bugs = bp->next;
+                               }
+                               break;
+                       }
+                       last = bp;
+               }
+               switch_thread_rwlock_unlock(session->bug_rwlock);
+
+               if (bp) {
+                       if (bp->callback) {
+                               bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_CLOSE);
+                       }
+                       switch_core_media_bug_destroy(bp);
+                       *bug = NULL;
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing BUG from %s\n", switch_channel_get_name(session->channel));
+                       return SWITCH_STATUS_SUCCESS;
+               }
+       }
+
+       return SWITCH_STATUS_FALSE;
+}
+
 struct switch_core_port_allocator {
        switch_port_t start;
        switch_port_t end;
@@ -158,7 +329,10 @@ struct switch_core_port_allocator {
        switch_memory_pool_t *pool;
 };
 
-SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start, switch_port_t end, uint8_t inc, switch_core_port_allocator_t **new_allocator)
+SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start,
+                                                                                                                          switch_port_t end,
+                                                                                                                          uint8_t inc,
+                                                                                                                          switch_core_port_allocator_t **new_allocator)
 {
        switch_status_t status;
        switch_memory_pool_t *pool;
@@ -1362,7 +1536,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
 {
        switch_io_event_hook_read_frame_t *ptr;
        switch_status_t status;
-       int need_codec, perfect;
+       int need_codec, perfect, do_bugs = 0;
  top:
        
        status = SWITCH_STATUS_FALSE;
@@ -1420,6 +1594,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
                need_codec = TRUE;
        }
 
+       if (session->bugs && !need_codec) {
+               do_bugs = 1;
+               need_codec = 1;
+       }
+
        if (status == SWITCH_STATUS_SUCCESS && need_codec) {
                switch_frame_t *enc_frame, *read_frame = *frame;
 
@@ -1472,6 +1651,24 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
                        read_frame->rate = session->read_resampler->to_rate;
                }
 
+               if (session->bugs) {
+                       switch_media_bug_t *bp;
+                       switch_thread_rwlock_rdlock(session->bug_rwlock);
+                       for (bp = session->bugs; bp; bp = bp->next) {
+                               switch_mutex_lock(bp->read_mutex);
+                               switch_buffer_write(bp->raw_read_buffer, read_frame->data, read_frame->datalen);
+                               if (bp->callback) {
+                                       bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ);
+                               }
+                               switch_mutex_unlock(bp->read_mutex);
+                       }
+                       switch_thread_rwlock_unlock(session->bug_rwlock);
+               }
+
+               if (do_bugs) {
+                       goto done;
+               }
+
                if (session->read_codec) {
                        if ((*frame)->datalen == session->read_codec->implementation->bytes_per_frame) {
                                perfect = TRUE;
@@ -1587,7 +1784,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess
 
        switch_status_t status = SWITCH_STATUS_FALSE;
        switch_frame_t *enc_frame = NULL, *write_frame = frame;
-       unsigned int flag = 0, need_codec = 0, perfect = 0;
+       unsigned int flag = 0, need_codec = 0, perfect = 0, do_bugs = 0, do_write = 0;
        switch_io_flag_t io_flag = SWITCH_IO_FLAG_NOOP;
 
        assert(session != NULL);
@@ -1620,6 +1817,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess
                need_codec = TRUE;
        }
 
+       if (session->bugs && !need_codec) {
+               do_bugs = 1;
+               need_codec = 1;
+       }
+
        if (need_codec) {
                if (frame->codec) {
                        session->raw_write_frame.datalen = session->raw_write_frame.buflen;
@@ -1680,7 +1882,23 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess
                        write_frame->rate = session->write_resampler->to_rate;
                }
 
-
+               if (session->bugs) {
+                       switch_media_bug_t *bp;
+                       switch_thread_rwlock_rdlock(session->bug_rwlock);
+                       for (bp = session->bugs; bp; bp = bp->next) {
+                               switch_mutex_lock(bp->write_mutex);
+                               switch_buffer_write(bp->raw_write_buffer, write_frame->data, write_frame->datalen);
+                               switch_mutex_unlock(bp->write_mutex);
+                               if (bp->callback) {
+                                       bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE);
+                               }
+                       }
+                       switch_thread_rwlock_unlock(session->bug_rwlock);
+               }
+               if (do_bugs) {
+                       do_write = 1;
+                       goto done;
+               }
                if (session->write_codec) {
                        if (write_frame->datalen == session->write_codec->implementation->bytes_per_frame) {
                                perfect = TRUE;
@@ -1819,6 +2037,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess
                        }
                }
        } else {
+               do_write = 1;
+       }
+
+ done:
+       if (do_write) {
                return perform_write(session, frame, timeout, io_flag, stream_id);
        }
 
@@ -2658,6 +2881,7 @@ SWITCH_DECLARE(void) switch_core_session_destroy(switch_core_session_t **session
                switch_event_fire(&event);
        }
 
+       switch_core_media_bug_remove_all(*session);
        switch_buffer_destroy(&(*session)->raw_read_buffer);
        switch_buffer_destroy(&(*session)->raw_write_buffer);
        switch_channel_uninit((*session)->channel);
@@ -2889,6 +3113,7 @@ SWITCH_DECLARE(switch_core_session_t *) switch_core_session_request(const switch
        session->enc_read_frame.buflen = sizeof(session->enc_read_buf);
 
        switch_mutex_init(&session->mutex, SWITCH_MUTEX_NESTED, session->pool);
+       switch_thread_rwlock_create(&session->bug_rwlock, session->pool);
        switch_thread_cond_create(&session->cond, session->pool);
        switch_thread_rwlock_create(&session->rwlock, session->pool);
 
index 89ab3ccc3c5b57e559aa33f2db406545c5862ca7..52c62c4d91e43e2c905d7e4f0e55cb84e5b40796 100644 (file)
@@ -448,6 +448,138 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se
        return status;
 }
 
+static void record_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
+{
+       switch_file_handle_t *fh = (switch_file_handle_t *) user_data;
+       uint8_t data[SWITCH_RECCOMMENDED_BUFFER_SIZE];
+       switch_frame_t frame = {0};
+
+       frame.data = data;
+       frame.buflen = SWITCH_RECCOMMENDED_BUFFER_SIZE;
+       
+       switch(type) {
+       case SWITCH_ABC_TYPE_INIT:
+               break;
+       case SWITCH_ABC_TYPE_CLOSE:
+               switch_core_file_close(fh);
+       case SWITCH_ABC_TYPE_READ:
+               if (fh) {
+                       switch_size_t len;
+
+                       if (switch_core_media_bug_read(bug, &frame) == SWITCH_STATUS_SUCCESS) {
+                               len = (switch_size_t) frame.datalen / 2;
+                               switch_core_file_write(fh, frame.data, &len);
+                       }
+               }
+               break;
+       case SWITCH_ABC_TYPE_WRITE:
+               break;
+       }
+}
+
+SWITCH_DECLARE(switch_status_t) switch_ivr_stop_record_session(switch_core_session_t *session, char *file) 
+{
+       switch_media_bug_t *bug;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+
+       assert(channel != NULL);
+       if ((bug = switch_channel_get_private(channel, file))) {
+               switch_channel_set_private(channel, file, NULL);
+               switch_core_media_bug_remove(session, &bug);
+               return SWITCH_STATUS_SUCCESS;
+       }
+       
+       return SWITCH_STATUS_FALSE;
+       
+}
+
+SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t *session, char *file,  switch_file_handle_t *fh)
+{
+       switch_channel_t *channel;
+       switch_codec_t *read_codec;
+       char *p;
+       const char *vval;
+       switch_media_bug_t *bug;
+       switch_status_t status;
+
+       if (!fh) {
+               if (!(fh = switch_core_session_alloc(session, sizeof(*fh)))) {
+                       return SWITCH_STATUS_MEMERR;
+               }
+       }
+
+       channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+
+    read_codec = switch_core_session_get_read_codec(session);
+    assert(read_codec != NULL);
+
+    fh->channels = read_codec->implementation->number_of_channels;
+    fh->samplerate = read_codec->implementation->samples_per_second;
+
+
+    if (switch_core_file_open(fh,
+                              file,
+                              SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT,
+                              switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+        switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+        switch_core_session_reset(session);
+        return SWITCH_STATUS_GENERR;
+    }
+
+    switch_channel_answer(channel);
+
+       if ((p = switch_channel_get_variable(channel, "RECORD_TITLE"))) {
+               vval = (const char *) switch_core_session_strdup(session, p);
+               switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_TITLE, vval);
+               switch_channel_set_variable(channel, "RECORD_TITLE", NULL);
+       }
+
+       if ((p = switch_channel_get_variable(channel, "RECORD_COPYRIGHT"))) {
+               vval = (const char *) switch_core_session_strdup(session, p);
+               switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COPYRIGHT, vval);
+               switch_channel_set_variable(channel, "RECORD_COPYRIGHT", NULL);
+       }
+
+       if ((p = switch_channel_get_variable(channel, "RECORD_SOFTWARE"))) {
+               vval = (const char *) switch_core_session_strdup(session, p);
+               switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_SOFTWARE, vval);
+               switch_channel_set_variable(channel, "RECORD_SOFTWARE", NULL);
+       }
+
+       if ((p = switch_channel_get_variable(channel, "RECORD_ARTIST"))) {
+               vval = (const char *) switch_core_session_strdup(session, p);
+               switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_ARTIST, vval);
+               switch_channel_set_variable(channel, "RECORD_ARTIST", NULL);
+       }
+
+       if ((p = switch_channel_get_variable(channel, "RECORD_COMMENT"))) {
+               vval = (const char *) switch_core_session_strdup(session, p);
+               switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COMMENT, vval);
+               switch_channel_set_variable(channel, "RECORD_COMMENT", NULL);
+       }
+
+       if ((p = switch_channel_get_variable(channel, "RECORD_DATE"))) {
+               vval = (const char *) switch_core_session_strdup(session, p);
+               switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_DATE, vval);
+               switch_channel_set_variable(channel, "RECORD_DATE", NULL);
+       }
+
+       
+
+       if ((status = switch_core_media_bug_add(session,
+                                                                                       record_callback,
+                                                                                       fh,
+                                                                                       &bug)) != SWITCH_STATUS_SUCCESS) {
+               switch_core_file_close(fh);
+               return status;
+       }
+
+       switch_channel_set_private(channel, file, bug);
+       
+       return SWITCH_STATUS_SUCCESS;
+}
+
 SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *session, 
                                                                                                   switch_file_handle_t *fh,
                                                                                                   char *file,