Skip to content

Commit

Permalink
Added support for Single NAL and kinda Non-interleaved mode
Browse files Browse the repository at this point in the history
  • Loading branch information
hdiniz committed Aug 30, 2016
1 parent 26b61df commit 0f7d4ba
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 52 deletions.
100 changes: 56 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,56 @@
# rtpdump

Extract media files from RTP streams in pcap format

## codec support

This program is intended to support usual audio/video codecs used on IMS networks (VoLTE/VoWiFi).
Therefore, some codecs might be limited to usual scenarios on these networks.

+ AMR - [RFC 4867](https://tools.ietf.org/html/rfc4867)
Supports bandwidth-efficient and octet-aligned modes.
Single-channel, single-frame per packet only.
+ EVS - [3GPP TS 26.445](http://www.3gpp.org/DynaReport/26445.htm)
*Not yet supported.*
+ H263 - [RFC 2190](https://tools.ietf.org/html/rfc2190)
*Not yet supported.*
+ H264 - [RFC 6184](https://tools.ietf.org/html/rfc6184)
*Not yet supported.*

## usage

+ rtpdump streams [pcap]
displays RTP streams
+ rtpdump dump -i [pcap]
dumps a media stream.
`-i` options is for interactive dump. Codecs and modes are choosen via prompt.
**Currently only mode available**

## compiling

Checkout [gopacket](https://github.com/google/gopacket).
Linux should be straightforward.
For Windows, make sure mingw(32/64) toolchain is on PATH for gopacket WinPcap dependency. Install WinPcap on standard location `C:\WpdPack`

## planned features

1. Support for H264
2. Include stream analisys, packets lost, jitter, etc
3. Media player directly from pcap. ffmpeg support.
4. Jitter buffer to simulate original condition, i.e. packet loss due to jitter

## contributions

Are always appreciated.
# rtpdump

Extract media files from RTP streams in pcap format

## codec support

This program is intended to support usual audio/video codecs used on IMS networks (VoLTE/VoWiFi).
Therefore, some codecs might be limited to usual scenarios on these networks.

+ AMR - [RFC 4867](https://tools.ietf.org/html/rfc4867)
Supports bandwidth-efficient and octet-aligned modes.
Single-channel, single-frame per packet only.
+ H264 - [RFC 6184](https://tools.ietf.org/html/rfc6184)
Supports Single NAL Mode and some Non-Interleaved Mode streams, due to current lack of STAP-A support

| Payload Type | Support |
|--------------- |-------------- |
| 1-23 NAL Unit | Yes |
| 24 STAP-A | No - planned |
| 25 STAP-B | No |
| 26 MTAP16 | No |
| 27 MTAP24 | Yes |
| 28 FU-A | Yes |
| 29 FU-B | No |

+ EVS - [3GPP TS 26.445](http://www.3gpp.org/DynaReport/26445.htm)
*Not yet supported.*
+ H263 - [RFC 2190](https://tools.ietf.org/html/rfc2190)
*Not yet supported.*


## usage

+ rtpdump streams [pcap]
displays RTP streams
+ rtpdump dump -i [pcap]
dumps a media stream.
`-i` options is for interactive dump. Codecs and modes are choosen via prompt.
**Currently only mode available**

## compiling

Checkout [gopacket](https://github.com/google/gopacket).
Linux should be straightforward.
For Windows, make sure mingw(32/64) toolchain is on PATH for gopacket WinPcap dependency. Install WinPcap on standard location `C:\WpdPack`

## planned features

1. Support for H264
2. Include stream analisys, packets lost, jitter, etc
3. Media player directly from pcap. ffmpeg support.
4. Jitter buffer to simulate original condition, i.e. packet loss due to jitter

## contributions

Are always appreciated.
1 change: 1 addition & 0 deletions codecs/codecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package codecs

var CodecList = []CodecMetadata{
AmrMetadata,
H264Metadata,
}
112 changes: 112 additions & 0 deletions codecs/h264.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package codecs
import (
"errors"
"github.com/hdiniz/rtpdump/log"
"github.com/hdiniz/rtpdump/rtp"
)


var SINGLE_NAL_MODE = 0
var NON_INTERLEAVED_MODE = 1
var INTERLEAVED_MODE = 2

type H264 struct {
packetizationMode string
started bool
configured bool
timestamp uint32
}

func NewH264() Codec {
return &H264{started: false, configured: false, timestamp: 0}
}

func (c *H264) Init() {
}

func (c *H264) SetOptions(options map[string]string) error {

v,ok := options["packetization-mode"]
if !ok {
return errors.New("required codec option not present")
}

c.packetizationMode = v
return nil
}

func (c H264) GetFormatMagic() []byte {
return []byte{}
}

func (c *H264) HandleRtpPacket(packet *rtp.RtpPacket) (result []byte, err error) {
payload := packet.Payload
forbidden := (payload[0] & 0x80) == 0x80
if forbidden {
log.Warn("forbidden bit set in this payload")
return nil, errors.New("forbidden bit set in this payload")
}

nri := (payload[0] & 0x60) >> 5
nalType := payload[0] & 0x1F

log.Sdebug("h264, seq:%d nri:%d, nalType:%d",
packet.SequenceNumber, nri, nalType)

switch {
case nalType >= 1 && nalType <= 23:
return c.handleNalUnit(payload[:])
case nalType >= 24 && nalType <= 27:
//aggregation packet
log.Debug("h264, aggregation not supported")
return nil, errors.New("h264, aggregation not supported")
case nalType == 28:
return c.handleFuA(payload[:])
default:
log.Sdebug("h264, nal type not supported")
return nil, errors.New("h264, nal type not supported")
}
}

func (c *H264) handleNalUnit(payload []byte) (result []byte, err error) {
result = append(result, []byte{0x00, 0x00, 0x00, 0x01}...)
result = append(result, payload[:]...)
return result, nil
}
func (c *H264) handleFuA(payload []byte) (result []byte, err error) {
isStart := payload[1] & 0x80 == 0x80
//isEnd := payload[0] & 0x40 == 0x40

log.Sdebug("h264, FU-A isStart:%t", isStart)
if isStart {
result = append(result, []byte{0x00, 0x00, 0x00, 0x01}...)
nalUnitHeader := payload[0] & 0xE0
nalUnitHeader = nalUnitHeader | (payload[1] & 0x1F)
result = append(result, nalUnitHeader)
result = append(result, payload[2:]...)
} else {
result = append(result, payload[2:]...)
}
log.Sdebug("FU-A: %#v", result)

return
}


var H264Metadata = CodecMetadata{
Name: "h264",
LongName: "H.264",
Options: []CodecOption {
h264PacketizationModeOption,
},
Init: NewH264,
}

var h264PacketizationModeOption = CodecOption{
Required: true,
Name: "packetization-mode",
Description: "whether this payload is octet-aligned or bandwidth-efficient",
ValidValues: []string {"0", "1", "2"},
ValueDescription: []string {"Single NAL Unit Mode", "Non-Interleaved Mode", "Interleaved Mode"},
RestrictValues: true,
}
Binary file added dist/x32/rtpdump_x32_0.6.0.exe
Binary file not shown.
Binary file added dist/x64/rtpdump_x64_0.6.0.exe
Binary file not shown.
13 changes: 5 additions & 8 deletions rtp/rtpreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package rtp
import (
"errors"
"time"
log "github.com/Sirupsen/logrus"
"github.com/hdiniz/rtpdump/log"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
Expand All @@ -26,18 +26,13 @@ func NewRtpReader(path string) (reader *RtpReader, err error) {
func (r *RtpReader) openPcapFile(path string) (err error) {
r.handle, err = pcap.OpenOffline(path)
if err != nil {
log.WithFields(log.Fields{
"path": path,
"error": err,
}).Error("Failed to open pcap file")
log.Error("Failed to open pcap file")
return err
}
err = r.handle.SetBPFFilter(RtpCapureFilter)
if err != nil {
r.handle.Close()
log.WithFields(log.Fields{
"capture-filter": RtpCapureFilter,
}).Error("Failed to set bpf file")
log.Error("Failed to set bpf file")
return err
}
return nil
Expand Down Expand Up @@ -81,9 +76,11 @@ func (r *RtpReader) parsePacket(packet gopacket.Packet) error {
rtp, _ := rtpLayer.(*RtpLayer)
r.processRtpPacket(receivedAt, ip, udp, rtp)
} else {
log.Debug("Not able to decode RTP layer")
return errors.New("Not able to decode RTP layer")
}
} else {
log.Debug("Not able to decode Network/Transport layer")
return errors.New("Not able to decode Network/Transport layers")
}
return nil
Expand Down

0 comments on commit 0f7d4ba

Please sign in to comment.