diff --git a/CHANGELOG.md b/CHANGELOG.md index c41f569..0a147f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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 diff --git a/cmd/pcap-unpack/main.go b/cmd/pcap-unpack/main.go index 922cd34..e10f1db 100644 --- a/cmd/pcap-unpack/main.go +++ b/cmd/pcap-unpack/main.go @@ -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 } @@ -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:]) @@ -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 } diff --git a/cmd/pcap-unpack/pcap.go b/cmd/pcap-unpack/pcap.go index cf807fc..9044a81 100644 --- a/cmd/pcap-unpack/pcap.go +++ b/cmd/pcap-unpack/pcap.go @@ -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") } @@ -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() @@ -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 { @@ -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 { diff --git a/internal/version.go b/internal/version.go index 6835ba2..090e244 100644 --- a/internal/version.go +++ b/internal/version.go @@ -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