Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 115 additions & 2 deletions pjsip-apps/src/pjsua/pjsua_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,56 @@ static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
pjsip_endpt_cancel_timer(endpt, &cd->timer);
}

/* Rewind play file when hangup automatically,
/* Rewind play file when hangup automatically,
* since file is not looped
*/
if (app_config.auto_play_hangup)
pjsua_player_set_pos(app_config.wav_id, 0);

/* Cleanup dynamic playback if attached to this call */
if (app_config.dyn_player_active &&
app_config.dyn_player_call == call_id)
{
PJ_LOG(3, (THIS_FILE, "Call ended, stopping playback"));

/* Disconnect and destroy player if created */
if (app_config.dyn_player_id != PJSUA_INVALID_ID) {
/* Destroy player (connections to call's slot will be cleaned
* up automatically as the call is terminating) */
pjsua_player_destroy(app_config.dyn_player_id);
}

/* Reset state */
app_config.dyn_player_id = PJSUA_INVALID_ID;
app_config.dyn_player_port = PJSUA_INVALID_ID;
app_config.dyn_player_call = PJSUA_INVALID_ID;
app_config.dyn_player_active = PJ_FALSE;
app_config.dyn_play_filename[0] = '\0';
}

/* Cleanup dynamic recording if attached to this call */
if (app_config.dyn_rec_active &&
app_config.dyn_rec_call == call_id)
{
PJ_LOG(3, (THIS_FILE, "Call ended, stopping recording"));

/* Disconnect and destroy recorder if created */
if (app_config.dyn_rec_id != PJSUA_INVALID_ID) {
/* Disconnect microphone from recorder (mic is always slot 0)
* (call's slot is being torn down, so that disconnects automatically) */
pjsua_conf_disconnect(0, app_config.dyn_rec_port);

/* Destroy recorder */
pjsua_recorder_destroy(app_config.dyn_rec_id);
}

/* Reset state */
app_config.dyn_rec_id = PJSUA_INVALID_ID;
app_config.dyn_rec_port = PJSUA_INVALID_ID;
app_config.dyn_rec_call = PJSUA_INVALID_ID;
app_config.dyn_rec_active = PJ_FALSE;
app_config.dyn_rec_filename[0] = '\0';
}

PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%.*s)]",
call_id,
Expand Down Expand Up @@ -423,6 +467,40 @@ static void on_call_audio_state(pjsua_call_info *ci, unsigned mi,
pjsua_conf_connect(call_conf_slot, app_config.rec_port);
}

/* Dynamic recording - start if queued */
if (app_config.dyn_rec_active &&
app_config.dyn_rec_id == PJSUA_INVALID_ID &&
app_config.dyn_rec_filename[0] != '\0')
{
pj_status_t status;
pjsua_recorder_id rec_id;
pjsua_conf_port_id rec_port;
pj_str_t rec_file;

/* Create recorder */
rec_file = pj_str(app_config.dyn_rec_filename);
status = pjsua_recorder_create(&rec_file, 0, NULL, 0, 0, &rec_id);
if (status == PJ_SUCCESS) {
rec_port = pjsua_recorder_get_conf_port(rec_id);

/* Connect call to recorder */
pjsua_conf_connect(call_conf_slot, rec_port);

/* Connect microphone to recorder */
pjsua_conf_connect(0, rec_port);

/* Save state */
app_config.dyn_rec_id = rec_id;
app_config.dyn_rec_port = rec_port;
app_config.dyn_rec_call = ci->id;

PJ_LOG(3, (THIS_FILE, "Dynamic recording auto-started"));
} else {
PJ_LOG(2, (THIS_FILE, "Failed to auto-start recording: %d", status));
app_config.dyn_rec_active = PJ_FALSE;
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When auto-start of recording fails and dyn_rec_active is set to FALSE (line 500), the dyn_rec_filename is not cleared. This creates an inconsistent state. The filename should be cleared along with the active flag to maintain consistency.

Suggested change
app_config.dyn_rec_active = PJ_FALSE;
app_config.dyn_rec_active = PJ_FALSE;
app_config.dyn_rec_filename[0] = '\0';

Copilot uses AI. Check for mistakes.
}
Comment on lines 470 to 502
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code for creating and connecting the recorder is duplicated from start_recording in pjsua_app_cli.c (lines 1608-1634). Consider extracting this logic into a shared helper function to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
}

/* Record audio into AVI, if desired */
if (app_config.avi_auto_rec && app_config.avi_rec_audio &&
app_config.avi_aud_slot != PJSUA_INVALID_ID)
Expand All @@ -431,13 +509,48 @@ static void on_call_audio_state(pjsua_call_info *ci, unsigned mi,
}

/* Stream a file, if desired */
if ((app_config.auto_play || app_config.auto_play_hangup) &&
if ((app_config.auto_play || app_config.auto_play_hangup) &&
app_config.wav_port != PJSUA_INVALID_ID)
{
pjsua_conf_connect(app_config.wav_port, call_conf_slot);
connect_sound = PJ_FALSE;
}

/* Dynamic playback - start if queued */
if (app_config.dyn_player_active &&
app_config.dyn_player_id == PJSUA_INVALID_ID &&
app_config.dyn_play_filename[0] != '\0')
{
pj_status_t status;
pjsua_player_id player_id;
pjsua_conf_port_id player_port;
pj_str_t play_file;

/* Create player */
play_file = pj_str(app_config.dyn_play_filename);
status = pjsua_player_create(&play_file, 0, &player_id);
if (status == PJ_SUCCESS) {
player_port = pjsua_player_get_conf_port(player_id);

/* Connect player to call */
pjsua_conf_connect(player_port, call_conf_slot);

/* Disconnect microphone (like auto-play) */
pjsua_conf_disconnect(0, call_conf_slot);

/* Save state */
app_config.dyn_player_id = player_id;
app_config.dyn_player_port = player_port;
app_config.dyn_player_call = ci->id;

PJ_LOG(3, (THIS_FILE, "Dynamic playback auto-started"));
connect_sound = PJ_FALSE;
} else {
PJ_LOG(2, (THIS_FILE, "Failed to auto-start playback: %d", status));
app_config.dyn_player_active = PJ_FALSE;
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When auto-start of playback fails and dyn_player_active is set to FALSE (line 550), the dyn_play_filename is not cleared. This creates an inconsistent state similar to the recording case. The filename should be cleared along with the active flag to maintain consistency.

Suggested change
app_config.dyn_player_active = PJ_FALSE;
app_config.dyn_player_active = PJ_FALSE;
app_config.dyn_play_filename[0] = '\0';

Copilot uses AI. Check for mistakes.
}
Comment on lines 520 to 553
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code for creating and connecting the player is duplicated from start_playback in pjsua_app_cli.c (lines 1446-1468). Consider extracting this logic into a shared helper function to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pjsua_app.c (callback context) already has call_conf_slot and ci->id available from callback, has no intermediate error checking for conf_connect
pjsua_app_cli.c (CLI command) must find and validate call/media slots itself, error handling with cleanup at each step, returns status to caller
A shared helper would require passing many parameters (call_id, call_conf_slot, filename, etc.), handling different error processing.
Don't you think that it will add more complexity?

}

/* Stream AVI, if desired */
if (app_config.avi_auto_play &&
app_config.avi_def_idx != PJSUA_INVALID_ID &&
Expand Down
Loading
Loading