diff --git a/scripts/rtp/Makefile b/scripts/rtp/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..57a32c3e34a53756e58ebe674d7a2cb7e9534bd8 --- /dev/null +++ b/scripts/rtp/Makefile @@ -0,0 +1,22 @@ +# Makefile for RTP utilities (rtpdump_concat) + +CC ?= gcc +CFLAGS ?= -Wall -Wextra -O2 -I../../lib_util -I../../lib_com +LDFLAGS ?= + +# Source files +RTPDUMP_SRC = ../../lib_util/rtpdump.c +CONCAT_SRC = rtpdump_concat.c + +# Output binary +TARGET ?= rtpdump_concat + +all: $(TARGET) + +$(TARGET): $(CONCAT_SRC) $(RTPDUMP_SRC) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +clean: + rm -f $(TARGET) *.o + +.PHONY: all clean diff --git a/scripts/rtp/README.md b/scripts/rtp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6c1f59c339f69f731f1994d7301c38719728db9e --- /dev/null +++ b/scripts/rtp/README.md @@ -0,0 +1,107 @@ +# RTP Utilities + +## rtpdump_concat + +Concatenates multiple RTPDUMP files with continuous sequence numbers and timestamps. + +### Building + +```bash +cd scripts/rtp +make +``` + +### Usage + +```bash +./rtpdump_concat -o [options] [...] +``` + +### Options + +**Required:** +- `-o ` - Output RTPDUMP file + +**RTP Header Overrides:** +- `-s ` - Override SSRC (synchronization source identifier) +- `-p ` - Override payload type (0-127) +- `-i ` - Initial sequence number (default: from first packet) + +**RTPDUMP Header:** +- `-a
` - Source IP address (default: from first file or 127.0.0.1) +- `-P ` - Source port (default: from first file or 5000) + +**Timing:** +- `-t ` - Timestamp interval in milliseconds (default: auto-detect from packets, fallback 20ms) + +**Limiting:** +- `-n ` - Maximum number of packets to read from each input file + +**Other:** +- `-v` - Verbose output +- `-h, --help` - Show help message + +### Examples + +Basic concatenation: +```bash +./rtpdump_concat -o combined.rtp part1.rtp part2.rtp part3.rtp +``` + +Override RTP header fields for consistency: +```bash +./rtpdump_concat -o out.rtp -s 0xAABBCCDD -p 96 -i 1000 in1.rtp in2.rtp +``` + +Force 20ms frame spacing: +```bash +./rtpdump_concat -o out.rtp -t 20 in1.rtp in2.rtp in3.rtp +``` + +Set source IP address and port: +```bash +./rtpdump_concat -o out.rtp -a 192.168.1.100 -P 5004 in1.rtp in2.rtp +``` + +### Description + +This tool reads multiple RTPDUMP files and concatenates them into a single output file. It ensures: + +1. **Continuous sequence numbers**: The RTP sequence numbers are adjusted so they increment continuously across file boundaries +2. **Continuous timestamps**: The RTP timestamps are adjusted to maintain continuity +3. **Continuous time offsets**: The packet time offsets (in the RTPDUMP format) are adjusted for seamless concatenation + +### RTP and RTPDUMP Header Overrides + +When concatenating files from separate encoding sessions, each file may have different header values. The override options allow you to normalize these fields: + +**RTP Packet Headers:** +- **SSRC override** (`-s`): Ensures all packets have the same synchronization source identifier, making the output appear as a single RTP stream +- **Payload type override** (`-p`): Normalizes the payload type field across all packets +- **Initial sequence number** (`-i`): Sets a specific starting sequence number instead of using the first packet's value +- **Timestamp interval** (`-t`): Forces a specific frame duration when the auto-detection might not work correctly + +**RTPDUMP File Header:** +- **Source address** (`-a`): Sets the source IP address in the RTPDUMP file header (default: copied from first input file, or 127.0.0.1) +- **Source port** (`-P`): Sets the source port in the RTPDUMP file header (default: copied from first input file, or 5000) + +The IP address MUST be IPv4 and can be specified in dotted notation (e.g., `192.168.1.100`) or hex format (e.g., `0xC0A80164`). + +These options are primarily intended for testing format switching behavior by concatenating separately-encoded files with different IVAS formats while maintaining consistent RTP headers (similar to the previous separate encoder main program in `encoder_fmtsw.c`). + +### Implementation Details + +- Uses the RTPDUMP library from lib_util/rtpdump.c +- Supports up to 100 input files +- Assumes 20ms frame duration at 16kHz RTP clock rate for timestamp calculation + +### Building the binaries on macOS + +**macOS universal:** +`make CFLAGS="-Wall -Wextra -O2 -I../../lib_util -I../../lib_com -arch x86_64 -arch arm64"` + +**Linux x86_64 static:** +`docker run --rm --platform linux/amd64 -v $(pwd)/../..:/src -w /src/scripts/rtp gcc:latest make clean all LDFLAGS="-static"` + +**Windows 64-bit:** +`docker run --rm --platform linux/amd64 -v $(pwd)/../..:/src -w /src/scripts/rtp gcc:latest bash -c "apt-get update -qq && apt-get install -y -qq gcc-mingw-w64-x86-64 && x86_64-w64-mingw32-gcc --version && make clean all CC=x86_64-w64-mingw32-gcc LDFLAGS='-static'"` diff --git a/scripts/rtp/rtpdump_concat.c b/scripts/rtp/rtpdump_concat.c new file mode 100644 index 0000000000000000000000000000000000000000..4088a241ed32b516210c4e9630cd51cc3baf2f86 --- /dev/null +++ b/scripts/rtp/rtpdump_concat.c @@ -0,0 +1,538 @@ +/****************************************************************************************************** + + (C) 2022-2026 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository. All Rights Reserved. + + This software is protected by copyright law and by international treaties. + The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB, + Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD., + Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange, + Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other + contributors to this repository retain full ownership rights in their respective contributions in + the software. This notice grants no license of any kind, including but not limited to patent + license, nor is any license granted by implication, estoppel or otherwise. + + Contributors are required to enter into the IVAS codec Public Collaboration agreement before making + contributions. + + This software is provided "AS IS", without any express or implied warranties. The software is in the + development stage. It is intended exclusively for experts who have experience with such software and + solely for the purpose of inspection. All implied warranties of non-infringement, merchantability + and fitness for a particular purpose are hereby disclaimed and excluded. + + Any dispute, controversy or claim arising under or in relation to providing this software shall be + submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in + accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and + the United Nations Convention on Contracts on the International Sales of Goods. + +*******************************************************************************************************/ + +/* + * rtpdump_concat - Concatenate multiple RTPDUMP files with continuous timestamps and sequence numbers + * + * Usage: rtpdump_concat -o output.rtp input1.rtp input2.rtp [input3.rtp ...] + */ + +#include +#include +#include + +#include "../../lib_com/options.h" +#include "../../lib_util/rtpdump.h" + +#define MAX_INPUT_FILES 100 + +/* Parse IP address (IPv4 only!) in dotted notation (e.g., "192.168.1.1") or hex format */ +static int parseIPAddress(const char *str, unsigned int *address) +{ + unsigned int a, b, c, d; + + /* Try dotted notation first (192.168.1.1) */ + if (sscanf(str, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) + { + if (a <= 255 && b <= 255 && c <= 255 && d <= 255) + { + *address = (a << 24) | (b << 16) | (c << 8) | d; + return 1; + } + } + + /* Try hex format (0xAABBCCDD) */ + if (sscanf(str, "%i", (int*)address) == 1) + { + return 1; + } + + return 0; +} + +static void usage(const char *progname) +{ + fprintf(stderr, "\nUsage: %s -o [options] [...]\n", progname); + fprintf(stderr, "\nConcatenates multiple RTPDUMP files with continuous sequence numbers and timestamps.\n"); + fprintf(stderr, "\nRequired:\n"); + fprintf(stderr, " -o Output RTPDUMP file\n"); + fprintf(stderr, "\nRTP Header Overrides:\n"); + fprintf(stderr, " -s Override SSRC (synchronization source identifier)\n"); + fprintf(stderr, " -p Override payload type (0-127)\n"); + fprintf(stderr, " -i Initial sequence number (default: from first packet)\n"); + fprintf(stderr, "\nRTPDUMP Header:\n"); + fprintf(stderr, " -a
Source IP (IPv4 only!) address (default: from first file or 127.0.0.1)\n"); + fprintf(stderr, " -P Source port (default: from first file or 5000)\n"); + fprintf(stderr, "\nTiming:\n"); + fprintf(stderr, " -t Timestamp interval in milliseconds (default: auto-detect, fallback 20ms)\n"); + fprintf(stderr, "\nLimiting:\n"); + fprintf(stderr, " -n Maximum number of packets to read from each input file\n"); + fprintf(stderr, "\nOther:\n"); + fprintf(stderr, " -v Verbose output\n"); + fprintf(stderr, " -h, --help Show this help message\n"); + fprintf(stderr, "\nExamples:\n"); + fprintf(stderr, " Basic concatenation:\n"); + fprintf(stderr, " %s -o combined.rtp part1.rtp part2.rtp part3.rtp\n\n", progname); + fprintf(stderr, " Override RTP headers for consistency:\n"); + fprintf(stderr, " %s -o out.rtp -s 0xAABBCCDD -p 96 -i 1000 in1.rtp in2.rtp\n\n", progname); + fprintf(stderr, " Set IP and port:\n"); + fprintf(stderr, " %s -o out.rtp -a 192.168.1.100 -P 5004 in1.rtp in2.rtp\n\n", progname); + fprintf(stderr, " Quick format-switching test (3 packets from each file):\n"); + fprintf(stderr, " %s -o switch.rtp -n 3 stereo.rtp foa.rtp mc51.rtp\n", progname); + fprintf(stderr, "\n"); +} + +int main(int argc, char **argv) +{ + const char *outputFilename = NULL; + const char *inputFilenames[MAX_INPUT_FILES]; + int numInputFiles = 0; + int verbose = 0; + int i, arg; + + RTPDUMP_HANDLE hOut = NULL; + RTPDUMP_RTPPACKET packet; + + /* RTP header overrides */ + int overrideSSRC = 0; + unsigned int ssrc = 0; + int overridePT = 0; + unsigned char payloadType = 96; + int overrideInitSeq = 0; + unsigned short initSeqNum = 0; + int overrideTsInterval = 0; + unsigned int timestampInterval = 320; /* Default: 20ms at 16kHz */ + unsigned int timeOffsetInterval_ms = 20; /* Default: 20ms */ + + /* Packet limit per file (0 = unlimited) */ + int maxPacketsPerFile = 0; + + /* RTPDUMP header overrides */ + int overrideAddress = 0; + unsigned int sourceAddress = 0x7F000001; /* Default: 127.0.0.1 */ + int overridePort = 0; + unsigned short sourcePort = 5000; /* Default: 5000 */ + + /* Tracking for continuity */ + unsigned short nextSeqNum = 0; + unsigned int nextTimestamp = 0; + unsigned int nextTimeOffset_ms = 0; + + /* Parse command line */ + for (arg = 1; arg < argc; arg++) + { + if (strcmp(argv[arg], "-o") == 0) + { + if (++arg >= argc) + { + fprintf(stderr, "Error: -o requires an argument\n"); + usage(argv[0]); + return 1; + } + outputFilename = argv[arg]; + } + else if (strcmp(argv[arg], "-s") == 0) + { + if (++arg >= argc) + { + fprintf(stderr, "Error: -s requires an argument\n"); + usage(argv[0]); + return 1; + } + overrideSSRC = 1; + if (sscanf(argv[arg], "%i", &i) == 1) + { + ssrc = (unsigned int)i; + } + else + { + fprintf(stderr, "Error: Invalid SSRC value: %s\n", argv[arg]); + return 1; + } + } + else if (strcmp(argv[arg], "-p") == 0) + { + if (++arg >= argc) + { + fprintf(stderr, "Error: -p requires an argument\n"); + usage(argv[0]); + return 1; + } + overridePT = 1; + if (sscanf(argv[arg], "%d", &i) == 1 && i >= 0 && i <= 127) + { + payloadType = (unsigned char)i; + } + else + { + fprintf(stderr, "Error: Invalid payload type (must be 0-127): %s\n", argv[arg]); + return 1; + } + } + else if (strcmp(argv[arg], "-i") == 0) + { + if (++arg >= argc) + { + fprintf(stderr, "Error: -i requires an argument\n"); + usage(argv[0]); + return 1; + } + overrideInitSeq = 1; + if (sscanf(argv[arg], "%d", &i) == 1 && i >= 0 && i <= 65535) + { + initSeqNum = (unsigned short)i; + } + else + { + fprintf(stderr, "Error: Invalid initial sequence number (must be 0-65535): %s\n", argv[arg]); + return 1; + } + } + else if (strcmp(argv[arg], "-t") == 0) + { + if (++arg >= argc) + { + fprintf(stderr, "Error: -t requires an argument\n"); + usage(argv[0]); + return 1; + } + overrideTsInterval = 1; + if (sscanf(argv[arg], "%u", &timeOffsetInterval_ms) == 1 && timeOffsetInterval_ms > 0) + { + /* Assume 16kHz sample rate: interval_ms * 16 samples/ms = timestamp_delta */ + timestampInterval = timeOffsetInterval_ms * 16; + } + else + { + fprintf(stderr, "Error: Invalid timestamp interval: %s\n", argv[arg]); + return 1; + } + } + else if (strcmp(argv[arg], "-a") == 0) + { + if (++arg >= argc) + { + fprintf(stderr, "Error: -a requires an argument\n"); + usage(argv[0]); + return 1; + } + overrideAddress = 1; + if (!parseIPAddress(argv[arg], &sourceAddress)) + { + fprintf(stderr, "Error: Invalid IP address: %s\n", argv[arg]); + fprintf(stderr, " Use dotted notation (e.g., 192.168.1.1) or hex (e.g., 0x7F000001)\n"); + return 1; + } + } + else if (strcmp(argv[arg], "-P") == 0) + { + if (++arg >= argc) + { + fprintf(stderr, "Error: -P requires an argument\n"); + usage(argv[0]); + return 1; + } + overridePort = 1; + if (sscanf(argv[arg], "%d", &i) == 1 && i >= 0 && i <= 65535) + { + sourcePort = (unsigned short)i; + } + else + { + fprintf(stderr, "Error: Invalid port number (must be 0-65535): %s\n", argv[arg]); + return 1; + } + } + else if (strcmp(argv[arg], "-n") == 0) + { + if (++arg >= argc) + { + fprintf(stderr, "Error: -n requires an argument\n"); + usage(argv[0]); + return 1; + } + if (sscanf(argv[arg], "%d", &maxPacketsPerFile) != 1 || maxPacketsPerFile <= 0) + { + fprintf(stderr, "Error: Invalid packet count (must be > 0): %s\n", argv[arg]); + return 1; + } + } + else if (strcmp(argv[arg], "-v") == 0) + { + verbose = 1; + } + else if (strcmp(argv[arg], "-h") == 0 || strcmp(argv[arg], "--help") == 0) + { + usage(argv[0]); + return 0; + } + else if (argv[arg][0] == '-') + { + fprintf(stderr, "Error: Unknown option: %s\n", argv[arg]); + usage(argv[0]); + return 1; + } + else + { + /* Input file */ + if (numInputFiles >= MAX_INPUT_FILES) + { + fprintf(stderr, "Error: Too many input files (max %d)\n", MAX_INPUT_FILES); + return 1; + } + inputFilenames[numInputFiles++] = argv[arg]; + } + } + + /* Validate arguments */ + if (!outputFilename) + { + fprintf(stderr, "Error: Output file not specified\n"); + usage(argv[0]); + return 1; + } + + if (numInputFiles == 0) + { + fprintf(stderr, "Error: No input files specified\n"); + usage(argv[0]); + return 1; + } + + if (verbose) + { + printf("Concatenating %d RTPDUMP files:\n", numInputFiles); + for (i = 0; i < numInputFiles; i++) + { + printf(" [%d] %s\n", i + 1, inputFilenames[i]); + } + printf("Output: %s\n", outputFilename); + + if (overrideSSRC) + printf("Override SSRC: 0x%08X\n", ssrc); + if (overridePT) + printf("Override Payload Type: %u\n", payloadType); + if (overrideInitSeq) + printf("Initial Sequence Number: %u\n", initSeqNum); + if (overrideAddress) + printf("Source Address: %u.%u.%u.%u\n", + (sourceAddress >> 24) & 0xFF, (sourceAddress >> 16) & 0xFF, + (sourceAddress >> 8) & 0xFF, sourceAddress & 0xFF); + if (overridePort) + printf("Source Port: %u\n", sourcePort); + if (overrideTsInterval) + printf("Timestamp Interval: %u ms (RTP timestamp delta: %u)\n", timeOffsetInterval_ms, timestampInterval); + if (maxPacketsPerFile > 0) + printf("Max packets per file: %d\n", maxPacketsPerFile); + printf("\n"); + } + + /* Read header info from first input file if not overridden */ + if (!overrideAddress || !overridePort) + { + RTPDUMP_HANDLE hFirstFile = NULL; + if (RTPDUMP_OpenForReading(&hFirstFile, inputFilenames[0]) == RTPDUMP_NO_ERROR) + { + uint32_t readAddress = 0; + uint16_t readPort = 0; + if (RTPDUMP_GetHeaderInfo(hFirstFile, &readAddress, &readPort, NULL, NULL) == RTPDUMP_NO_ERROR) + { + if (!overrideAddress) + { + sourceAddress = readAddress; + if (verbose) + { + printf("Using source address from first file: %u.%u.%u.%u\n", + (sourceAddress >> 24) & 0xFF, (sourceAddress >> 16) & 0xFF, + (sourceAddress >> 8) & 0xFF, sourceAddress & 0xFF); + } + } + if (!overridePort) + { + sourcePort = readPort; + if (verbose) + { + printf("Using source port from first file: %u\n", sourcePort); + } + } + } + RTPDUMP_Close(&hFirstFile, 1); + if (verbose && (!overrideAddress || !overridePort)) + { + printf("\n"); + } + } + else + { + fprintf(stderr, "Warning: Could not read header from first file, using defaults\n"); + } + } + + /* Open output file */ + if (RTPDUMP_OpenForWriting(&hOut, outputFilename) != RTPDUMP_NO_ERROR) + { + fprintf(stderr, "Error: Failed to open output file: %s\n", outputFilename); + return 1; + } + + /* Configure output file header */ + if (RTPDUMP_SetHeaderInfo(hOut, sourceAddress, sourcePort, 0, 0) != RTPDUMP_NO_ERROR) + { + fprintf(stderr, "Error: Failed to set RTPDUMP header info\n"); + RTPDUMP_Close(&hOut, 1); + return 1; + } + + /* Set initial sequence number */ + if (overrideInitSeq) + { + nextSeqNum = initSeqNum; + } + + /* Process each input file */ + for (i = 0; i < numInputFiles; i++) + { + RTPDUMP_HANDLE hIn = NULL; + unsigned int timeoffset_ms; + unsigned int firstTimeOffset_ms = 0; + unsigned short firstSeqNum = 0; + unsigned int firstTimestamp = 0; + unsigned short lastSeqNum = 0; + unsigned int lastTimestamp = 0; + unsigned int lastTimeOffset_ms = 0; + int firstPacket = 1; + int packetCount = 0; + unsigned int timestampDelta = overrideTsInterval ? timestampInterval : 320; /* Use override or default */ + unsigned int timeOffsetDelta_ms = overrideTsInterval ? timeOffsetInterval_ms : 20; /* Use override or default */ + + if (verbose) + { + printf("Processing file %d/%d: %s\n", i + 1, numInputFiles, inputFilenames[i]); + } + + /* Open input file */ + if (RTPDUMP_OpenForReading(&hIn, inputFilenames[i]) != RTPDUMP_NO_ERROR) + { + fprintf(stderr, "Error: Failed to open input file: %s\n", inputFilenames[i]); + RTPDUMP_Close(&hOut, 1); + return 1; + } + + /* Read and process packets */ + while ((maxPacketsPerFile == 0 || packetCount < maxPacketsPerFile) && + RTPDUMP_ReadPacket(hIn, &packet, &timeoffset_ms) == RTPDUMP_NO_ERROR) + { + RTPDUMP_RTPPACKET adjustedPacket = packet; + unsigned int adjustedTimeOffset_ms; + + /* On first packet of each file, record base values */ + if (firstPacket) + { + firstPacket = 0; + firstSeqNum = packet.sequenceNumber; + firstTimestamp = packet.timeStamp; + firstTimeOffset_ms = timeoffset_ms; + + if (verbose) + { + printf(" First packet - seq=%u, ts=%u, offset=%u ms\n", + firstSeqNum, firstTimestamp, firstTimeOffset_ms); + } + } + + /* Adjust sequence number for continuity */ + adjustedPacket.sequenceNumber = nextSeqNum + (packet.sequenceNumber - firstSeqNum); + + /* Adjust RTP timestamp for continuity */ + adjustedPacket.timeStamp = nextTimestamp + (packet.timeStamp - firstTimestamp); + + /* Adjust time offset for continuity */ + adjustedTimeOffset_ms = nextTimeOffset_ms + (timeoffset_ms - firstTimeOffset_ms); + + /* Apply RTP header overrides */ + if (overrideSSRC) + { + adjustedPacket.ssrc = ssrc; + } + if (overridePT) + { + adjustedPacket.payloadTypeId = payloadType; + } + + /* Write adjusted packet */ + if (RTPDUMP_WritePacket(hOut, &adjustedPacket, adjustedTimeOffset_ms) != RTPDUMP_NO_ERROR) + { + fprintf(stderr, "Error: Failed to write packet to output file\n"); + RTPDUMP_Close(&hIn, 1); + RTPDUMP_Close(&hOut, 1); + return 1; + } + + /* Track last packet values for calculating delta to next file */ + if (packetCount > 0 && !overrideTsInterval) + { + /* Infer deltas from packet spacing (unless overridden by user) */ + if (adjustedPacket.sequenceNumber == lastSeqNum + 1) + { + timestampDelta = adjustedPacket.timeStamp - lastTimestamp; + timeOffsetDelta_ms = adjustedTimeOffset_ms - lastTimeOffset_ms; + } + } + + lastSeqNum = adjustedPacket.sequenceNumber; + lastTimestamp = adjustedPacket.timeStamp; + lastTimeOffset_ms = adjustedTimeOffset_ms; + + packetCount++; + } + + /* Update next values for the following file */ + if (packetCount > 0) + { + nextSeqNum = lastSeqNum + 1; + nextTimestamp = lastTimestamp + timestampDelta; + nextTimeOffset_ms = lastTimeOffset_ms + timeOffsetDelta_ms; + } + + if (verbose) + { + printf(" Processed %d packets\n", packetCount); + if (packetCount > 0) + { + printf(" Last packet - seq=%u, ts=%u, offset=%u ms\n", lastSeqNum, lastTimestamp, lastTimeOffset_ms); + printf(" Next packet - seq=%u, ts=%u, offset=%u ms (delta: ts=%u, offset=%u ms)\n\n", + nextSeqNum, nextTimestamp, nextTimeOffset_ms, timestampDelta, timeOffsetDelta_ms); + } + } + + RTPDUMP_Close(&hIn, 1); + } + + RTPDUMP_Close(&hOut, 1); + + if (verbose) + { + printf("Successfully concatenated %d files to %s\n", numInputFiles, outputFilename); + } + + return 0; +} diff --git a/scripts/tools/Darwin/rtpdump_concat b/scripts/tools/Darwin/rtpdump_concat new file mode 100755 index 0000000000000000000000000000000000000000..ff177559496555aa1aadf453f625f3546a9d9b21 Binary files /dev/null and b/scripts/tools/Darwin/rtpdump_concat differ diff --git a/scripts/tools/Linux/rtpdump_concat b/scripts/tools/Linux/rtpdump_concat new file mode 100755 index 0000000000000000000000000000000000000000..7739dc7b8e467911584bfab5946614cef096d2ef Binary files /dev/null and b/scripts/tools/Linux/rtpdump_concat differ diff --git a/scripts/tools/Win32/rtpdump_concat.exe b/scripts/tools/Win32/rtpdump_concat.exe new file mode 100755 index 0000000000000000000000000000000000000000..d20120c066d1eeb5ac26d2f198d5591ccc0bc8d3 --- /dev/null +++ b/scripts/tools/Win32/rtpdump_concat.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acc0c27c0a12aac237be84ce0b5543b515ed6d2d0026a378effa5d672509468a +size 414364