From e5fae31a44c0b966fa90627064334a3bd726899f Mon Sep 17 00:00:00 2001 From: Christophe Massiot Date: Mon, 28 Oct 2013 15:16:39 +0100 Subject: [PATCH] new mpeg_restamp example --- .gitignore | 1 + examples/Makefile | 4 +- examples/mpeg_restamp.c | 191 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 examples/mpeg_restamp.c diff --git a/.gitignore b/.gitignore index 0f60761..e95dfff 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ examples/dvb_ecmg_test examples/dvb_gen_si examples/dvb_print_si examples/mpeg_print_pcr +examples/mpeg_restamp examples/rtp_check_seqnum diff --git a/examples/Makefile b/examples/Makefile index 43ebc95..61ac82f 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,7 +1,7 @@ CFLAGS = -Wall -O2 -g CFLAGS_LOCAL = -Wall -O2 -g -I. -I../.. -LDFLAGS = -OBJ = dvb_print_si dvb_gen_si dvb_ecmg dvb_ecmg_test mpeg_print_pcr rtp_check_seqnum +LDFLAGS = -lrt +OBJ = dvb_print_si dvb_gen_si dvb_ecmg dvb_ecmg_test mpeg_print_pcr rtp_check_seqnum mpeg_restamp ifeq "$(shell uname -s)" "Darwin" LDFLAGS += -liconv diff --git a/examples/mpeg_restamp.c b/examples/mpeg_restamp.c new file mode 100644 index 0000000..8d226e3 --- /dev/null +++ b/examples/mpeg_restamp.c @@ -0,0 +1,191 @@ +/***************************************************************************** + * mpeg_restamp.c: Restamps PCR and PTS/DTS to make them appear continuous + ***************************************************************************** + * Copyright (C) 2013 VideoLAN + * + * Authors: Christophe Massiot + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +/* Limitation: this supposes the PES header is not fragmented over several + * TS packets, and that the stream is not scrambled. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define UINT33_MAX UINT64_C(8589934592) +#define TS_CLOCK_MAX UINT33_MAX +#define CLOCK_FREQ UINT64_C(90000) +#define MAX_PCR_INTERVAL (CLOCK_FREQ / 10) + +/***************************************************************************** + * Local declarations + *****************************************************************************/ +static uint64_t i_last_pcr_date; +static uint64_t i_last_pcr = TS_CLOCK_MAX; +static uint64_t i_pcr_offset = 0; + +/***************************************************************************** + * get_date: + *****************************************************************************/ +static uint64_t get_date(void) +{ + struct timespec ts; + + /* Try to use POSIX monotonic clock if available */ + if (clock_gettime(CLOCK_MONOTONIC, &ts) == EINVAL) + /* Run-time fallback to real-time clock (always available) */ + (void)clock_gettime(CLOCK_REALTIME, &ts); + + return ((uint64_t)ts.tv_sec * CLOCK_FREQ) + + ((uint64_t)ts.tv_nsec * (CLOCK_FREQ / 10000) / 100000); +} + +/***************************************************************************** + * handle_pcr: + *****************************************************************************/ +static void handle_pcr(uint8_t *p_ts, uint64_t i_date) +{ + uint64_t i_pcr = tsaf_get_pcr(p_ts); + bool b_discontinuity = tsaf_has_discontinuity(p_ts); + + if (i_last_pcr == TS_CLOCK_MAX) + i_last_pcr = i_pcr; + else { + /* handle 2^33 wrap-arounds */ + uint64_t i_delta = + (TS_CLOCK_MAX + i_pcr - + (i_last_pcr % TS_CLOCK_MAX)) % TS_CLOCK_MAX; + if (i_delta <= MAX_PCR_INTERVAL && !b_discontinuity) + i_last_pcr = i_pcr; + else { + i_last_pcr += i_date - i_last_pcr_date; + i_last_pcr %= TS_CLOCK_MAX; + i_pcr_offset += TS_CLOCK_MAX + i_last_pcr - i_pcr; + i_pcr_offset %= TS_CLOCK_MAX; + i_last_pcr = i_pcr; + } + } + i_last_pcr_date = i_date; + if (!i_pcr_offset) + return; + + i_pcr += i_pcr_offset; + i_pcr %= TS_CLOCK_MAX; + tsaf_set_pcr(p_ts, i_pcr); + tsaf_clear_discontinuity(p_ts); +} + +/***************************************************************************** + * handle_ts: + *****************************************************************************/ +static uint64_t handle_ts(uint64_t i_ts) +{ + i_ts += i_pcr_offset; + i_ts %= TS_CLOCK_MAX; + return i_ts; +} + +/***************************************************************************** + * Main loop + *****************************************************************************/ +static void usage(const char *psz) +{ + fprintf(stderr, "usage: %s [] < [> ]\n", psz); + exit(EXIT_FAILURE); +} + +int main(int i_argc, char **ppsz_argv) +{ + if (i_argc > 2 || i_argc < 1 || + (!strcmp(ppsz_argv[1], "-h") || !strcmp(ppsz_argv[1], "--help"))) + usage(ppsz_argv[0]); + + unsigned int i_mtu = TS_SIZE; + if (i_argc == 2) { + i_mtu = strtoul(ppsz_argv[1], NULL, 0); + if (!i_mtu) + usage(ppsz_argv[0]); + } + + for ( ; ; ) { + uint8_t p_buffer[i_mtu]; + ssize_t i_read = read(STDIN_FILENO, p_buffer, i_mtu); + uint64_t date = get_date(); + if (i_read == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + exit(EXIT_FAILURE); + } + + if (!i_read) + exit(EXIT_SUCCESS); + + uint8_t *p_ts = p_buffer; + while (p_ts < p_buffer + i_mtu && ts_validate(p_ts)) { + if (ts_has_adaptation(p_ts) && ts_get_adaptation(p_ts) && + tsaf_has_pcr(p_ts)) + handle_pcr(p_ts, date); + + uint16_t header_size = TS_HEADER_SIZE + + (ts_has_adaptation(p_ts) ? 1 : 0) + + ts_get_adaptation(p_ts); + if (ts_get_unitstart(p_ts) && ts_has_payload(p_ts) && + header_size + PES_HEADER_SIZE_PTS <= TS_SIZE && + pes_validate(p_ts + header_size) && + pes_get_streamid(p_ts + header_size) != + PES_STREAM_ID_PRIVATE_2 && + pes_validate_header(p_ts + header_size) && + pes_has_pts(p_ts + header_size) && + pes_validate_pts(p_ts + header_size)) { + pes_set_pts(p_ts + header_size, + handle_ts(pes_get_pts(p_ts + header_size))); + + if (header_size + PES_HEADER_SIZE_PTSDTS <= TS_SIZE && + pes_has_dts(p_ts + header_size) && + pes_validate_dts(p_ts + header_size)) + pes_set_dts(p_ts + header_size, + handle_ts(pes_get_dts(p_ts + header_size))); + } + + p_ts += TS_SIZE; + } + + ssize_t i_written = write(STDOUT_FILENO, p_buffer, i_mtu); + if (i_written == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + exit(EXIT_FAILURE); + } + } + + return EXIT_FAILURE; +}