|
@ -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 <massiot@via.ecp.fr> |
|
|
|
|
|
* |
|
|
|
|
|
* 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 <stdlib.h> |
|
|
|
|
|
#include <stdint.h> |
|
|
|
|
|
#include <stdbool.h> |
|
|
|
|
|
#include <inttypes.h> |
|
|
|
|
|
#include <string.h> |
|
|
|
|
|
#include <stdio.h> |
|
|
|
|
|
|
|
|
|
|
|
#include <bitstream/mpeg/ts.h> |
|
|
|
|
|
#include <bitstream/mpeg/psi.h> |
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************** |
|
|
|
|
|
* 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 < <input file> [> <output>]\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; |
|
|
|
|
|
} |