Send audio with RTP to channel

I’m using external media and trying to send back the audio with ffmpeg, but I don’t hear anything.
Everything is running on localhost (ARI application, ffmpeg).
I hear the audio coming from asterisk, but when I try to send the audio back to the RTP port of asterisk, I don’t get a sound. I get the port from the UNICASTRTP_LOCAL_PORT and UNICASTRTP_LOCAL_ADDRESS’: '127.0.0.1
Using this ffmpeg command:
ffmpeg -re -i gtts_output.wav -acodec pcm_alaw -ac 1 -ar 8000 -f rtp rtp://127.0.0.1:14836 ← depending on the unicast rtp port

Output from ffmpeg:

  libavutil      57. 28.100 / 57. 28.100
  libavcodec     59. 37.100 / 59. 37.100
  libavformat    59. 27.100 / 59. 27.100
  libavdevice    59.  7.100 / 59.  7.100
  libavfilter     8. 44.100 /  8. 44.100
  libswscale      6.  7.100 /  6.  7.100
  libswresample   4.  7.100 /  4.  7.100
  libpostproc    56.  6.100 / 56.  6.100
Guessed Channel Layout for Input Stream #0.0 : mono
Input #0, wav, from 'gtts_output.wav':
  Duration: 00:00:10.08, bitrate: 352 kb/s
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 22050 Hz, mono, s16, 352 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (pcm_s16le (native) -> pcm_alaw (native))
Press [q] to stop, [?] for help
Output #0, rtp, to 'rtp://127.0.0.1:14478':
  Metadata:
    encoder         : Lavf59.27.100
  Stream #0:0: Audio: pcm_alaw, 8000 Hz, mono, s16, 64 kb/s
    Metadata:
      encoder         : Lavc59.37.100 pcm_alaw
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat LIBAVFORMAT_VERSION
m=audio 14478 RTP/AVP 8
b=AS:64

size=      80kB time=00:00:10.07 bitrate=  65.1kbits/s speed=   1x    
video:0kB audio:79kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.741763%

The external media channel was also created with the alaw format.

Enabled rtp debug on the asterisk cli.
The packets are coming:

Sent RTP packet to      127.0.0.1:45000 (type 08, seq 014597, ts 3932647640, len 000160) -> ARI application
Got  RTP packet from    127.0.0.1:38566 (type 08, seq 002822, ts 1049953894, len 000743) -> ffmpeg back to asterisk

What is wrong here?

The length looks rather large… I’d expect it to be 160 which would be 20ms of audio.

I am trying to send audio through RTP too. I can hear my voice but cannot play a WAV file by splitting it into several chunks. Interestingly, I can echo my voice and hear that clearly. I use the header of the received chunk for the output chunk. thank you for your help.


the below code is echoing and works,

packet_info = self.packet_queue.get(timeout=0.1)
                    
# Extract packet info
addr = packet_info['addr']
payload = packet_info['payload']
payload_type = packet_info['payload_type']
timestamp = packet_info['timestamp']
is_silent = packet_info['is_silent']
counter = packet_info['counter']

# Create RTP header with carefully managed timestamp

echo_rtp_header = struct.pack(
 "!BBHII",
 (self.RTP_VERSION << 6) | 0,
payload_type,
self.seq_num,
timestamp,  # Using our managed timestamp
self.RTP_SSRC
)
                    
# Send appropriate packet based on silence state (not just current packet)
if not silence_state:
    echo_packet = echo_rtp_header + payload
    self.sock.sendto(echo_packet, addr)

but when playing wav file like below (of course this not the whole code) i hear noise, also attached wireshark screenshot:

 
    def load_wav_file(self):
        """Load and preprocess WAV file"""
        try:
            # Path to the WAV file
            audio_dir = os.path.join(settings.MEDIA_ROOT, 'uploads/reserved_audio')
            os.makedirs(audio_dir, exist_ok=True)
            wav_path = os.path.join(audio_dir, 'welcome_voice.wav')
            
            # Check if file exists, if not, return silence
            if not os.path.exists(wav_path):
                return self.silence_payload_ulaw * 50  # 1 second of silence
                
            with wave.open(wav_path, 'rb') as wav_file:
                # Read the entire file
                pcm_data = wav_file.readframes(wav_file.getnframes())
                
                # Convert to mono and 8kHz if needed
                if wav_file.getnchannels() == 2:
                    pcm_data = audioop.tomono(pcm_data, wav_file.getsampwidth(), 0.5, 0.5)
                
                if wav_file.getsampwidth() != 2:
                    pcm_data = audioop.lin2lin(pcm_data, wav_file.getsampwidth(), 2)
                
                if wav_file.getframerate() != 8000:
                    pcm_data, _ = audioop.ratecv(pcm_data, 2, 1, wav_file.getframerate(), 8000, None)
                
    
                alaw_data = audioop.lin2alaw(pcm_data, 2)
                return alaw_data
