Friday, 13 January 2017

VoIP calls encoded with SILK: from RTP to WAV, update

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.


5 comments:

  1. Thanks. This looks pretty good to decode SILK codec and convert to wav file.

    Do you have any steps to extract bitstream of RTAudio codec of Microsoft, convert to raw to wav file ?

    ReplyDelete
  2. 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.

    I 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)

    ReplyDelete
    Replies
    1. Hi Denis, take a look at https://github.com/giavac/silk_rtp_to_bitstream/blob/master/silk_rtp_to_bitstream.c

      You 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

      Delete

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...