PjSIP and memory captures...

SB
Simon Beginn
Tue, Aug 28, 2018 9:21 AM

Hi,
as first: I'm not a professional at this topic and I'm really trying my
best, but now I'm stuck and don't know what to do.
I want to modify my program, which is using PjSIP to create and do a
wav-file-record, to perform a memory_capture to a buffer - this requires
a existing call-conference - i guess (otherwise I will get error 70004).
The easy-to-use function "pjsua_recorder_create()" does all by itself
and i can get in a very simple way a plain old recorder to file. After
copy-and-pasting (i choosed this snippet from "pjsua_aud.c", because it
needs only little modifications to work with the memory_capture) THIS
code...

PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,
                        unsigned enc_type,
                        void *enc_param,
                        pj_ssize_t max_size,
                        unsigned options,
                        pjsua_recorder_id *p_id)
{
     enum Format
     {
     FMT_UNKNOWN,
     FMT_WAV,
     FMT_MP3,
     };
     unsigned slot, file_id;
     char path[PJ_MAXPATH];
     pj_str_t ext;
     int file_format;
     pj_pool_t *pool = NULL;
     pjmedia_port *port;
     pj_status_t status = PJ_SUCCESS;

     /* Filename must present */
     PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);

     /* Don't support max_size at present */
     PJ_ASSERT_RETURN(max_size == 0 || max_size == -1, PJ_EINVAL);

     /* Don't support encoding type at present */
     PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL);

     PJ_LOG(4,(THIS_FILE, "Creating recorder %.*s..",
           (int)filename->slen, filename->ptr));
     pj_log_push_indent();

     if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) {
     pj_log_pop_indent();
     return PJ_ETOOMANY;
     }

     /* Determine the file format */
     ext.ptr = filename->ptr + filename->slen - 4;
     ext.slen = 4;

     if (pj_stricmp2(&ext, ".wav") == 0)
     file_format = FMT_WAV;
     else if (pj_stricmp2(&ext, ".mp3") == 0)
     file_format = FMT_MP3;
     else {
     PJ_LOG(1,(THIS_FILE, "pjsua_recorder_create() error: unable to "
                  "determine file format for %.*s",
                  (int)filename->slen, filename->ptr));
     pj_log_pop_indent();
     return PJ_ENOTSUP;
     }

     PJSUA_LOCK();

     for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) {
     if (pjsua_var.recorder[file_id].port == NULL)
         break;
     }

     if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {
     /* This is unexpected */
     pj_assert(0);
     status = PJ_EBUG;
     goto on_return;
     }

     pj_memcpy(path, filename->ptr, filename->slen);
     path[filename->slen] = '\0';

     pool = pjsua_pool_create(get_basename(path, 
(unsigned)filename->slen), 1000,
                  1000);
     if (!pool) {
     status = PJ_ENOMEM;
     goto on_return;
     }

     if (file_format == FMT_WAV) {
     status = pjmedia_wav_writer_port_create(pool, path,
                         pjsua_var.media_cfg.clock_rate,
                         pjsua_var.mconf_cfg.channel_count,
                         pjsua_var.mconf_cfg.samples_per_frame,
                         pjsua_var.mconf_cfg.bits_per_sample,
                         options, 0, &port);
     } else {
     PJ_UNUSED_ARG(enc_param);
     port = NULL;
     status = PJ_ENOTSUP;
     }

     if (status != PJ_SUCCESS) {
     pjsua_perror(THIS_FILE, "Unable to open file for recording", status);
     goto on_return;
     }

     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
                    port, filename, &slot);
     if (status != PJ_SUCCESS) {
     pjmedia_port_destroy(port);
     goto on_return;
     }

     pjsua_var.recorder[file_id].port = port;
     pjsua_var.recorder[file_id].slot = slot;
     pjsua_var.recorder[file_id].pool = pool;

     if (p_id) *p_id = file_id;

     ++pjsua_var.rec_cnt;

     PJSUA_UNLOCK();

     PJ_LOG(4,(THIS_FILE, "Recorder created, id=%d, slot=%d", file_id, 
slot));

     pj_log_pop_indent();
     return PJ_SUCCESS;

on_return:
     PJSUA_UNLOCK();
     if (pool) pj_pool_release(pool);
     pj_log_pop_indent();
     return status;
}