##------------------- new method for generating and sending rtp packets**
           last_timestamp = 0
            timestamp_increment = 160  # 20ms at 8kHz
            
            # WAV playback position
            audio_position = 0
            max_position = max(1, len(self.wav_data) // self.packet_size)
            
            while self.running:
                try:
                    # Get packet from queue with timeout
                    packet_info = self.packet_queue.get(timeout=0.1)
     
                    addr = packet_info['addr']
                    timestamp = packet_info['timestamp']
                    
                    payload_type = 0
                    
                    # Ensure smooth timestamp progression
                    if timestamp > last_timestamp:
                        last_timestamp = timestamp
                    else:
                        last_timestamp += timestamp_increment
                    timestamp = last_timestamp
                    
                    # Create RTP header
                    rtp_header = struct.pack(
                        "!BBHII",
                        (self.RTP_VERSION << 6),  # Version 2, no padding, no extension, no CSRC
                        payload_type,             # μ-law (8)
                        self.seq_num,             # Sequence number
                        timestamp,                # Timestamp
                        self.RTP_SSRC             # SSRC identifier
                    )
                    
                    # Get the next chunk of audio data
                    start_pos = (audio_position % max_position) * self.packet_size
                    
                    # Extract the chunk
                    if start_pos + self.packet_size <= len(self.wav_data):
                        audio_chunk = self.wav_data[start_pos:start_pos + self.packet_size]
                    else:
                        # Handle end of file wrap-around
                        audio_chunk = self.wav_data[start_pos:]
                        # Pad with silence if needed
                        if len(audio_chunk) < self.packet_size:
                            padding = self.silence_payload_ulaw[:self.packet_size - len(audio_chunk)]
                            audio_chunk += padding
                    
                    # Increment position
                    audio_position = (audio_position + 1) % max_position
                    
                    # Create packet and send it
                    packet = rtp_header + audio_chunk
                    self.sock.sendto(packet, addr)
                    
                    # Increment sequence number
                    self.seq_num = (self.seq_num + 1) % 65536

Asterisk CLI:

-- Called 0.0.0.0:6000/c(alaw)
   -- UnicastRTP/0.0.0.0:6000-0x5ec7053ed440 answered
      > Launching Stasis(python_app) on UnicastRTP/0.0.0.0:6000-0x5ec7053ed440
   -- Channel PJSIP/myvoip_endpoint-0000001a joined 'simple_bridge' stasis-bridge <e9904607-d530-49f3-96f9-3ae192d8da3c>
   -- Channel UnicastRTP/0.0.0.0:6000-0x5ec7053ed440 joined 'simple_bridge' stasis-bridge <e9904607-d530-49f3-96f9-3ae192d8da3c>
   -- Channel Snoop/1745932639.148-00000018 joined 'simple_bridge' stasis-bridge <e9904607-d530-49f3-96f9-3ae192d8da3c>
      > Bridge e9904607-d530-49f3-96f9-3ae192d8da3c: switching from simple_bridge technology to softmix
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007373, ts 000160, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029469, ts 000160, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029470, ts 000320, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007374, ts 000320, len 000160)
      > 0x5ec7053e8a50 -- Strict RTP qualifying stream type: <unknown>
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029471, ts 000480, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007375, ts 000480, len 000160)
      > 0x5ec7053e8a50 -- Strict RTP qualifying stream type: <unknown>
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029472, ts 000640, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007376, ts 000640, len 000160)
      > 0x5ec7053e8a50 -- Strict RTP qualifying stream type: <unknown>
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007377, ts 000800, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029473, ts 000800, len 000160)
      > 0x5ec7053e8a50 -- Strict RTP qualifying stream type: <unknown>
      > 0x5ec7053e8a50 -- Strict RTP switching source address to 127.0.0.1:6000
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000004, ts 000800, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007378, ts 000960, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029474, ts 000960, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000005, ts 000960, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007379, ts 001120, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029475, ts 001120, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000006, ts 001120, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007380, ts 001280, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029476, ts 001280, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000007, ts 001280, len 000160)
      > 0x7477c404ba60 -- Strict RTP switching to RTP target address 148.122.250.9:11092 as source
