go - How to extract Parrot's Anafi AI drone protobuf metadata from RTP packet, using Golang? - Stack Overflow

I really struggle extracting protobuf payload (Parrot TimedMetadata) from a recording of RTP packets.I

I really struggle extracting protobuf payload (Parrot TimedMetadata) from a recording of RTP packets. I'm using golang. I followed the parrot's dev guide without success.

This is my main.go:

package main

import (
    "encoding/binary"
    "fmt"
    "log"
    "os"

    "github/gopacket/gopacket"
    "github/gopacket/gopacket/pcap"
    "github/pion/rtp"
    "<my_module>/vmeta"
    "google.golang/protobuf/proto"
)

func main() {
    h, err := pcap.OpenOffline("data.pcapng")
    if err != nil {
        log.Fatalf("failed to open file: %v", err)
    }
    outputFile, err := os.Create("out.txt")
    if err != nil {
        log.Fatalf("failed to create save file: %v", err)
    }
    defer outputFile.Close()
    ps := gopacket.NewPacketSource(h, h.LinkType())
    i := 0

    for packet := range ps.Packets() {
        layer := packet.TransportLayer()
        if layer == nil {
            continue
        }
        payload := packet.TransportLayer().LayerPayload()
        rtpPacket := rtp.Packet{}
        err = rtpPacket.Unmarshal(payload)
        if err != nil {
            continue
        }
        i++
        if !rtpPacket.Extension {
            continue // no extension header
        }
        if err := parse(payload); err == nil {
            log.Println("Success !")
            os.Exit(0)
        }
    }
}

func parse(data []byte) error {
    n := 12 // skip fixed size header
    log.Printf("Initial buffer: %x", data)
    version := data[n : n+2]
    n += 2
    log.Printf("Defined by profile: 0x%x\n", version) // 0x5062 here, so no pb
    length := binary.BigEndian.Uint16(data[n : n+2])
    n += 2
    log.Printf("Length: %d\n", length)
    packIndicator := binary.BigEndian.Uint16(data[n : n+2])
    lastPack := (packIndicator >> 9) & 0x7f
    curPack := (packIndicator >> 2) & 0x7f
    padding := (packIndicator >> 0) & 0x03
    n += 2
    log.Printf("Packet: %d/%d\n", curPack, lastPack)
    log.Printf("Padding: %d\n", padding)
    offset := binary.BigEndian.Uint16(data[n : n+2])
    n += 2
    log.Printf("Offset: %d\n", offset)
    rawData := data[n : uint16(n)+(4*length)]
    log.Printf("RawData: %x", rawData)

    model := vmeta.TimedMetadata{}
    if err := proto.Unmarshal(rawData, &model); err != nil {
        return fmt.Errorf("failed to deserialize: %v", err)
    }

    return nil
}