...(of the normal recorder-to-file) to my program (and modified it) - it
create a 44 bytes big file and thats it. I guess my problem is, that my
modified version uses a new instance of a pjmedia_conf* (createt by
pjmedia_conf_create()), which is not connected with the incoming call.
So my questions are:

  • How I can bind an existing call (e.g. with its call_info) to a new
    conference (conf)?
  • Or how can I get the default "pjsua_var.mconf" to use it inside the
    mem_capture or at least with this extracted code snippet?
  • Or has anyone a good snippet with an example of the usage of PjSIP
    calls with mem_capture? This here is a good example of the
    high-level-recorder-to-wav function:
    "http://binerry.de/post/29180946733/raspberry-pi-caller-and-answering-machine"
  • but I would love to see a code snippet with the direct source code of
    the easy-to-use-function, so I can simply modify it to work with the
    mem_capture.
  • Can anyone explain me, how PjSIP-Conference/Media structures work
    (How to use ports, what are the slots - what can I get from the "normal"
    call_info - structures)? I've read the documentation really often, but I
    don't know what I'm doing wrong...

Btw this is my modified version - yeah I know it is dirty...

void VoIP_client::record_wav_start(string filename_out) {
     //Update call_info
     pjsua_call_get_info(this->call_id, &this->call_info);

     pj_str_t rec_file;
     pj_status_t status = PJ_ENOTFOUND;

     VoIP::voip_log->log_msg("[VoIP] [REC] Creating recorder for " + 
string(this->call_info.remote_info.ptr), LOG_DEBUG);

     // Create recorder for call
     //status = pjsua_recorder_create(pj_cstr(&rec_file, 
filename_out.c_str()), 0, NULL, 0, 0, &this->recorder_id);
     {
         const pj_str_t *filename = pj_cstr(&rec_file, 
filename_out.c_str());
         unsigned options = 0;
         unsigned pjsua_var_rec_cnt;
         pjmedia_port * pjsua_var_recorder_0_port;
         unsigned pjsua_var_recorder_0_slot;
         pj_pool_t * pjsua_var_recorder_0_pool;
         pjmedia_conf * pjsua_var_mconf;

         enum Format {
             FMT_UNKNOWN,
             FMT_WAV,
             FMT_MP3,
         };
         unsigned slot, file_id;
         char path[PJ_MAXPATH];
         pj_str_t ext;
         int file_format;
         pj_pool_t *pool = NULL;
         pjmedia_port *port;
         pj_status_t statust = PJ_SUCCESS;

         /* Filename must present */
         //PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);

         /* Don't support max_size at present */
         //PJ_ASSERT_RETURN(max_size == 0 || max_size == -1, PJ_EINVAL);

         /* Don't support encoding type at present */
         //PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL);

         PJ_LOG(4, ("", "Creating recorder %.*s..",
                 (int) filename->slen, filename->ptr));
         pj_log_push_indent();

         //if (pjsua_var_rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) {
         //    pj_log_pop_indent();
         //    return PJ_ETOOMANY;
         // }

         /* Determine the file format */
         ext.ptr = filename->ptr + filename->slen - 4;
         ext.slen = 4;

         if (pj_stricmp2(&ext, ".wav") == 0)
             file_format = FMT_WAV;
         else if (pj_stricmp2(&ext, ".mp3") == 0)
             file_format = FMT_MP3;
         else {
             PJ_LOG(1, ("", "pjsua_recorder_create() error: unable to "
                     "determine file format for %.*s",
                     (int) filename->slen, filename->ptr));
             pj_log_pop_indent();
             exit(PJ_ENOTSUP);
         }

         //for (file_id = 0; file_id < 
PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) {
         //    if (pjsua_var_recorder_0_port == NULL)
         //        break;
         //}

         //if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {
         //    /* This is unexpected */
         //    pj_assert(0);
         //    status = PJ_EBUG;
         //    goto on_return;
         //}

         pj_memcpy(path, filename->ptr, filename->slen);
         path[filename->slen] = '\0';

         pool = pjsua_pool_create("pool1", 1000,
                 1000);
         if (!pool) {
             statust = PJ_ENOMEM;
             goto on_return;
         }

         if (file_format == FMT_WAV) {
             statust = pjmedia_wav_writer_port_create(pool, path,
                     8000, //pjsua_var.media_cfg.clock_rate,
                     1, //pjsua_var.mconf_cfg.channel_count,
                     160, //pjsua_var.mconf_cfg.samples_per_frame,
                     16, //pjsua_var.mconf_cfg.bits_per_sample,
                     options, 0, &port);
         } else {
             port = NULL;
             exit(PJ_ENOTSUP);
         }

         if (statust != PJ_SUCCESS) {
             pjsua_perror("", "Unable to open file for recording", status);
             goto on_return;
         }

         status = pjmedia_conf_create(
                 pool,
                 1 /*Phone*/ + 1 /*Sound device*/,
                 8000 /*8kHz clock rate*/,
                 1 /*Channel count*/,
                 160 /*samples_per_frame*/,
                 16 /*bits_per_sample*/,
                 PJMEDIA_CONF_NO_DEVICE /* options */,
                 &pjsua_var_mconf /* result */);
         if (status != PJ_SUCCESS)
             VoIP::voip_log->log_msg("[VoIP] Error on creating 
conference for client " + string(this->call_info.remote_info.ptr) + ": " 
+ to_string(status), LOG_ERROR);

         statust = pjmedia_conf_add_port(pjsua_var_mconf, pool, port, 
filename, &slot);
         if (statust != PJ_SUCCESS) {
             pjmedia_port_destroy(port);
             goto on_return;
         }

         pjsua_var_recorder_0_port = port;
         pjsua_var_recorder_0_slot = slot;
         pjsua_var_recorder_0_pool = pool;

         //if (p_id) *p_id = file_id;

         //++pjsua_var.rec_cnt;

         PJ_LOG(4, ("", "Recorder created, id=%d, slot=%d", file_id, slot));

         pj_log_pop_indent();
         status = PJ_SUCCESS;

on_return:
         if (pool) pj_pool_release(pool);
         pj_log_pop_indent();
         //exit(statust);
     }
     if (status != PJ_SUCCESS)
         VoIP::voip_log->log_msg("[VoIP] [REC] Error recording answer" + 
to_string(status), LOG_ERROR);

     //Connect recorder with a new virtual phone conference
     pjsua_conf_port_id rec_port = 
