/* * ISO 13818 stream multiplexer * Copyright (C) 2001 Convergence Integrated Media GmbH Berlin * Author: Oskar Schirmer (oskar@convergence.de) */ /* * Module: Splice TS * Purpose: Generate transport stream. * * This module generates from the available input stream data (as * seperated by the split functions) the complete output stream. * It provides functions to handle programs for the resulting stream, * as these are output format dependent. Further, it accepts PSI data * just in time, validating it not earlier than with the arrival of * the corresponding payload at this stage. */ #include "global.h" #include "crc.h" #include "error.h" #include "input.h" #include "output.h" #include "descref.h" #include "pes.h" #include "ts.h" #include "splice.h" #include "splicets.h" const boolean splice_multipleprograms = TRUE; static boolean changed_pat; static int pat_section; static const int last_patsection = 0; static byte nextpat_version; static byte pat_conticnt; static int transportstreamid; static int psi_size; static int psi_done; static byte psi_data [MAX_PSI_SIZE]; static byte unit_start; static byte *conticnt; static int psi_pid; static int progs; static prog_descr *prog [MAX_OUTPROG]; static int nextpid; static stream_descr *outs [MAX_STRPERTS]; static int next_psi_periodic; static int psi_frequency_msec; static boolean psi_frequency_changed; boolean splice_init (void) { progs = 0; nextpid = 0; memset (outs,0,sizeof(outs)); changed_pat = TRUE; pat_section = 0; nextpat_version = 0; pat_conticnt = 0; psi_size = psi_done = 0; unit_start = TS_UNIT_START; transportstreamid = 0x4227; psi_frequency_msec = 0; psi_frequency_changed = FALSE; return (TRUE); } void splice_settransportstreamid (int tsid) { transportstreamid = tsid; } void splice_setpsifrequency (int freq) { psi_frequency_msec = freq; psi_frequency_changed = TRUE; } static prog_descr *openprog (int programnb) { int i; i = progs; while (--i >= 0) { if (prog[i]->program_number == programnb) { return (prog[i]); } } return (NULL); } static int findapid (stream_descr *s) { boolean ok = TRUE; do { if ((nextpid < TS_PID_SPLICELO) || (nextpid >= TS_PID_SPLICEHI)) { if (!ok) { warn (LERR,"No PID found",ETSC,2,1,0); return (0); } ok = FALSE; nextpid = TS_PID_SPLICELO; } else { nextpid += 1; } } while (outs[nextpid] != NULL); outs[nextpid] = s; warn (LDEB,"Next PID",ETSC,2,2,nextpid); return (nextpid); } prog_descr *splice_openprog (int programnb) { prog_descr *p; int pid; warn (LIMP,"Open prog",ETSC,1,0,programnb); p = openprog (programnb); if (p == NULL) { if (progs < MAX_OUTPROG) { if ((pid = findapid (PMT_STREAM)) > 0) { if ((p = malloc(sizeof(prog_descr))) != NULL) { p->program_number = programnb; p->pcr_pid = -1; p->pmt_pid = pid; p->pmt_conticnt = 0; p->pmt_version = 0; p->changed = TRUE; p->pat_section = 0; /* more ? */ p->streams = 0; prog[progs++] = p; changed_pat = TRUE; } else { outs[pid] = NULL; warn (LERR,"Open prog",ETSC,1,1,0); } } } else { warn (LERR,"Max prog open",ETSC,1,2,0); } } return (p); } void splice_closeprog (prog_descr *p) { int i, n; stream_descr *s; file_descr *f; warn (LIMP,"Close prog",ETSC,3,0,p->program_number); while (p->streams > 0) { s = p->stream[0]; input_delprog (s,p); splice_delstream (p,s); if (s->u.d.progs == 0) { f = s->fdescr; input_closestream (s); input_closefileifunused (f); } } n = -1; if (p->pmt_pid >= 0) { i = progs; while (--i >= 0) { if (prog[i]->pmt_pid == p->pmt_pid) { n += 1; } } } i = progs; while (--i >= 0) { if (prog[i] == p) { prog[i] = prog[--progs]; if (n == 0) { outs[p->pmt_pid] = NULL; } free (p); changed_pat = TRUE; return; } } warn (LERR,"Close lost prog",ETSC,3,1,progs); } boolean splice_emptyprog (prog_descr *p) { return (p->streams == 0); } int splice_addstream (prog_descr *p, stream_descr *s, boolean force_sid) { int pid = 0; warn (LIMP,"Add stream",ETSC,4,force_sid,s->stream_id); if (p->streams < MAX_STRPERPRG) { if ((pid = findapid (s)) > 0) { if (!force_sid) { s->stream_id = input_findfreestreamid (p,s->stream_id); } p->stream[p->streams++] = s; p->changed = TRUE; s->u.d.pid = pid; } } return (pid); } boolean splice_delstream (prog_descr *p, stream_descr *s) { int i; warn (LIMP,"Del stream",ETSC,5,0,s->u.d.pid); i = p->streams; while (--i >= 0) { if (p->stream[i] == s) { outs[s->u.d.pid] = NULL; p->stream[i] = p->stream[--(p->streams)]; p->changed = TRUE; if (p->pcr_pid == s->u.d.pid) { p->pcr_pid = -1; } s->u.d.pid = 0; return (TRUE); } } warn (LERR,"Del lost stream",ETSC,5,1,p->streams); return (FALSE); } static int make_patsection (int section, byte *dest) { int i; byte *d; d = dest; *d++ = TS_TABLEID_PAT; d += 2; *d++ = transportstreamid >> 8; *d++ = (byte)transportstreamid; *d++ = 0xC0 | 0x01 | (nextpat_version << 1); *d++ = section; *d++ = last_patsection; i = progs; while (--i >= 0) { if (prog[i]->pat_section == section) { int x; x = prog[i]->program_number; *d++ = (x >> 8); *d++ = x; x = prog[i]->pmt_pid; *d++ = 0xE0 | (x >> 8); *d++ = x; } } i = d + CRC_SIZE - dest - TS_TRANSPORTID; dest[TS_SECTIONLEN] = 0xB0 | (i >> 8); dest[TS_SECTIONLEN+1] = i; crc32_calc (dest,i + TS_TRANSPORTID - CRC_SIZE,d); return (i + TS_TRANSPORTID); } static int make_pmtsection (stream_descr *s, prog_descr *p, byte *dest) { int i; byte *d; p->changed = FALSE; d = dest; *d++ = TS_TABLEID_PMT; d += 2; i = p->program_number; *d++ = (i >> 8); *d++ = i; *d++ = 0xC0 | 0x01 | (p->pmt_version << 1); p->pmt_version = (p->pmt_version+1) & 0x1F; *d++ = 0; *d++ = 0; if (p->pcr_pid < 0) { stream_descr *pcrs; pcrs = input_findpcrstream (p); if (pcrs == NULL) { pcrs = s; } pcrs->u.d.has_clockref = TRUE; pcrs->u.d.next_clockref = msec_now () - MAX_MSEC_PCRDIST; p->pcr_pid = pcrs->u.d.pid; } i = p->pcr_pid; *d++ = 0xE0 | (i >> 8); *d++ = i; d += 2; i = NUMBER_ELEMD; while (--i > 0) { if (s->fdescr->content == ct_program) { byte *y; y = s->fdescr->u.ps.stream[0]->elemdvld[i]; /* this one ? why ? */ if (y != NULL) { memcpy (d,y,y[1]+2); d += y[1]+2; } } } i = d - dest - (TS_PMT_PILEN+2); dest[TS_PMT_PILEN] = 0xF0 | (i >> 8); dest[TS_PMT_PILEN+1] = i; i = p->streams; while (--i >= 0) { if (p->stream[i]->u.d.mention) { int x; byte *e; *d++ = p->stream[i]->stream_type; x = p->stream[i]->u.d.pid; *d++ = 0xE0 | (x >> 8); *d++ = x; d += 2; e = d; x = NUMBER_ELEMD; while (--x > 0) { byte *y; y = p->stream[i]->elemdvld[x]; if (y != NULL) { memcpy (d,y,y[1]+2); d += y[1]+2; } } x = d - e; *--e = x; *--e = 0xF0 | (x >> 8); } } i = d + CRC_SIZE - dest - TS_TRANSPORTID; dest[TS_SECTIONLEN] = 0xB0 | (i >> 8); dest[TS_SECTIONLEN+1] = i; crc32_calc (dest,i + TS_TRANSPORTID - CRC_SIZE,d); return (i + TS_TRANSPORTID); } void process_finish (void) { warn (LIMP,"Finish",ETSC,6,0,0); } stream_descr *process_something (stream_descr *s) { byte *d; int pid; byte scramble; ctrl_buffer *c; int l, f, k; int privdata; int adapt_ext_len; int now; byte adapt_field_ctrl; byte adapt_flags1, adapt_flags2; warn (LDEB,"Splice TS",ETSC,0,0,s->ctrl.out); if (s->isamap) { validate_mapref (s); return (NULL); } c = &s->ctrl.ptr[s->ctrl.out]; if (psi_size > 0) { pid = psi_pid; scramble = 0; k = psi_size; } else { if (unit_start != 0) { now = msec_now (); if ((psi_frequency_changed) || ((psi_frequency_msec > 0) && ((next_psi_periodic - now) <= 0))) { changed_pat = TRUE; l = progs; while (--l >= 0) { prog[l]->changed = TRUE; } psi_frequency_changed = FALSE; next_psi_periodic = now + psi_frequency_msec; } if (changed_pat) { psi_pid = TS_PID_PAT; conticnt = &pat_conticnt; psi_data[0] = 0; psi_size = make_patsection (pat_section,&psi_data[1]) + 1; if (pat_section >= last_patsection) { changed_pat = FALSE; nextpat_version = (nextpat_version+1) & 0x1F; pat_section = 0; } else { pat_section += 1; } psi_done = 0; pid = psi_pid; scramble = 0; k = psi_size; } else { l = s->u.d.progs; while (--l >= 0) { if (s->u.d.pdescr[l]->changed) { f = s->u.d.pdescr[l]->streams; while ((--f >= 0) && (!s->u.d.pdescr[l]->stream[f]->u.d.mention)) { } if (f >= 0) { psi_pid = s->u.d.pdescr[l]->pmt_pid; conticnt = &s->u.d.pdescr[l]->pmt_conticnt; psi_data[0] = 0; psi_size = make_pmtsection (s,s->u.d.pdescr[l],&psi_data[1]) + 1; psi_done = 0; pid = psi_pid; scramble = 0; k = psi_size; break; } } } if (l < 0) { s->data.ptr[c->index+PES_STREAM_ID] = s->stream_id; conticnt = &s->conticnt; pid = s->u.d.pid; scramble = c->scramble; k = c->length; } } } else { pid = s->u.d.pid; scramble = c->scramble; k = c->length; } } d = output_pushdata (TS_PACKET_SIZE,c->msecpush + s->u.d.delta); if (d == NULL) { return (s); } adapt_ext_len = 1; adapt_flags2 = 0; adapt_flags1 = 0; f = TS_PACKET_SIZE - TS_PACKET_HEADSIZE; if ((psi_size <= 0) && (s->u.d.discontinuity)) { /* o, not for contents, but PCR-disco ? */ s->u.d.discontinuity = FALSE; adapt_flags1 |= TS_ADAPT_DISCONTI; } if (0) { adapt_flags1 |= TS_ADAPT_RANDOMAC; } if (0) { adapt_flags1 |= TS_ADAPT_PRIORITY; } if ((psi_size <= 0) && (s->u.d.has_clockref) && ((c->pcr.valid) || (s->u.d.next_clockref - (c->msecpush + s->u.d.delta) <= 0))) { adapt_flags1 |= TS_ADAPT_PCRFLAG; f -= 6; } if ((psi_size <= 0) && (c->opcr.valid)) { adapt_flags1 |= TS_ADAPT_OPCRFLAG; f -= 6; } if (0) { adapt_flags1 |= TS_ADAPT_SPLICING; f -= 1; } if (0) { adapt_flags1 |= TS_ADAPT_TPRIVATE; privdata = 0; f -= (privdata + 1); } if (0) { adapt_flags2 |= TS_ADAPT_LTWFLAG; adapt_ext_len += 2; } if (0) { adapt_flags2 |= TS_ADAPT_PIECEWRF; adapt_ext_len += 3; } if (0) { adapt_flags2 |= TS_ADAPT_SEAMLESS; adapt_ext_len += 5; } if (adapt_flags2 != 0) { adapt_flags1 |= TS_ADAPT_EXTENSIO; f -= adapt_ext_len; } if (adapt_flags1 != 0) { f -= 2; } if (k < f) { l = k; if (adapt_flags1 == 0) { f -= 1; if (f > l) { f -= 1; } } adapt_field_ctrl = TS_AFC_BOTH; } else { l = f; if (f == 0) { adapt_field_ctrl = TS_AFC_ADAPT; } else if (adapt_flags1 == 0) { adapt_field_ctrl = TS_AFC_PAYLD; } else { adapt_field_ctrl = TS_AFC_BOTH; } } *d++ = TS_SYNC_BYTE; warn (LSEC,"Splice unitstart",ETSC,0,1,unit_start); warn (LSEC,"Splice PID",ETSC,0,2,pid); *d++ = (0 << 7) /* transport_error_indicator */ | unit_start | (0 << 5) /* transport_priority */ | (pid >> 8); *d++ = pid; *d++ = (scramble << 6) | adapt_field_ctrl | *conticnt; warn (LSEC,"Splice continuity cnt",ETSC,0,3,*conticnt); if (adapt_field_ctrl & TS_AFC_PAYLD) { *conticnt = (*conticnt+1) & 0x0F; } if (adapt_field_ctrl & TS_AFC_ADAPT) { if ((*d++ = (TS_PACKET_SIZE - TS_PACKET_FLAGS1) - l) != 0) { *d++ = adapt_flags1; if (adapt_flags1 & TS_ADAPT_PCRFLAG) { clockref pcr; /* msec2clockref (c->msecpush + s->u.d.delta,pcr); */ msec2cref (&s->u.d.conv,c->msecpush + s->u.d.delta,&pcr); *d++ = (pcr.base >> 25) | (pcr.ba33 << 7); *d++ = pcr.base >> 17; *d++ = pcr.base >> 9; *d++ = pcr.base >> 1; *d++ = (pcr.base << 7) | (pcr.ext >> 8) | 0x7E; *d++ = pcr.ext; s->u.d.next_clockref = (c->msecpush + s->u.d.delta) + MAX_MSEC_PCRDIST; c->pcr.valid = FALSE; } if (adapt_flags1 & TS_ADAPT_OPCRFLAG) { *d++ = (c->opcr.base >> 25) | (c->opcr.ba33 << 7); *d++ = c->opcr.base >> 17; *d++ = c->opcr.base >> 9; *d++ = c->opcr.base >> 1; *d++ = (c->opcr.base << 7) | (c->opcr.ext >> 8) | 0x7E; *d++ = c->opcr.ext; c->opcr.valid = FALSE; } if (adapt_flags1 & TS_ADAPT_SPLICING) { } if (adapt_flags1 & TS_ADAPT_TPRIVATE) { } if (adapt_flags1 & TS_ADAPT_EXTENSIO) { *d++ = adapt_ext_len; *d++ = adapt_flags2 | 0x1F; if (adapt_flags2 & TS_ADAPT_LTWFLAG) { } if (adapt_flags2 & TS_ADAPT_PIECEWRF) { } if (adapt_flags2 & TS_ADAPT_SEAMLESS) { } } } if (f > l) { warn (LSEC,"Splice padding",ETSC,0,4,f-l); memset (d,-1,f-l); d += (f-l); } } if (l > 0) { if (psi_size > 0) { memcpy (d,&psi_data[psi_done],l); if (l < psi_size) { warn (LSEC,"Splice PSI Data",ETSC,0,s->stream_id,l); psi_done += l; psi_size -= l; unit_start = 0; } else { warn (LINF,"Splice PSI Done",ETSC,0,s->stream_id,l); psi_done = psi_size = 0; unit_start = TS_UNIT_START; } } else { memcpy (d,&s->data.ptr[c->index],l); if (l < c->length) { warn (LSEC,"Splice Data",ETSC,0,s->stream_id,l); c->length -= l; s->data.out = (c->index += l); unit_start = 0; } else { warn (LINF,"Splice Done",ETSC,0,s->stream_id,l); list_incr (s->ctrl.out,s->ctrl,1); if (list_empty (s->ctrl)) { s->data.out = s->data.in; } else { s->data.out = s->ctrl.ptr[s->ctrl.out].index; } s = NULL; unit_start = TS_UNIT_START; } } } return (s); }