Three and a half years ago (which really sounds like a lot of time!) I was working with a VoIP infrastructure using SILK. As it often happens to server-side developers/integrators, you have to prove whether the audio provided by a client or to a client is correctly encoded :-)
Wireshark is able to decode, and play, G.711 streams, but not SILK (or Opus - more on this later). So I thought of having my own tool handy, to generate a WAV file for a PCAP with RTP carrying SILK frames.
The first part requires extracting the SILK payload and writing it down into a bistream file. Then you have to decode the audio using the SILK SDK decoder, to get a raw audio file. From there to a WAV file it is very easy.
As I tried to describe in this previous post, I had to reverse engineer the test files contained in the SDK, to see what a SILK file looked like.
Since the SILK payload is not constant, all that was needed was to insert 2 Bytes with the length of the following SILK frame. At the beginning of the file you have to add a header containing "#!SILK_V3", and voilà.
This is accomplished by silk_rtp_to_bistream.c (from https://github.com/giavac/silk_rtp_to_bitstream), a small program based on libpcap that extracts the SILK payload from a PCAP and writes it properly into a bistream file.
Build the binary with:
gcc silk_rtp_to_bitstream.c -lpcap -o silk_rtp_to_bitstream
(you'll need libpcap-dev installed)
Create the bistream with:
./silk_rtp_to_bitstream input.pcap silk.bit
Now you can decode, using the SILK SDK, from bitstream into raw audio with:
$SILK_SDK/decoder silk.bit silk.raw
Raw audio to WAV can be done with sox:
sox -V -t raw -b 16 -e signed-integer -r 24000 silk.raw silk.wav
This works fine with single channel SILK at 8000 Hz.
More to come: an update on how to accomplish the same but for Opus.
Subscribe to:
Post Comments (Atom)
Decrypt SDES SRTP from pcap
If you have a pcap file with encrypted RTP (SDES SRTP) and have access to the SIP signalling to see the keys, these instructions will help y...
-
Before I forget again, a Wireshark setting that can help saving time by trying to interpret any UDP as RTP, if possible: Analyze --> Ena...
-
I needed an efficient way to programmatically extract RTP streams from a network capture. In addition I wanted to: save each stream into a s...
-
WebRTC applications use the ICE negotiation to discovery the best way to communicate with a remote party. I t dynamically finds a pair of...
Thanks. This looks pretty good to decode SILK codec and convert to wav file.
ReplyDeleteDo you have any steps to extract bitstream of RTAudio codec of Microsoft, convert to raw to wav file ?
Since the SILK payload is not constant, all that was needed was to insert 2 bytes with the length of the next SILK frame. At the beginning of the file you have to add a header containing "#!SILK_V3", and that's it.
ReplyDeleteI could ask you more about how you did this process. I tried a python script but it didn't work when I applied the silkv3 to wav converter.
import struct
def silk_v1_to_v3(input_file, output_file):
"""
Convierte un archivo SILK_V1 a SILK_V3 agregando el encabezado correcto
y la longitud de los frames.
Args:
input_file (str): Ruta al archivo SILK_V1 de entrada.
output_file (str): Ruta donde se guardará el archivo SILK_V3 convertido.
"""
silk_v3_header = b"#!SILK_V3\n" # Encabezado esperado para SILK_V3
with open(input_file, "rb") as infile, open(output_file, "wb") as outfile:
# Escribe el encabezado SILK_V3
outfile.write(silk_v3_header)
# Lee el contenido del archivo SILK_V1 (omitiendo el encabezado inicial #!SILK)
infile.seek(len(b"#!SILK\n")) # Salta el encabezado SILK_V1
silk_data = infile.read()
# Procesa cada frame de audio
frame_size = 320 # Suponiendo un tamaño fijo de frames; ajustar si necesario
index = 0
while index < len(silk_data):
# Lee un frame completo
frame = silk_data[index:index + frame_size]
# Calcula y escribe la longitud del frame (2 bytes)
frame_length = len(frame)
outfile.write(struct.pack(">H", frame_length)) # Escribe en formato big-endian
# Escribe el frame en el archivo de salida
outfile.write(frame)
# Avanza al siguiente frame
index += frame_size
print(f"Conversión completada. Archivo guardado en {output_file}")
# Uso del script
input_file = "6792daa02abae.slk" # Reemplazar con la ruta del archivo SILK_V1
output_file = "output.silk" # Ruta donde guardar el archivo SILK_V3
silk_v1_to_v3(input_file, output_file)
Hi Denis, take a look at https://github.com/giavac/silk_rtp_to_bitstream/blob/master/silk_rtp_to_bitstream.c
DeleteYou can see that it first adds the magic number at the beginning of the file:
https://github.com/giavac/silk_rtp_to_bitstream/blob/master/silk_rtp_to_bitstream.c#L36
then loops through all the frames in the pcap file and for each it reads the frame length and writes it before copying the payload:
https://github.com/giavac/silk_rtp_to_bitstream/blob/master/silk_rtp_to_bitstream.c#L61
What was the error you got from the converter? Did you check the hexdump of the input file to ensure it's consistent with the expectations?
Maybe you can share the pcap and the .slk files, if they don't contain sensitive information?
Cheers,
Giacomo
kind regards
ReplyDeletekind regards
ReplyDelete