From 7114114e0063a303fc6bc5156880dfe8969005b4 Mon Sep 17 00:00:00 2001 From: Christophe Massiot Date: Tue, 12 Jul 2011 21:03:55 +0000 Subject: [PATCH] * examples/mpeg_print_pcr.c: New very basic (but useful) example program which prints the PCR PID of the first PMT. --- examples/Makefile | 2 +- examples/mpeg_print_pcr.c | 312 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 examples/mpeg_print_pcr.c diff --git a/examples/Makefile b/examples/Makefile index 48588f6..99bb9f7 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,7 +2,7 @@ CFLAGS = -Wall -O2 -g LDFLAGS = -OBJ = dvb_print_si dvb_ecmg dvb_ecmg_test +OBJ = dvb_print_si dvb_ecmg dvb_ecmg_test mpeg_print_pcr all: $(OBJ) diff --git a/examples/mpeg_print_pcr.c b/examples/mpeg_print_pcr.c new file mode 100644 index 0000000..4d28541 --- /dev/null +++ b/examples/mpeg_print_pcr.c @@ -0,0 +1,312 @@ +/***************************************************************************** + * mpeg_print_pcr.c: Prints PCR PID from the first program of the stream + ***************************************************************************** + * Copyright (C) 2011 VideoLAN + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/***************************************************************************** + * Local declarations + *****************************************************************************/ +#define MAX_PIDS 8192 +#define READ_ONCE 7 + +typedef struct ts_pid_t { + int i_psi_refcount; + int8_t i_last_cc; + + /* biTStream PSI section gathering */ + uint8_t *p_psi_buffer; + uint16_t i_psi_buffer_used; +} ts_pid_t; + +typedef struct sid_t { + uint16_t i_sid, i_pmt_pid; + uint8_t *p_current_pmt; +} sid_t; + +ts_pid_t p_pids[MAX_PIDS]; +static sid_t **pp_sids = NULL; +static int i_nb_sids = 0; + +static PSI_TABLE_DECLARE(pp_current_pat_sections); +static PSI_TABLE_DECLARE(pp_next_pat_sections); + +/***************************************************************************** + * handle_pat + *****************************************************************************/ +static void handle_pat(void) +{ + PSI_TABLE_DECLARE(pp_old_pat_sections); + uint8_t i_last_section = psi_table_get_lastsection(pp_next_pat_sections); + uint8_t i; + + if (psi_table_validate(pp_current_pat_sections) && + psi_table_compare(pp_current_pat_sections, pp_next_pat_sections)) { + /* Identical PAT. Shortcut. */ + psi_table_free(pp_next_pat_sections); + psi_table_init(pp_next_pat_sections); + return; + } + + if (!pat_table_validate(pp_next_pat_sections)) { + psi_table_free(pp_next_pat_sections); + psi_table_init(pp_next_pat_sections); + return; + } + + /* Switch tables. */ + psi_table_copy(pp_old_pat_sections, pp_current_pat_sections); + psi_table_copy(pp_current_pat_sections, pp_next_pat_sections); + psi_table_init(pp_next_pat_sections); + + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_current_pat_sections, i); + const uint8_t *p_program; + int j = 0; + + while ((p_program = pat_get_program(p_section, j)) != NULL) { + const uint8_t *p_old_program = NULL; + uint16_t i_sid = patn_get_program(p_program); + uint16_t i_pid = patn_get_pid(p_program); + j++; + + if (i_sid == 0) + continue; /* NIT */ + + if (!psi_table_validate(pp_old_pat_sections) + || (p_old_program = + pat_table_find_program(pp_old_pat_sections, i_sid)) + == NULL + || patn_get_pid(p_old_program) != i_pid) { + sid_t *p_sid; + int i_pmt; + if (p_old_program != NULL) + p_pids[patn_get_pid(p_old_program)].i_psi_refcount--; + p_pids[i_pid].i_psi_refcount++; + + for (i_pmt = 0; i_pmt < i_nb_sids; i_pmt++) + if (pp_sids[i_pmt]->i_sid == i_sid || + pp_sids[i_pmt]->i_sid == 0) + break; + + if (i_pmt == i_nb_sids) { + p_sid = malloc(sizeof(sid_t)); + pp_sids = realloc(pp_sids, ++i_nb_sids * sizeof(sid_t *)); + pp_sids[i_pmt] = p_sid; + p_sid->p_current_pmt = NULL; + } + else + p_sid = pp_sids[i_pmt]; + + p_sid->i_sid = i_sid; + p_sid->i_pmt_pid = i_pid; + } + } + } + + if (psi_table_validate(pp_old_pat_sections)) { + i_last_section = psi_table_get_lastsection( pp_old_pat_sections ); + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_old_pat_sections, i); + const uint8_t *p_program; + int j = 0; + + while ((p_program = pat_get_program(p_section, j)) != NULL) { + uint16_t i_sid = patn_get_program(p_program); + j++; + + if (i_sid == 0) + continue; /* NIT */ + + if (pat_table_find_program(pp_current_pat_sections, i_sid) + == NULL) { + int i_pmt; + for (i_pmt = 0; i_pmt < i_nb_sids; i_pmt++) + if (pp_sids[i_pmt]->i_sid == i_sid) { + pp_sids[i_pmt]->i_sid = 0; + free(pp_sids[i_pmt]->p_current_pmt); + pp_sids[i_pmt]->p_current_pmt = NULL; + break; + } + } + } + } + + psi_table_free(pp_old_pat_sections); + } +} + +static void handle_pat_section(uint16_t i_pid, uint8_t *p_section) +{ + if (i_pid != PAT_PID || !pat_validate(p_section)) { + free(p_section); + return; + } + + if (!psi_table_section(pp_next_pat_sections, p_section)) + return; + + handle_pat(); +} + +/***************************************************************************** + * handle_pmt + *****************************************************************************/ +static void handle_pmt(uint16_t i_pid, uint8_t *p_pmt) +{ + uint16_t i_sid = pmt_get_program(p_pmt); + sid_t *p_sid; + int i; + + /* we do this before checking the service ID */ + if (!pmt_validate(p_pmt)) { + free(p_pmt); + return; + } + + for (i = 0; i < i_nb_sids; i++) + if (pp_sids[i]->i_sid && pp_sids[i]->i_sid == i_sid) + break; + + if (i != i_nb_sids) { + p_sid = pp_sids[i]; + if (i_pid == p_sid->i_pmt_pid) { + printf("%"PRIu16"\n", pmt_get_pcrpid(p_pmt)); + exit(EXIT_SUCCESS); + } + } + + free(p_pmt); +} + +/***************************************************************************** + * handle_section + *****************************************************************************/ +static void handle_section(uint16_t i_pid, uint8_t *p_section) +{ + uint8_t i_table_id = psi_get_tableid(p_section); + + if (!psi_validate(p_section)) { + free(p_section); + return; + } + + switch (i_table_id) { + case PAT_TABLE_ID: + handle_pat_section(i_pid, p_section); + break; + + case PMT_TABLE_ID: + handle_pmt(i_pid, p_section); + break; + + default: + free( p_section ); + break; + } +} + +/***************************************************************************** + * handle_psi_packet + *****************************************************************************/ +static void handle_psi_packet(uint8_t *p_ts) +{ + uint16_t i_pid = ts_get_pid(p_ts); + ts_pid_t *p_pid = &p_pids[i_pid]; + uint8_t i_cc = ts_get_cc(p_ts); + const uint8_t *p_payload; + uint8_t i_length; + + if (ts_check_duplicate(i_cc, p_pid->i_last_cc) || !ts_has_payload(p_ts)) + return; + + if (p_pid->i_last_cc != -1 + && ts_check_discontinuity(i_cc, p_pid->i_last_cc)) + psi_assemble_reset(&p_pid->p_psi_buffer, &p_pid->i_psi_buffer_used); + + p_payload = ts_section(p_ts); + i_length = p_ts + TS_SIZE - p_payload; + + if (!psi_assemble_empty(&p_pid->p_psi_buffer, &p_pid->i_psi_buffer_used)) { + uint8_t *p_section = psi_assemble_payload(&p_pid->p_psi_buffer, + &p_pid->i_psi_buffer_used, + &p_payload, &i_length); + if (p_section != NULL) + handle_section(i_pid, p_section); + } + + p_payload = ts_next_section( p_ts ); + i_length = p_ts + TS_SIZE - p_payload; + + while (i_length) { + uint8_t *p_section = psi_assemble_payload(&p_pid->p_psi_buffer, + &p_pid->i_psi_buffer_used, + &p_payload, &i_length); + if (p_section != NULL) + handle_section(i_pid, p_section); + } +} + +/***************************************************************************** + * 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) +{ + int i; + + if (ppsz_argv[1] != NULL && + (!strcmp(ppsz_argv[1], "-h") || !strcmp(ppsz_argv[1], "--help"))) + usage(ppsz_argv[0]); + + setvbuf(stdout, NULL, _IOLBF, 0); + + memset(p_pids, 0, sizeof(p_pids)); + + for (i = 0; i < 8192; i++) { + p_pids[i].i_last_cc = -1; + psi_assemble_init( &p_pids[i].p_psi_buffer, + &p_pids[i].i_psi_buffer_used ); + } + + p_pids[PAT_PID].i_psi_refcount++; + + while (!feof(stdin) && !ferror(stdin)) { + uint8_t p_ts[TS_SIZE]; + size_t i_ret = fread(p_ts, sizeof(p_ts), 1, stdin); + if (i_ret != 1) continue; + if (ts_validate(p_ts)) { + uint16_t i_pid = ts_get_pid(p_ts); + ts_pid_t *p_pid = &p_pids[i_pid]; + if (p_pid->i_psi_refcount) + handle_psi_packet(p_ts); + p_pid->i_last_cc = ts_get_cc(p_ts); + } + } + + return EXIT_FAILURE; +}