pjsua_recorder_get_conf_port(this->recorder_id);
     pjsua_conf_connect(this->call_info.conf_slot, rec_port);

     VoIP::voip_log->log_msg("[VoIP] [REC] Created a recorder for " + 
string(this->call_info.remote_info.ptr), LOG_DEBUG);

}

Thank to anyone who can help or at least understands waht i want and
where i fail.
Simonmicro

Hi, as first: I'm not a professional at this topic and I'm really trying my best, but now I'm stuck and don't know what to do. I want to modify my program, which is using PjSIP to create and do a wav-file-record, to perform a memory_capture to a buffer - this requires a existing call-conference - i guess (otherwise I will get error 70004). The easy-to-use function "pjsua_recorder_create()" does all by itself and i can get in a very simple way a plain old recorder to file. After copy-and-pasting (i choosed this snippet from "pjsua_aud.c", because it needs only little modifications to work with the memory_capture) THIS code... ``` PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,                        unsigned enc_type,                        void *enc_param,                        pj_ssize_t max_size,                        unsigned options,                        pjsua_recorder_id *p_id) {     enum Format     {     FMT_UNKNOWN,     FMT_WAV,     FMT_MP3,     };     unsigned slot, file_id;     char path[PJ_MAXPATH];     pj_str_t ext;     int file_format;     pj_pool_t *pool = NULL;     pjmedia_port *port;     pj_status_t status = PJ_SUCCESS;     /* Filename must present */     PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);     /* Don't support max_size at present */     PJ_ASSERT_RETURN(max_size == 0 || max_size == -1, PJ_EINVAL);     /* Don't support encoding type at present */     PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL);     PJ_LOG(4,(THIS_FILE, "Creating recorder %.*s..",           (int)filename->slen, filename->ptr));     pj_log_push_indent();     if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) {     pj_log_pop_indent();     return PJ_ETOOMANY;     }     /* Determine the file format */     ext.ptr = filename->ptr + filename->slen - 4;     ext.slen = 4;     if (pj_stricmp2(&ext, ".wav") == 0)     file_format = FMT_WAV;     else if (pj_stricmp2(&ext, ".mp3") == 0)     file_format = FMT_MP3;     else {     PJ_LOG(1,(THIS_FILE, "pjsua_recorder_create() error: unable to "                  "determine file format for %.*s",                  (int)filename->slen, filename->ptr));     pj_log_pop_indent();     return PJ_ENOTSUP;     }     PJSUA_LOCK();     for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) {     if (pjsua_var.recorder[file_id].port == NULL)         break;     }     if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {     /* This is unexpected */     pj_assert(0);     status = PJ_EBUG;     goto on_return;     }     pj_memcpy(path, filename->ptr, filename->slen);     path[filename->slen] = '\0';     pool = pjsua_pool_create(get_basename(path, (unsigned)filename->slen), 1000,                  1000);     if (!pool) {     status = PJ_ENOMEM;     goto on_return;     }     if (file_format == FMT_WAV) {     status = pjmedia_wav_writer_port_create(pool, path,                         pjsua_var.media_cfg.clock_rate,                         pjsua_var.mconf_cfg.channel_count,                         pjsua_var.mconf_cfg.samples_per_frame,                         pjsua_var.mconf_cfg.bits_per_sample,                         options, 0, &port);     } else {     PJ_UNUSED_ARG(enc_param);     port = NULL;     status = PJ_ENOTSUP;     }     if (status != PJ_SUCCESS) {     pjsua_perror(THIS_FILE, "Unable to open file for recording", status);     goto on_return;     }     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,                    port, filename, &slot);     if (status != PJ_SUCCESS) {     pjmedia_port_destroy(port);     goto on_return;     }     pjsua_var.recorder[file_id].port = port;     pjsua_var.recorder[file_id].slot = slot;     pjsua_var.recorder[file_id].pool = pool;     if (p_id) *p_id = file_id;     ++pjsua_var.rec_cnt;     PJSUA_UNLOCK();     PJ_LOG(4,(THIS_FILE, "Recorder created, id=%d, slot=%d", file_id, slot));     pj_log_pop_indent();     return PJ_SUCCESS; on_return:     PJSUA_UNLOCK();     if (pool) pj_pool_release(pool);     pj_log_pop_indent();     return status; } ``` ...(of the normal recorder-to-file) to my program (and modified it) - it create a 44 bytes big file and thats it. I guess my problem is, that my modified version uses a new instance of a pjmedia_conf* (createt by pjmedia_conf_create()), which is not connected with the incoming call. So my questions are: - How I can bind an existing call (e.g. with its call_info) to a new conference (conf)? - Or how can I get the default "pjsua_var.mconf" to use it inside the mem_capture or at least with this extracted code snippet? - Or has anyone a good snippet with an example of the usage of PjSIP calls with mem_capture? This here is a good example of the high-level-recorder-to-wav function: "http://binerry.de/post/29180946733/raspberry-pi-caller-and-answering-machine" - but I would love to see a code snippet with the direct source code of the easy-to-use-function, so I can simply modify it to work with the mem_capture. - Can *anyone* explain me, how PjSIP-Conference/Media structures work (How to use ports, what are the slots - what can I get from the "normal" call_info - structures)? I've read the documentation really often, but I don't know what I'm doing wrong... Btw this is my modified version - yeah I know it is dirty... ``` void VoIP_client::record_wav_start(string filename_out) {     //Update call_info     pjsua_call_get_info(this->call_id, &this->call_info);     pj_str_t rec_file;     pj_status_t status = PJ_ENOTFOUND;     VoIP::voip_log->log_msg("[VoIP] [REC] Creating recorder for " + string(this->call_info.remote_info.ptr), LOG_DEBUG);     // Create recorder for call     //status = pjsua_recorder_create(pj_cstr(&rec_file, filename_out.c_str()), 0, NULL, 0, 0, &this->recorder_id);     {         const pj_str_t *filename = pj_cstr(&rec_file, filename_out.c_str());         unsigned options = 0;         unsigned pjsua_var_rec_cnt;         pjmedia_port * pjsua_var_recorder_0_port;         unsigned pjsua_var_recorder_0_slot;         pj_pool_t * pjsua_var_recorder_0_pool;         pjmedia_conf * pjsua_var_mconf;         enum Format {             FMT_UNKNOWN,             FMT_WAV,             FMT_MP3,         };         unsigned slot, file_id;         char path[PJ_MAXPATH];         pj_str_t ext;         int file_format;         pj_pool_t *pool = NULL;         pjmedia_port *port;         pj_status_t statust = PJ_SUCCESS;         /* Filename must present */         //PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);         /* Don't support max_size at present */         //PJ_ASSERT_RETURN(max_size == 0 || max_size == -1, PJ_EINVAL);         /* Don't support encoding type at present */         //PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL);         PJ_LOG(4, ("", "Creating recorder %.*s..",                 (int) filename->slen, filename->ptr));         pj_log_push_indent();         //if (pjsua_var_rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) {         //    pj_log_pop_indent();         //    return PJ_ETOOMANY;         // }         /* Determine the file format */         ext.ptr = filename->ptr + filename->slen - 4;         ext.slen = 4;         if (pj_stricmp2(&ext, ".wav") == 0)             file_format = FMT_WAV;         else if (pj_stricmp2(&ext, ".mp3") == 0)             file_format = FMT_MP3;         else {             PJ_LOG(1, ("", "pjsua_recorder_create() error: unable to "                     "determine file format for %.*s",                     (int) filename->slen, filename->ptr));             pj_log_pop_indent();             exit(PJ_ENOTSUP);         }         //for (file_id = 0; file_id < PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) {         //    if (pjsua_var_recorder_0_port == NULL)         //        break;         //}         //if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {         //    /* This is unexpected */         //    pj_assert(0);         //    status = PJ_EBUG;         //    goto on_return;         //}         pj_memcpy(path, filename->ptr, filename->slen);         path[filename->slen] = '\0';         pool = pjsua_pool_create("pool1", 1000,                 1000);         if (!pool) {             statust = PJ_ENOMEM;             goto on_return;         }         if (file_format == FMT_WAV) {             statust = pjmedia_wav_writer_port_create(pool, path,                     8000, //pjsua_var.media_cfg.clock_rate,                     1, //pjsua_var.mconf_cfg.channel_count,                     160, //pjsua_var.mconf_cfg.samples_per_frame,                     16, //pjsua_var.mconf_cfg.bits_per_sample,                     options, 0, &port);         } else {             port = NULL;             exit(PJ_ENOTSUP);         }         if (statust != PJ_SUCCESS) {             pjsua_perror("", "Unable to open file for recording", status);             goto on_return;         }         status = pjmedia_conf_create(                 pool,                 1 /*Phone*/ + 1 /*Sound device*/,                 8000 /*8kHz clock rate*/,                 1 /*Channel count*/,                 160 /*samples_per_frame*/,                 16 /*bits_per_sample*/,                 PJMEDIA_CONF_NO_DEVICE /* options */,                 &pjsua_var_mconf /* result */);         if (status != PJ_SUCCESS)             VoIP::voip_log->log_msg("[VoIP] Error on creating conference for client " + string(this->call_info.remote_info.ptr) + ": " + to_string(status), LOG_ERROR);         statust = pjmedia_conf_add_port(pjsua_var_mconf, pool, port, filename, &slot);         if (statust != PJ_SUCCESS) {             pjmedia_port_destroy(port);             goto on_return;         }         pjsua_var_recorder_0_port = port;         pjsua_var_recorder_0_slot = slot;         pjsua_var_recorder_0_pool = pool;         //if (p_id) *p_id = file_id;         //++pjsua_var.rec_cnt;         PJ_LOG(4, ("", "Recorder created, id=%d, slot=%d", file_id, slot));         pj_log_pop_indent();         status = PJ_SUCCESS; on_return:         if (pool) pj_pool_release(pool);         pj_log_pop_indent();         //exit(statust);     }     if (status != PJ_SUCCESS)         VoIP::voip_log->log_msg("[VoIP] [REC] Error recording answer" + to_string(status), LOG_ERROR);     //Connect recorder with a new virtual phone conference     pjsua_conf_port_id rec_port = pjsua_recorder_get_conf_port(this->recorder_id);     pjsua_conf_connect(this->call_info.conf_slot, rec_port);     VoIP::voip_log->log_msg("[VoIP] [REC] Created a recorder for " + string(this->call_info.remote_info.ptr), LOG_DEBUG); } ``` Thank to anyone who can help or at least understands waht i want and where i fail. Simonmicro