Add primative ability to extract RTP payloads from pcap

and also convert RTP to 264
master
John Cox 2014-08-19 14:57:51 +01:00
rodzic 3291138f81
commit 15a7be8f75
3 zmienionych plików z 394 dodań i 15 usunięć

Wyświetl plik

@ -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)

Wyświetl plik

@ -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;
}

281
rtp2264.c 100644
Wyświetl plik

@ -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;
}