-
Notifications
You must be signed in to change notification settings - Fork 938
pjsua: Add audio play/rec commands to CLI framework #4723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ba603d5
65a77f4
d339ebf
a145ec7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -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, | ||||||||
|
|
@@ -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; | ||||||||
| } | ||||||||
|
Comment on lines
470
to
502
|
||||||||
| } | ||||||||
|
|
||||||||
| /* 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) | ||||||||
|
|
@@ -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; | ||||||||
|
||||||||
| app_config.dyn_player_active = PJ_FALSE; | |
| app_config.dyn_player_active = PJ_FALSE; | |
| app_config.dyn_play_filename[0] = '\0'; |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.