The problem is that it never succeed... We are expected to work with RTP Extensions Header but if I extract it using Wireshark and decode it, it fails. Even if I follow the source code of Parrot-Developers/libvideo-streaming (.h#L140) and skip the first two bytes of Extension Header it does not work. Does anyone know how the 'padding' field should be used ? It's not mentionned in the doc (.html#id4)

Has anyone ever worked on such a thing ?

I really struggle extracting protobuf payload (Parrot TimedMetadata) from a recording of RTP packets. I'm using golang. I followed the parrot's dev guide without success.

This is my main.go:

package main

import (
    "encoding/binary"
    "fmt"
    "log"
    "os"

    "github/gopacket/gopacket"
    "github/gopacket/gopacket/pcap"
    "github/pion/rtp"
    "<my_module>/vmeta"
    "google.golang./protobuf/proto"
)

func main() {
    h, err := pcap.OpenOffline("data.pcapng")
    if err != nil {
        log.Fatalf("failed to open file: %v", err)
    }
    outputFile, err := os.Create("out.txt")
    if err != nil {
        log.Fatalf("failed to create save file: %v", err)
    }
    defer outputFile.Close()
    ps := gopacket.NewPacketSource(h, h.LinkType())
    i := 0

    for packet := range ps.Packets() {
        layer := packet.TransportLayer()
        if layer == nil {
            continue
        }
        payload := packet.TransportLayer().LayerPayload()
        rtpPacket := rtp.Packet{}
        err = rtpPacket.Unmarshal(payload)
        if err != nil {
            continue
        }
        i++
        if !rtpPacket.Extension {
            continue // no extension header
        }
        if err := parse(payload); err == nil {
            log.Println("Success !")
            os.Exit(0)
        }
    }
}

func parse(data []byte) error {
    n := 12 // skip fixed size header
    log.Printf("Initial buffer: %x", data)
    version := data[n : n+2]
    n += 2
    log.Printf("Defined by profile: 0x%x\n", version) // 0x5062 here, so no pb
    length := binary.BigEndian.Uint16(data[n : n+2])
    n += 2
    log.Printf("Length: %d\n", length)
    packIndicator := binary.BigEndian.Uint16(data[n : n+2])
    lastPack := (packIndicator >> 9) & 0x7f
    curPack := (packIndicator >> 2) & 0x7f
    padding := (packIndicator >> 0) & 0x03
    n += 2
    log.Printf("Packet: %d/%d\n", curPack, lastPack)
    log.Printf("Padding: %d\n", padding)
    offset := binary.BigEndian.Uint16(data[n : n+2])
    n += 2
    log.Printf("Offset: %d\n", offset)
    rawData := data[n : uint16(n)+(4*length)]
    log.Printf("RawData: %x", rawData)

    model := vmeta.TimedMetadata{}
    if err := proto.Unmarshal(rawData, &model); err != nil {
        return fmt.Errorf("failed to deserialize: %v", err)
    }

    return nil
}

The problem is that it never succeed... We are expected to work with RTP Extensions Header but if I extract it using Wireshark and decode it, it fails. Even if I follow the source code of Parrot-Developers/libvideo-streaming (https://github/Parrot-Developers/libvideo-streaming/blob/master/src/vstrm_rtp_h264.h#L140) and skip the first two bytes of Extension Header it does not work. Does anyone know how the 'padding' field should be used ? It's not mentionned in the doc (https://developer.parrot/docs/groundsdk-tools/video-metadata.html#id4)

Has anyone ever worked on such a thing ?

Share Improve this question edited Mar 12 at 13:54 blackgreen 45.2k28 gold badges161 silver badges156 bronze badges asked Mar 11 at 14:41 user29960667user29960667 311 silver badge3 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 1

I finally managed to find out a solution.

My issue came from a lack of documentation from Parrot. If we take a look at the C library they provide (https://github/Parrot-Developers/libvideo-streaming/blob/bfd02a6c385dcd4645dfe8a7973f2e426e75199a/src/vstrm_rtp_h264.h#L144) we can see a presence of an extension header's header (yep, that is a little bit intricate).

To wrap things up, you need to skip the 12 bytes RTP header, check if the following two bytes are equal to 0x5062 (protobuf format), get the length of the header extension with the following 2 bytes (multiple of 4 bytes), get information of packet splitting using the following code :

packIndicator := binary.BigEndian.Uint16(data[16 : 18])
lastPack := (packIndicator >> 9) & 0x7f
curPack := (packIndicator >> 2) & 0x7f
padding := (packIndicator >> 0) & 0x03

If curPack == lastPack we have a whole proto message. Otherwise append an intermediate buffer.

We can then skip the 2 following bytes (represent an offset for intermediate buffer, we shouldn't need this) and finally we arrive at our protobuf raw data.

The bounds for the protobuf message is the following:

data[20 : 20+(4*(length - 1)) - padding]

So really the protobuf section of the extension header can be found using the following parse function:

func parse(data []byte) error {
    n := 12 // skip fixed size header
    log.Printf("Initial buffer: %x", data)
    version := data[n : n+2]
    n += 2
    log.Printf("Defined by profile: 0x%x\n", version) // 0x5062 here, so no pb
    length := binary.BigEndian.Uint16(data[n : n+2])
    n += 2
    log.Printf("Length: %d\n", length)
    packIndicator := binary.BigEndian.Uint16(data[n : n+2])
    lastPack := (packIndicator >> 9) & 0x7f
    curPack := (packIndicator >> 2) & 0x7f
    padding := (packIndicator >> 0) & 0x03
    n += 2
    log.Printf("Packet: %d/%d\n", curPack, lastPack)
    log.Printf("Padding: %d\n", padding)
    offset := binary.BigEndian.Uint16(data[n : n+2])
    n += 2
    log.Printf("Offset: %d\n", offset)
    rawData := data[n : int(n)+(4*int(length - 1)) - int(padding)]
    log.Printf("RawData: %x", rawData)

    model := vmeta.TimedMetadata{}
    if err := proto.Unmarshal(rawData, &model); err != nil {
        return fmt.Errorf("failed to deserialize: %v", err)
    }

    return nil
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744787475a4593715.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信