Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Nothing yet

## [0.3.0] - 2025-11-19

### Changed

- Output file names are now capture_192_168_1_1_5000.ts instead of capture.pcap_192.168.1.1_5000.ts
- RTP headers are automatically removed if 12-byte length and 13'th byte is sync byte

## [0.2.0] - 2024-12-18

### Added
Expand All @@ -22,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- New tool pcap-replay that resends all UDP packets carrying UDP/TS or UDP/RTP/TS streams
- initial version of the repo

[Unreleased]: https://github.com/Eyevinn/mp2ts-tools/releases/tag/v0.2.0...HEAD
[Unreleased]: https://github.com/Eyevinn/mp2ts-tools/releases/tag/v0.3.0...HEAD
[0.3.0]: https://github.com/Eyevinn/mp2ts-tools/releases/tag/v0.3.0...v0.3.0
[0.2.0]: https://github.com/Eyevinn/mp2ts-tools/releases/tag/v0.1.0...v0.2.0
[0.1.0]: https://github.com/Eyevinn/mp2ts-tools/releases/tag/v0.1.0
9 changes: 5 additions & 4 deletions cmd/pcap-unpack/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ const (

var usg = `Usage of %s:

%s unpacks UDP streams from from a Wireshark/tcpdump capture.
%s unpacks UDP TS streams from from a Wireshark/tcpdump capture.

The output is saved as files with names input_destAddress_port.ts
(assuming that the streams are MPEG-2 TS streams).

RTP headers are automatically removed if the UDP length modulo
188 is 12 and the 13th byte is sync byte 0x47.
`

type options struct {
dst string
rtpHdr bool
version bool
}

Expand All @@ -36,7 +38,6 @@ func parseOptions(fs *flag.FlagSet, args []string) (*options, error) {

opts := options{}
fs.StringVar(&opts.dst, "dst", "", "Destination directory for output files")
fs.BoolVar(&opts.rtpHdr, "rtp", false, "Remove RTP headers UDP")
fs.BoolVar(&opts.version, "version", false, "Get mp4ff version")

err := fs.Parse(args[1:])
Expand Down Expand Up @@ -72,7 +73,7 @@ func run(args []string) error {
}

for _, pcapFile := range pcapFiles {
err := processPCAP(pcapFile, opts.dst, opts.rtpHdr)
err := processPCAP(pcapFile, opts.dst)
if err != nil {
return err
}
Expand Down
33 changes: 24 additions & 9 deletions cmd/pcap-unpack/pcap.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import (
"io"
"os"
"path/filepath"
"strings"

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)

func processPCAP(pcapFile string, dst string, rtpHdr bool) error {
func processPCAP(pcapFile string, dst string) error {
if pcapFile == "" {
return fmt.Errorf("pcapFile is required")
}
Expand All @@ -21,10 +22,10 @@ func processPCAP(pcapFile string, dst string, rtpHdr bool) error {
}
defer handle.Close()

return processPcapHandle(handle, pcapFile, dst, rtpHdr)
return processPcapHandle(handle, pcapFile, dst)
}

func processPcapHandle(handle *pcap.Handle, fileName, dstDir string, rtpHdr bool) error {
func processPcapHandle(handle *pcap.Handle, fileName, dstDir string) error {
// Loop through packets from source
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
packetChannel := packetSource.Packets()
Expand All @@ -47,15 +48,19 @@ func processPcapHandle(handle *pcap.Handle, fileName, dstDir string, rtpHdr bool
continue
}
dstPort := int(udp.DstPort)
dst := fmt.Sprintf("%s_%d.ts", ip.DstIP, dstPort)
// Replace dots in IP address with underscores
ipStr := strings.ReplaceAll(ip.DstIP.String(), ".", "_")
dst := fmt.Sprintf("%s_%d.ts", ipStr, dstPort)
if _, ok := udpDsts[dst]; !ok {
var dstPath string
// Remove file extension from input filename (e.g., .pcap)
baseName := filepath.Base(fileName)
baseNameWithoutExt := strings.TrimSuffix(baseName, filepath.Ext(baseName))
switch {
case dstDir == "":
dstPath = fmt.Sprintf("%s_%s", fileName, dst)
dstPath = fmt.Sprintf("%s_%s", baseNameWithoutExt, dst)
default:
base := filepath.Base(fileName)
dstPath = filepath.Join(dstDir, fmt.Sprintf("%s_%s", base, dst))
dstPath = filepath.Join(dstDir, fmt.Sprintf("%s_%s", baseNameWithoutExt, dst))
}
fh, err := os.Create(dstPath)
if err != nil {
Expand All @@ -67,8 +72,18 @@ func processPcapHandle(handle *pcap.Handle, fileName, dstDir string, rtpHdr bool
}
fh := udpDsts[dst]
payload := udp.Payload
if rtpHdr {
payload = payload[12:]
hdrLen := len(payload) % 188

switch hdrLen {
case 0:
// Pure TS, do nothing
case 12:
// Assume RTP header of length 12 and remove it if sync byte found
if payload[12] == 0x47 {
payload = payload[12:]
}
default:
// Unknown header length, write anyway
}
_, err := fh.Write(payload)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
)

var (
commitVersion string = "v0.2" // May be updated using build flags
commitDate string = "1734534139" // commitDate in Epoch seconds (may be overridden using build flags)
commitVersion string = "v0.3" // May be updated using build flags
commitDate string = "1763545288" // commitDate in Epoch seconds (may be overridden using build flags)
)

// GetVersion - get version and also commitHash and commitDate if inserted via Makefile
Expand Down
Loading