kopia lustrzana https://github.com/F5OEO/tstools
Add primative ability to extract RTP payloads from pcap
and also convert RTP to 264master
rodzic
3291138f81
commit
15a7be8f75
5
Makefile
5
Makefile
|
@ -183,7 +183,8 @@ PROGS = \
|
|||
$(BINDIR)/ts_packet_insert \
|
||||
$(BINDIR)/m2ts2ts \
|
||||
$(BINDIR)/pcapreport \
|
||||
$(BINDIR)/tsfilter
|
||||
$(BINDIR)/tsfilter \
|
||||
$(BINDIR)/rtp2264
|
||||
#\
|
||||
# $(BINDIR)/test_ps
|
||||
|
||||
|
@ -287,6 +288,8 @@ $(BINDIR)/tsfilter: $(OBJDIR)/tsfilter.o $(STATIC_LIB)
|
|||
$(BINDIR)/tsdvbsub: $(OBJDIR)/tsdvbsub.o $(STATIC_LIB)
|
||||
$(CC) $< -o $(BINDIR)/tsdvbsub $(LIBOPTS) $(LDFLAGS)
|
||||
|
||||
$(BINDIR)/rtp2264: $(OBJDIR)/rtp2264.o $(STATIC_LIB)
|
||||
$(CC) $< -o $(BINDIR)/rtp2264 $(LIBOPTS) $(LDFLAGS)
|
||||
|
||||
|
||||
|
||||
|
|
123
pcapreport.c
123
pcapreport.c
|
@ -87,6 +87,8 @@ struct pcapreport_section_struct {
|
|||
uint64_t pcr_start; // 90kHz
|
||||
uint64_t pcr_last;
|
||||
int64_t skew_last;
|
||||
int64_t skew_min;
|
||||
int64_t skew_max;
|
||||
uint64_t ts_byte_start;
|
||||
uint64_t ts_byte_final;
|
||||
int32_t rtp_skew_min;
|
||||
|
@ -111,7 +113,8 @@ typedef struct pcapreport_rtp_info_s
|
|||
// RTP info (if any) in a packet
|
||||
typedef struct rtp_header_s
|
||||
{
|
||||
int is_rtp;
|
||||
int is_rtp_ts;
|
||||
int is_rtp_raw;
|
||||
int marker;
|
||||
uint8_t payload_type;
|
||||
uint16_t sequence_number;
|
||||
|
@ -226,6 +229,8 @@ typedef struct pcapreport_ctx_struct
|
|||
uint32_t time_usec;
|
||||
time_t time_sec;
|
||||
|
||||
uint8_t rtp_raw_wanted[256];
|
||||
|
||||
pcapreport_stream_t * stream_hash[256];
|
||||
pcapreport_reassembly_t reassembly_env;
|
||||
} pcapreport_ctx_t;
|
||||
|
@ -362,7 +367,8 @@ section_name(const pcapreport_ctx_t * const ctx, const pcapreport_stream_t * con
|
|||
}
|
||||
|
||||
static void
|
||||
stream_gen_names2(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st)
|
||||
stream_gen_names2(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st,
|
||||
const rtp_header_t * const rtp_header)
|
||||
{
|
||||
const uint32_t dest_addr = st->output_dest_addr;
|
||||
const uint32_t dest_port = st->output_dest_port;
|
||||
|
@ -395,7 +401,8 @@ stream_gen_names2(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * cons
|
|||
memcpy(name, base_name, base_len + 1);
|
||||
|
||||
if (!fixed_extract_name)
|
||||
snprintf(name + base_len, 64, "%s.ts", identifier);
|
||||
snprintf(name + base_len, 64, "%s.%s", identifier,
|
||||
(rtp_header != NULL && rtp_header->is_rtp_raw) ? "rtp" : "ts");
|
||||
st->output_name = name;
|
||||
}
|
||||
|
||||
|
@ -409,11 +416,12 @@ stream_gen_names2(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * cons
|
|||
}
|
||||
|
||||
static void
|
||||
stream_gen_names(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st)
|
||||
stream_gen_names(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st,
|
||||
const rtp_header_t * const rtp_header)
|
||||
{
|
||||
// Only bother if there is some reason
|
||||
if (ctx->extract || ctx->csv_gen)
|
||||
stream_gen_names2(ctx, st);
|
||||
stream_gen_names2(ctx, st, rtp_header);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -475,6 +483,8 @@ section_create(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const s
|
|||
// Init "obvious" non-zero stuff
|
||||
tsect->rtp_skew_max = -0x7fffffff;
|
||||
tsect->rtp_skew_min = 0x7fffffff;
|
||||
tsect->skew_max = -0x7fffffff;
|
||||
tsect->skew_min = 0x7fffffff;
|
||||
|
||||
tsect->time_final =
|
||||
tsect->time_start = pkt_time(pcap_pkt_hdr);
|
||||
|
@ -484,7 +494,7 @@ section_create(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const s
|
|||
tsect->ts_byte_final = st->ts_bytes;
|
||||
|
||||
if (ctx->file_split_section || last == NULL)
|
||||
stream_gen_names(ctx, st);
|
||||
stream_gen_names(ctx, st, NULL);
|
||||
|
||||
return tsect;
|
||||
}
|
||||
|
@ -544,7 +554,7 @@ static int digest_times(pcapreport_ctx_t * const ctx,
|
|||
|
||||
// Deal with RTP contents - currently held with stream but could be moved to section
|
||||
// especially if we do more timestamp analysis
|
||||
if (rtp_header->is_rtp)
|
||||
if (rtp_header->is_rtp_ts)
|
||||
{
|
||||
pcapreport_rtp_info_t * const ri = &st->rtp_info;
|
||||
|
||||
|
@ -728,10 +738,15 @@ static int digest_times(pcapreport_ctx_t * const ctx,
|
|||
cur_jitter = jitter_add(&st->jitter, (int)skew,
|
||||
(uint32_t)(t_pcr & 0xffffffffU), 90000 * 10);
|
||||
|
||||
if (tsect->skew_max < skew)
|
||||
tsect->skew_max = skew;
|
||||
if (tsect->skew_min > skew)
|
||||
tsect->skew_min = skew;
|
||||
|
||||
if (tsect->jitter_max < cur_jitter)
|
||||
tsect->jitter_max = cur_jitter;
|
||||
|
||||
if (rtp_header->is_rtp)
|
||||
if (rtp_header->is_rtp_ts)
|
||||
{
|
||||
// We have both PCR & RTP times - look for min & max
|
||||
int32_t rtp_skew = (int32_t)(rtp_header->timestamp - (uint32_t)(t_pcr & 0xffffffffU));
|
||||
|
@ -895,6 +910,71 @@ stream_ts_check(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const
|
|||
// RTP payload types - RFC 3551
|
||||
// M2TS - RFC 2250
|
||||
|
||||
static int write_rtp_raw_packet(pcapreport_ctx_t * const ctx,
|
||||
pcapreport_stream_t * const st,
|
||||
const byte *data,
|
||||
const uint32_t len)
|
||||
{
|
||||
if (st->output_name)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (st->output_file == NULL)
|
||||
{
|
||||
fprint_msg("pcapreport: Dumping raw RTP packets for %s:%d to %s\n",
|
||||
ipv4_addr_to_string(st->output_dest_addr),
|
||||
st->output_dest_port,
|
||||
st->output_name);
|
||||
st->output_file = fopen(st->output_name, "wb");
|
||||
if (!st->output_file)
|
||||
{
|
||||
fprint_err("### pcapreport: Cannot open %s .\n",
|
||||
st->output_name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->verbose)
|
||||
{
|
||||
fprint_msg("++ Dumping %d bytes to output file.\n", len);
|
||||
}
|
||||
|
||||
// need header
|
||||
{
|
||||
byte hdr[8];
|
||||
hdr[0] = 'R';
|
||||
hdr[1] = 'T';
|
||||
hdr[2] = 'P';
|
||||
hdr[3] = ' ';
|
||||
hdr[4] = (len >> 24) & 0xff;
|
||||
hdr[5] = (len >> 16) & 0xff;
|
||||
hdr[6] = (len >> 8) & 0xff;
|
||||
hdr[7] = len & 0xff;
|
||||
rv = fwrite(hdr, sizeof(hdr), 1, st->output_file);
|
||||
if (rv != 1)
|
||||
{
|
||||
fprint_err( "### pcapreport: Couldn't write RTP hdr bytes"
|
||||
" to %s (error = %d).\n",
|
||||
st->output_name,
|
||||
ferror(st->output_file));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
rv = fwrite(data, 1, len, st->output_file);
|
||||
if (rv != len)
|
||||
{
|
||||
fprint_err( "### pcapreport: Couldn't write %d bytes"
|
||||
" to %s (error = %d).\n",
|
||||
len, st->output_name,
|
||||
ferror(st->output_file));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
stream_rtp_check(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const st,
|
||||
const byte * const data,
|
||||
|
@ -903,6 +983,8 @@ stream_rtp_check(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const
|
|||
{
|
||||
uint32_t offset;
|
||||
uint32_t padlen = 0;
|
||||
unsigned int payload_type;
|
||||
int is_raw = FALSE;
|
||||
|
||||
// Flatten output
|
||||
memset(rh, 0, sizeof(*rh));
|
||||
|
@ -916,7 +998,10 @@ stream_rtp_check(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const
|
|||
if ((data[0] & 0xc0) != 0x80)
|
||||
return FALSE;
|
||||
// We only deal with TS in RTP so check for that alone
|
||||
if ((data[1] & 0x7f) != 33) // PT bits
|
||||
payload_type = data[1] & 0x7f;
|
||||
if (ctx->rtp_raw_wanted[payload_type] != 0)
|
||||
is_raw = TRUE;
|
||||
else if ((data[1] & 0x7f) != 33) // PT bits
|
||||
return FALSE;
|
||||
|
||||
// ??Check sequence??
|
||||
|
@ -942,13 +1027,14 @@ stream_rtp_check(const pcapreport_ctx_t * const ctx, pcapreport_stream_t * const
|
|||
offset += 4 + uint_16_be(data + offset + 2);
|
||||
}
|
||||
|
||||
// trivial check for TS in payload
|
||||
if (offset + 188 + padlen > len || data[offset] != 0x47)
|
||||
// trivial check for TS in payload if not raw extraction
|
||||
if (!is_raw && (offset + 188 + padlen > len || data[offset] != 0x47))
|
||||
return FALSE;
|
||||
|
||||
rh->is_rtp = TRUE;
|
||||
rh->is_rtp_raw = is_raw;
|
||||
rh->is_rtp_ts = !is_raw;
|
||||
rh->marker = ((data[1] & 0x80) != 0);
|
||||
rh->payload_type = data[0] & 0x7f;
|
||||
rh->payload_type = (uint8_t)payload_type;
|
||||
rh->sequence_number = uint_16_be(data + 2);
|
||||
rh->timestamp = uint_32_be(data + 4);
|
||||
rh->ssrc = uint_32_be(data + 8);
|
||||
|
@ -1138,7 +1224,9 @@ stream_analysis(const pcapreport_ctx_t * const ctx, const pcapreport_stream_t *
|
|||
fmtx_timestamp(time_len == 0 ? 0LL : drift * 60LL * 90000LL / time_len, ctx->tfmt),
|
||||
drift == 0 ? 0LL : time_len / drift,
|
||||
drift == 0 ? "" : drift < 0 ? " (fast)" : " (slow)");
|
||||
fprint_msg(" Max jitter: %s\n", fmtx_timestamp(tsect->jitter_max, ctx->tfmt));
|
||||
fprint_msg(" Max jitter: %s; Skew min: %s, max: %s\n", fmtx_timestamp(tsect->jitter_max, ctx->tfmt),
|
||||
fmtx_timestamp(tsect->skew_min, ctx->tfmt),
|
||||
fmtx_timestamp(tsect->skew_max, ctx->tfmt));
|
||||
}
|
||||
if (st->rtp_info.n != 0)
|
||||
{
|
||||
|
@ -1450,6 +1538,7 @@ int main(int argc, char **argv)
|
|||
|
||||
ctx->opt_skew_discontinuity_threshold = SKEW_DISCONTINUITY_THRESHOLD;
|
||||
ctx->tfmt = FMTX_TS_DISPLAY_90kHz_RAW;
|
||||
ctx->rtp_raw_wanted[96] = 1;
|
||||
|
||||
ip_reassembly_init(&ctx->reassembly_env);
|
||||
|
||||
|
@ -1845,6 +1934,12 @@ int main(int argc, char **argv)
|
|||
|
||||
if (stream_rtp_check(ctx, st, data, len, &rtp_hdr))
|
||||
{
|
||||
if (ctx->extract && rtp_hdr.is_rtp_raw)
|
||||
{
|
||||
stream_gen_names(ctx, st, &rtp_hdr);
|
||||
write_rtp_raw_packet(ctx, st, data, len);
|
||||
}
|
||||
|
||||
data += rtp_hdr.header_len;
|
||||
len -= rtp_hdr.header_len + rtp_hdr.pad_len;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* Report on a pcap (.pcap) file.
|
||||
*
|
||||
* <rrw@kynesim.co.uk> 2008-09-05
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the MPEG TS, PS and ES tools.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Amino Communications Ltd.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Richard Watts, Kynesim <rrw@kynesim.co.uk>
|
||||
*
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
// H.264 over RTP is defined in RFC3984
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#ifdef _WIN32
|
||||
#include <stddef.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include "compat.h"
|
||||
#include "version.h"
|
||||
#include "misc_fns.h"
|
||||
#include "fmtx.h"
|
||||
|
||||
#define RTP_HDR_LEN 8
|
||||
|
||||
#define RTP_PREFIX_STRING "RTP "
|
||||
#define RTP_PREFIX_LEN 4
|
||||
#define RTP_LEN_OFFSET 4
|
||||
|
||||
static int c642b(const char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') ? c - 'A' :
|
||||
(c >= 'a' && c <= 'z') ? c - 'a' + 26 :
|
||||
(c >= '0' && c <= '9') ? c - '0' + 52 :
|
||||
(c == '+' || c == '-') ? 62 :
|
||||
(c == '/' || c == '_') ? 63 :
|
||||
(c == '=') ? -1 : -2;
|
||||
}
|
||||
|
||||
static size_t b64str2binn(byte * const dest0, const size_t dlen, const char ** const plast, const char * src)
|
||||
{
|
||||
byte * dest = dest0;
|
||||
uint32_t a = 0;
|
||||
ssize_t i = 4;
|
||||
size_t slen = (dlen * 4 + 5) / 3;
|
||||
int b;
|
||||
|
||||
while ((b = c642b(*src++)) >= 0 && --slen != 0)
|
||||
{
|
||||
a = (a << 6) | b;
|
||||
if (--i == 0)
|
||||
{
|
||||
*dest++ = (a >> 16) & 0xff;
|
||||
*dest++ = (a >> 8) & 0xff;
|
||||
*dest++ = a & 0xff;
|
||||
i = 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Tidy up at the end
|
||||
if (i < 3) // i == 4 good, all done, i == 3 error
|
||||
{
|
||||
a <<= i * 6;
|
||||
*dest++ = (a >> 16) & 0xff;
|
||||
|
||||
// Consume '='
|
||||
if (b == -1)
|
||||
b = c642b(*src++);
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
*dest++ = (a >> 8) & 0xff;
|
||||
}
|
||||
else if (b == -1)
|
||||
++src;
|
||||
}
|
||||
|
||||
if (plast != NULL)
|
||||
*plast = src - 1;
|
||||
|
||||
return dest - dest0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
FILE *f_in = NULL;
|
||||
FILE *f_out = NULL;
|
||||
const char * fname_in;
|
||||
const char * fname_out;
|
||||
int zcount = 0;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: <in.rtp> <out.264>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fname_in = argv[1];
|
||||
fname_out = argv[2];
|
||||
|
||||
if ((f_in = fopen(fname_in, "rb")) == NULL)
|
||||
{
|
||||
perror(argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((f_out = fopen(fname_out, "wb")) == NULL)
|
||||
{
|
||||
perror(argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc > 3)
|
||||
{
|
||||
byte psbuf[0x1000];
|
||||
const char * eo64 = argv[3];
|
||||
|
||||
psbuf[0] = 0;
|
||||
psbuf[1] = 0;
|
||||
psbuf[2] = 0;
|
||||
psbuf[3] = 1;
|
||||
|
||||
do
|
||||
{
|
||||
size_t len = b64str2binn(psbuf + 4, sizeof(psbuf) - 4, &eo64, eo64);
|
||||
|
||||
if ((*eo64 != 0 && *eo64 != ',') || len == 0)
|
||||
{
|
||||
fprintf(stderr, "Bad B64 string: '%s' (len=%zd, chr=%d)\n", argv[3], len, *eo64);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fwrite(psbuf, len + 4, 1, f_out) != 1)
|
||||
{
|
||||
perror(fname_out);
|
||||
exit(1);
|
||||
}
|
||||
} while (*eo64++ == ',');
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
byte buf[0x10000];
|
||||
uint32_t rtplen;
|
||||
|
||||
if (fread(buf, RTP_HDR_LEN, 1, f_in) != 1)
|
||||
{
|
||||
if (ferror(f_in))
|
||||
perror(fname_in);
|
||||
break;
|
||||
}
|
||||
if (memcmp(buf, RTP_PREFIX_STRING, RTP_PREFIX_LEN) != 0)
|
||||
{
|
||||
fprintf(stderr, "### Bad RTP prefix\n");
|
||||
break;
|
||||
}
|
||||
rtplen = uint_32_be(buf + RTP_LEN_OFFSET);
|
||||
if (rtplen > sizeof(buf) || rtplen < 12)
|
||||
{
|
||||
fprintf(stderr, "### Bad RTP len: %" PRIu32 "\n", rtplen);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fread(buf, rtplen, 1, f_in) != 1)
|
||||
{
|
||||
if (ferror(f_in))
|
||||
perror(fname_in);
|
||||
else
|
||||
fprintf(stderr, "### Unexpected EOF\n");
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
size_t offset = 12 + (buf[0] & 0xf) * 4;
|
||||
size_t padlen = ((buf[0] & 0x20) != 0) ? buf[rtplen - 1] : 0;
|
||||
|
||||
// Check for extension
|
||||
if ((buf[0] & 0x10) != 0) // X bit
|
||||
offset += 4 + uint_16_be(buf + offset + 2);
|
||||
|
||||
if (rtplen < offset + padlen + 1)
|
||||
{
|
||||
fprintf(stderr, "### Bad RTP offset + padding\n");
|
||||
}
|
||||
|
||||
// OK - got payload
|
||||
|
||||
{
|
||||
const byte * p = buf + offset;
|
||||
const byte * p_end = buf + rtplen - padlen;
|
||||
byte buf2[0x18000]; // Allow for max expansion
|
||||
byte * q = buf2;
|
||||
byte sc1 = *p++;
|
||||
|
||||
if ((sc1 & 0x1f) == 28)
|
||||
{
|
||||
byte sc2 = *p++;
|
||||
if ((sc2 & 0x80) != 0) // S bit
|
||||
{
|
||||
// Start of fragmented unit
|
||||
sc1 = (sc1 & 0xe0) | (sc2 & 0x1f);
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
*q++ = 1;
|
||||
*q++ = sc1;
|
||||
zcount = 0;
|
||||
|
||||
printf("Fragmented block with code: %x\n", sc1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal start code
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
*q++ = 1;
|
||||
*q++ = sc1;
|
||||
zcount = 0;
|
||||
printf("Start block with code: %x\n", sc1);
|
||||
}
|
||||
|
||||
|
||||
// Engage emulation protect
|
||||
while (p < p_end)
|
||||
{
|
||||
const byte b = *p++;
|
||||
|
||||
if (zcount == 2 && b <= 3)
|
||||
{
|
||||
*q++ = 3;
|
||||
zcount = 0;
|
||||
}
|
||||
|
||||
*q++ = b;
|
||||
zcount = (b == 0) ? zcount + 1 : 0;
|
||||
}
|
||||
|
||||
if (fwrite(buf2, q - buf2, 1, f_out) != 1)
|
||||
{
|
||||
perror(fname_out);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fclose(f_out);
|
||||
fclose(f_in);
|
||||
return 0;
|
||||
}
|
||||
|
Ładowanie…
Reference in New Issue