Thanks,
I updated to Asterisk 22.0 to verify that the version is not the issue, but the problem still persists. I have collected debug information [1] and the logs appear normal. I am attaching the logs and the ARI application [2] that reproduces the issue."
Would you like me to help review any other text?
[1]
Asterisk-22-0-no-audio.txt (89.5 KB)
[2] Python code
def on_message(ws, message):
try:
logging.info("\n--- New on_message execution ---")
event = json.loads(message)
logging.info(f"Received event: {event}")
if event['type'] == 'StasisStart':
channel = event['channel']
channel_id = channel['id']
channel_name = channel['name']
# Ensure no redundant processing of channels
if channel_id in processed_channels:
logging.info(f"Ignoring already processed or snoop channel: {channel_id}")
logging.info("--- Exit on_message: Ignoring channel ---\n")
return
processed_channels.add(channel_id)
logging.info(f"Processing new channel: {channel_id}")
# Answer the call
answer_call(channel_id)
# Start external media and add to processed channels
external_channel_id = start_external_media()
if not external_channel_id:
logging.error("Failed to start external media.")
logging.info("--- Exit on_message: Failed external media ---\n")
return
processed_channels.add(external_channel_id)
# Create snoop channel and add to processed channels
snoop_channel_id = create_snoop_channel(channel_id)
if not snoop_channel_id:
logging.error("Failed to create snoop channel.")
logging.info("--- Exit on_message: Failed snoop channel ---\n")
return
processed_channels.add(snoop_channel_id)
# Create and populate bridge
bridge_id = create_bridge()
if bridge_id:
logging.info(f"Bridge {bridge_id} created. Adding channels...")
# Add original channel to the bridge
# add_channel_to_bridge(bridge_id, channel_id)
# Add snoop channel to the bridge
add_channel_to_bridge(bridge_id, snoop_channel_id)
# Add external media channel to the bridge
add_channel_to_bridge(bridge_id, external_channel_id)
logging.info(f"Channels added to bridge {bridge_id}.")
else:
logging.error("Failed to create bridge.")
logging.info("--- Exit on_message: Failed to create bridge ---\n")
logging.info("--- End of on_message execution ---\n")
except Exception as e:
logging.error(f"Error in on_message: {e}")
logging.info("--- Exit on_message: Exception occurred ---\n")
def start_external_media():
"""Start an external media channel."""
url = f"{ARI_URL}/ari/channels/externalMedia"
data = {
"app": APP_NAME,
"external_host": f"{MEDIA_HOST}:{MEDIA_PORT}",
"format": "ulaw" # Verify this matches PCMU (G.711) as per SIP SDP
}
logging.info(f"Opening external media with data: {data}")
response = requests.post(url, auth=(USERNAME, PASSWORD), json=data)
if response.status_code == 200:
external_channel = response.json()
logging.info(f"Started external media: {external_channel}")
return external_channel['id']
else:
logging.error(f"Failed to start external media: {response.status_code} - {response.text}")
return None
def create_snoop_channel(original_channel_id):
url = f"{ARI_URL}/ari/channels/{original_channel_id}/snoop"
data = {"app": APP_NAME, "spy": "in", "format": "ulaw"}
response = requests.post(url, auth=(USERNAME, PASSWORD), json=data)
if response.status_code == 200:
snoop_channel = response.json()
logging.info(f"Snoop channel created: {snoop_channel}")
# Log to confirm stream
logging.info(f"Snoop channel is set to spy on 'in' direction of channel: {original_channel_id}")
return snoop_channel['id']
else:
logging.error(f"Failed to create snoop channel: {response.status_code} - {response.text}")
return None
def answer_call(channel_id):
"""Answer the incoming call."""
url = f"{ARI_URL}/ari/channels/{channel_id}/answer"
response = requests.post(url, auth=(USERNAME, PASSWORD))
if response.status_code == 204:
logging.info(f"Answered call on channel: {channel_id}")
else:
logging.error(f"Failed to answer call: {response.status_code} - {response.text}")
def create_bridge():
"""Create a mixing bridge."""
url = f"{ARI_URL}/ari/bridges"
data = {"type": "mixing"}
response = requests.post(url, auth=(USERNAME, PASSWORD), json=data)
if response.status_code == 200:
bridge = response.json()
logging.info(f"Created bridge: {bridge}")
return bridge['id']
else:
logging.error(f"Failed to create bridge: {response.status_code} - {response.text}")
return None
def add_channel_to_bridge(bridge_id, channel_id):
"""Add a channel to the bridge."""
url = f"{ARI_URL}/ari/bridges/{bridge_id}/addChannel"
data = {"channel": channel_id}
response = requests.post(url, auth=(USERNAME, PASSWORD), json=data)
if response.status_code == 204:
logging.info(f"Added channel {channel_id} to bridge {bridge_id}")
else:
logging.error(f"Failed to add channel to bridge: {response.status_code} - {response.text}")
def clean_all_channels_and_bridges():
"""Retrieve and clean up all open channels and bridges."""
# Clean up channels
channels_url = f"{ARI_URL}/ari/channels"
channels_response = requests.get(channels_url, auth=(USERNAME, PASSWORD))
if channels_response.status_code == 200:
channels = channels_response.json()
if not channels:
logging.info("No open channels to clean.")
else:
for channel in channels:
channel_id = channel['id']
hangup_url = f"{channels_url}/{channel_id}"
hangup_response = requests.delete(hangup_url, auth=(USERNAME, PASSWORD))
if hangup_response.status_code == 204:
logging.info(f"Successfully hung up channel {channel_id}.")
else:
logging.error(f"Failed to hang up channel {channel_id}: {hangup_response.status_code} - {hangup_response.text}")
else:
logging.error(f"Failed to retrieve channels: {channels_response.status_code} - {channels_response.text}")
# Clean up bridges
bridges_url = f"{ARI_URL}/ari/bridges"
bridges_response = requests.get(bridges_url, auth=(USERNAME, PASSWORD))
if bridges_response.status_code == 200:
bridges = bridges_response.json()
if not bridges:
logging.info("No open bridges to clean.")
else:
for bridge in bridges:
bridge_id = bridge['id']
delete_bridge_url = f"{bridges_url}/{bridge_id}"
delete_bridge_response = requests.delete(delete_bridge_url, auth=(USERNAME, PASSWORD))
if delete_bridge_response.status_code == 204:
logging.info(f"Successfully deleted bridge {bridge_id}.")
else:
logging.error(f"Failed to delete bridge {bridge_id}: {delete_bridge_response.status_code} - {delete_bridge_response.text}")
else:
logging.error(f"Failed to retrieve bridges: {bridges_response.status_code} - {bridges_response.text}")
def start_ari_ws():
"""Start the ARI WebSocket connection."""
credentials = f"{USERNAME}:{PASSWORD}".encode('utf-8')
auth_header = base64.b64encode(credentials).decode('utf-8')
ws_url = f"ws://127.0.0.1:8088/ari/events?app={APP_NAME}&subscribeAll=true"
ws = WebSocketApp(
ws_url,
on_message=on_message,
on_error=lambda ws, err: logging.error(f"WebSocket error: {err}"),
on_close=lambda ws, code, msg: logging.info("WebSocket connection closed"),
header=[f"Authorization: Basic {auth_header}"]
)
ws.on_open = lambda ws: logging.info(f"WebSocket connected to ARI app '{APP_NAME}'")
ws.run_forever()
if __name__ == "__main__":
logging.info(f"Starting ARI client for app '{APP_NAME}'...")
clean_all_channels_and_bridges()
start_ari_ws()```