Got  RTP packet from    148.122.250.9:11092 (type 08, seq 000001, ts 000480, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029477, ts 001440, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007381, ts 001440, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000008, ts 001440, len 000160)
Got  RTP packet from    148.122.250.9:11092 (type 08, seq 000002, ts 000640, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029478, ts 001600, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007382, ts 001600, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000009, ts 001600, len 000160)
Got  RTP packet from    148.122.250.9:11092 (type 08, seq 000003, ts 000800, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007383, ts 001760, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029479, ts 001760, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000010, ts 001760, len 000160)
Got  RTP packet from    148.122.250.9:11092 (type 08, seq 000004, ts 000960, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029480, ts 001920, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007384, ts 001920, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000011, ts 001920, len 000160)
Got  RTP packet from    148.122.250.9:11092 (type 08, seq 000005, ts 001120, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007385, ts 002080, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029481, ts 002080, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000012, ts 002080, len 000160)
Got  RTP packet from    148.122.250.9:11092 (type 08, seq 000006, ts 001280, len 000160)
Sent RTP packet to      148.122.250.9:11092 (type 08, seq 007386, ts 002240, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029482, ts 002240, len 000160)
Got  RTP packet from    127.0.0.1:6000 (type 08, seq 000013, ts 002240, len 000160)
Got  RTP packet from    148.122.250.9:11092 (type 08, seq 000007, ts 001440, len 000160)
Sent RTP packet to      0.0.0.0:6000 (type 08, seq 029483, ts 002400, len 000160) ```

Please tell wireshark that that stream is RTP, so that it can analyze it fully.

thank you @david551 for replying. I attached, everything is correct to me!

Wire shark can analyze the RTP for timing problems, and can playback the audio it contains.

The sample rate towards 10.0.0.4 seems to be too high.

Thank you @david551. Yes, using WireShark, I playedback audio from (source) 10.0.0.4 which is buzz, but the audio from 148.122.250.0 is fine.
could you please let me know how you found out the sample rate is too high?

thank you @rizwan1 , please see the below picture. I hear buzz here. and the sample rate for both sources is 8000.

any idea about this part of the code for converting wav file to alaw?

            self.packet_size = 160
            file_path = 'welcome_voice.wav'
       
            with wave.open(file_path, 'rb') as wav_file:
                # Get audio parameters
                channels = wav_file.getnchannels()
                sample_width = wav_file.getsampwidth()
                framerate = wav_file.getframerate()
                
                # Read all frames from the WAV file
                pcm_data = wav_file.readframes(wav_file.getnframes())
            
            # Convert stereo to mono if needed
            if channels == 2:
                pcm_data = audioop.tomono(pcm_data, sample_width, 0.5, 0.5)
            
            # Ensure we're working with 8kHz audio
            target_rate = 8000
            if framerate != target_rate:
                print(f"Resampling audio from {framerate}Hz to {target_rate}Hz")
                pcm_data = audioop.ratecv(pcm_data, sample_width, 1, 
                                         framerate, target_rate, None)[0]
            
            # Ensure we're using 16-bit PCM (required for alaw conversion)
            if sample_width != 2:
                pcm_data = audioop.lin2lin(pcm_data, sample_width, 2)
            
            # Convert PCM to A-law
            alaw_data = audioop.lin2alaw(pcm_data, 2)
            
            # Split A-law data into chunks of packet_size bytes (usually 160 bytes)
            chunks = [alaw_data[i:i+self.packet_size] for i in range(0, len(alaw_data), self.packet_size)]
            
            # Pad the last chunk if necessary
            if chunks and len(chunks[-1]) < self.packet_size:
                chunks[-1] = chunks[-1] + bytes([0xD5] * (self.packet_size - len(chunks[-1])))
            

Even converting the wav file to alaw using below command and then sending it chunk by chunk (each 160 bytes) was not successful (hear buzz), but I received the input audio clearly.
ffmpeg -i input.wav -af "highpass=f=300, lowpass=f=3400" -ar 8000 -ac 1 -ab 64k -f alaw output.alaw

Your timestamps are going up in steps of 160, but looking over several frames, the interval between them is less than 20ms, and it seemed to me to be a systematic discrepancy, rather than just a temporary one, due to jitter.

Looks like you might be alternating between two different values between samples. My guess is that your actual data has been converted to .sln format (16 bit signed linear, and you are alternating high and low bytes.

Referecing the first post, thank you for the tip, with alaw it is working now. Unfortunately I need to set slin16 in the external media channel. Therefore I can’t use alaw in ffmpeg to send back the audio to asterisk.
I’m trying to use this ffmpeg parameters to send back the audio, but I don’t hear any audio:

acodec='pcm_s16le', ac=1, ar=16000, format='rtp', payload_type=118 packet_size=172

Output of ffmpeg

utput #0, rtp, to 'rtp://127.0.0.1:14964?pkt_size=172':
  Metadata:
    encoder         : Lavf59.27.100
  Stream #0:0: Audio: pcm_s16le, 16000 Hz, mono, s16, 256 kb/s
    Metadata:
      encoder         : Lavc59.37.100 pcm_s16le
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat LIBAVFORMAT_VERSION
m=audio 14964 RTP/AVP 118
b=AS:256

Any tip for me, how I have to configure the correct ffmpeg parameters to get an audio in asterisk?