/* * ISO 13818 stream multiplexer * Copyright (C) 2001 Convergence Integrated Media GmbH Berlin * Author: Oskar Schirmer (oskar@scara.com) */ /* * Module: Split PS * Purpose: Split a program stream. * * This module examines a program stream and copies the packets to * the input stream buffers. PSI data is extracted, descriptors are * copied to the mapstream (stream 0). */ #include "global.h" #include "error.h" #include "pes.h" #include "ps.h" #include "input.h" #include "splice.h" #include "descref.h" #include "splitpes.h" #include "splitps.h" static int ps_program_end_code (file_descr *f) { warn (LIMP,"Program End Code",EPST,3,0,0); /* close file, do ... */ return (PES_HDCODE_SIZE); } static int ps_pack_header (file_descr *f, int l) { int i, s; long x; byte a, b; clockref oldscr; /* used for correcting bad DVB scr values */ if (l < PS_PACKHD_SIZE) { warn (LDEB,"Pack header (incomplete)",EPST,2,1,l); return (0); } i = f->data.out; list_incr (i,f->data,PS_PACKHD_STUFLN); s = (f->data.ptr[i] & 0x07) + PS_PACKHD_SIZE; if (l < s) { warn (LDEB,"Pack header (incomplete)",EPST,2,2,l); return (0); } oldscr = f->u.ps.ph.scr; warn (LINF,"Pack header",EPST,2,0,s); i = f->data.out; list_incr (i,f->data,PS_PACKHD_SCR); a = f->data.ptr[i]; marker_check (a,0x44,0xC4); f->u.ps.ph.scr.ba33 = (a >> 5) & 1; x = ((a & 0x18) | ((a & 0x03) << 1)) << 7; list_incr (i,f->data,1); x = (x | f->data.ptr[i]) << 8; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,2); x = (x | ((a & 0xF8) | ((a & 0x03) << 1))) << 7; list_incr (i,f->data,1); x = (x | f->data.ptr[i]); list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,2); f->u.ps.ph.scr.base = (x << 5) | (a >> 3); warn (LSEC,"SCR base",EPST,2,3,f->u.ps.ph.scr.base); if (accept_weird_scr && ((oldscr.base - (90*40+1)) == f->u.ps.ph.scr.base)) { /* the DVB card produces weird scr-s, every second scr is less than previous one, indicating an odd value decrease of 40ms. weird! */ f->u.ps.ph.scr.base = oldscr.base + (90*40); } list_incr (i,f->data,1); b = f->data.ptr[i]; marker_bit (b,0); f->u.ps.ph.scr.ext = ((a & 0x03) << 7) | (b >> 1); warn (LSEC,"SCR ext",EPST,2,4,f->u.ps.ph.scr.ext); f->u.ps.ph.scr.valid = TRUE; cref2msec (&f->u.ps.stream[0]->u.m.conv, f->u.ps.ph.scr, &f->u.ps.stream[0]->u.m.msectime); warn (LDEB,"(map time)",EPST,2,5,f->u.ps.stream[0]->u.m.msectime); list_incr (i,f->data,1); x = f->data.ptr[i] << 8; list_incr (i,f->data,1); x = (x | f->data.ptr[i]) << 6; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_check (a,0x03,0x03); f->u.ps.ph.muxrate = x | (a >> 2); warn (LSEC,"muxrate",EPST,2,6,f->u.ps.ph.muxrate); return (s); } static boolean ps_system_header (file_descr *f, int size) { int i; byte a, sid; long x; boolean bbs; int bsb; warn (LINF,"System header",EPST,1,0,size); if (size < PS_SYSTHD_STREAM) { warn (LWAR,"System header too short",EPST,1,1,size); return (FALSE); } i = f->data.out; list_incr (i,f->data,PES_HEADER_SIZE); a = f->data.ptr[i]; marker_bit (a,7); x = (a & 0x7F) << 8; list_incr (i,f->data,1); x = (x | f->data.ptr[i]) << 7; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,0); f->u.ps.sh.ratebound = x | (a >> 1); warn (LSEC,"rate_bound",EPST,1,5,f->u.ps.sh.ratebound); list_incr (i,f->data,1); a = f->data.ptr[i]; f->u.ps.sh.csps_flag = a & 0x01; warn (LSEC,"csps_flag",EPST,1,6,f->u.ps.sh.csps_flag); f->u.ps.sh.fixed_flag = (a >>= 1) & 0x01; warn (LSEC,"fixed_flag",EPST,1,7,f->u.ps.sh.fixed_flag); f->u.ps.sh.audiobound = a >> 1; warn (LSEC,"audiobound",EPST,1,8,f->u.ps.sh.audiobound); list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,5); f->u.ps.sh.videobound = a & 0x1F; warn (LSEC,"videobound",EPST,1,9,f->u.ps.sh.videobound); f->u.ps.sh.system_video_lock_flag = (a >>= 6) & 0x01; warn (LSEC, "system_video_lock_flag",EPST,1,10,f->u.ps.sh.system_video_lock_flag); f->u.ps.sh.system_audio_lock_flag = a >> 1; warn (LSEC, "system_audio_lock_flag",EPST,1,11,f->u.ps.sh.system_audio_lock_flag); list_incr (i,f->data,1); f->u.ps.sh.packet_rate_restriction_flag = f->data.ptr[i] >> 7; warn (LSEC,"packet_rate_restriction_flag", EPST,1,12,f->u.ps.sh.packet_rate_restriction_flag); memset (f->u.ps.sh.buffer_bound,0,sizeof(f->u.ps.sh.buffer_bound)); size -= PS_SYSTHD_STREAM; while ((size -= PS_SYSTHD_STRLEN) >= 0) { list_incr (i,f->data,1); a = f->data.ptr[i]; if (a & 0x80) { sid = a; warn (LSEC,"stream_id",EPST,1,13,sid); list_incr (i,f->data,1); a = f->data.ptr[i]; if (marker_check (a,0xC0,0xC0)) { warn (LWAR,"Missing 11",EPST,1,3,a); return (FALSE); } bbs = (a >> 5) & 0x01; warn (LSEC,"buffer bound scale",EPST,1,14,bbs); list_incr (i,f->data,1); bsb = ((a & 0x1F) << 8) | f->data.ptr[i]; warn (LSEC,"buffer size bound",EPST,1,15,bsb); /* register stream here, if auto */ if (a == PES_JOKER_AUDIO) { a = PES_CODE_AUDIO; x = PES_NUMB_AUDIO; } else if (a == PES_JOKER_VIDEO) { a = PES_CODE_VIDEO; x = PES_NUMB_VIDEO; } else if (a >= PES_LOWEST_SID) { x = 1; } else { x = 0; } if (bbs) { bsb = -bsb; } while (--x >= 0) { f->u.ps.sh.buffer_bound[a-PES_LOWEST_SID] = bsb; a += 1; } } else { warn (LWAR,"Next bit 0",EPST,1,2,a); return (FALSE); } } if (size != -PS_SYSTHD_STRLEN) { warn (LWAR,"System header length",EPST,1,4,size); return (FALSE); } return (TRUE); } static boolean ps_stream_map (file_descr *f, int size) { int i, psmv, psil, esil, esml; byte a, styp, esid; boolean cni; warn (LINF,"Stream Map",EPST,4,0,size); if ((size > (1018 + PES_HEADER_SIZE)) || (size < PS_STRMAP_SIZE)) { warn (LWAR,"Map size bad",EPST,4,12,size); return (FALSE); } i = f->data.out; list_incr (i,f->data,PES_HEADER_SIZE); a = f->data.ptr[i]; psmv = a & 0x1F; warn (LSEC,"Map Version",EPST,4,1,psmv); alloc_descriptor (f->u.ps.stream[0],0,0,psmv); cni = a >> 7; warn (LSEC,"Current Next",EPST,4,2,cni); list_incr (i,f->data,2); psil = f->data.ptr[i] << 8; list_incr (i,f->data,1); psil = psil | f->data.ptr[i]; list_incr (i,f->data,1); warn (LSEC,"PS Info Length",EPST,4,3,psil); size -= PS_STRMAP_SIZE; if (size < psil) { warn (LWAR,"Invalid Size",EPST,4,7,size); return (FALSE); } size -= psil; while (psil > 0) { i = put_descriptor (f,f->u.ps.stream[0],i,&psil); } if (psil < 0) { warn (LWAR,"PS Info Broken",EPST,4,4,psil); return (FALSE); } if (cni) { finish_descriptor (f->u.ps.stream[0]); } esml = f->data.ptr[i] << 8; list_incr (i,f->data,1); esml = esml | f->data.ptr[i]; list_incr (i,f->data,1); warn (LSEC,"ES Map Length",EPST,4,5,esml); if (size != esml) { warn (LWAR,"Invalid Size",EPST,4,8,size); return (FALSE); } while (esml > 0) { styp = f->data.ptr[i]; list_incr (i,f->data,1); esid = f->data.ptr[i]; list_incr (i,f->data,1); esil = f->data.ptr[i] << 8; list_incr (i,f->data,1); esil = esil | f->data.ptr[i]; list_incr (i,f->data,1); warn (LSEC,"Stream Type",EPST,4,9,styp); warn (LSEC,"E Stream Id",EPST,4,10,esid); warn (LSEC,"ES Info Length",EPST,4,11,esil); if (esid >= PES_LOWEST_SID) { if (f->automatic) { if (f->u.ps.stream[esid] == NULL) { f->u.ps.stream[esid] = connect_streamprog ( f,f->auto_programnb,esid,-esid,styp, NULL,f->u.ps.stream[0],FALSE); } } } esml -= (esil + 4); if (esml >= 0) { alloc_descriptor (f->u.ps.stream[0],esid,0,psmv); while (esil > 0) { i = put_descriptor (f,f->u.ps.stream[0],i,&esil); } if (esil < 0) { warn (LWAR,"ES Map Broken",EPST,4,13,esil); return (FALSE); } if (cni) { finish_descriptor (f->u.ps.stream[0]); } } } if (esml < 0) { warn (LWAR,"ES Map Broken",EPST,4,6,esml); return (FALSE); } return (TRUE); } /* Parse a 45 bit stream directory offset value in 3 parts a 15 bit. * Precondition: f!=NULL, result!=NULL. * Postcontition: *result = offset. * Return: Index increased by 6. */ static int ps_stream_dir_get45 (file_descr *f, int i, long long *result) { byte a; int b, n; long long r; n = 2; r = 0; do { list_incr (i,f->data,1); b = f->data.ptr[i]; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,0); b = (b << 7) | (a >> 1); r = (r << 15) | b; } while (--n >= 0); *result = r; return (i); } static boolean ps_stream_directory (file_descr *f, int size) { int i, n; long x; byte a; int numoau; long long prevdo, nextdo; warn (LINF,"Stream Dir",EPST,5,0,0); i = f->data.out; list_incr (i,f->data,PES_HEADER_SIZE); x = f->data.ptr[i]; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,0); numoau = (x << 7) | (a >> 1); warn (LSEC,"Num Acces Units",EPST,5,1,numoau); if (size != (PS_STRDIR_SIZE + numoau * PS_STRDIR_SIZEAU)) { warn (LWAR,"Invalid Size",EPST,5,2,size); return (FALSE); } i = ps_stream_dir_get45 (f,i,&prevdo); warn (LSEC,"Prev Dir Offset",EPST,5,3,((long)prevdo)); i = ps_stream_dir_get45 (f,i,&nextdo); warn (LSEC,"Next Dir Offset",EPST,5,4,((long)nextdo)); n = 0; while (n < numoau) { byte psid; boolean idi; int refo, btr, cpi; long long headpo; clockref pts; /* and process all this ... */ list_incr (i,f->data,1); psid = f->data.ptr[i]; warn (LSEC,"Packet Str Id",EPST,5,5,psid); i = ps_stream_dir_get45 (f,i,&headpo); if (headpo & (1LL << 44)) { headpo = (1LL << 44) - headpo; } warn (LSEC,"Head Pos Offset",EPST,5,6,((long)headpo)); list_incr (i,f->data,1); refo = f->data.ptr[i]; list_incr (i,f->data,1); refo = (refo << 8) | f->data.ptr[i]; warn (LSEC,"Reference Offset",EPST,5,7,refo); list_incr (i,f->data,1); a = f->data.ptr[i]; marker_check (a,0x81,0x81); pts.ba33 = (a >> 3) & 1; x = a & 0x06; list_incr (i,f->data,1); x = (x << 7) | f->data.ptr[i]; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,0); x = (x << 8) | (a & 0xFE); list_incr (i,f->data,1); x = (x << 7) | f->data.ptr[i]; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,0); pts.base = (x << 7) | (a >> 1); pts.ext = 0; warn (LSEC,"PTS base",EPST,5,8,pts.base); list_incr (i,f->data,1); btr = f->data.ptr[i]; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,0); btr = (btr << 8) | (a & 0xFE); list_incr (i,f->data,1); btr = (btr << 7) | f->data.ptr[i]; list_incr (i,f->data,1); a = f->data.ptr[i]; marker_bit (a,7); cpi = (a >> 4) & 0x03; idi = (a >> 6) & 0x01; n += 1; } return (TRUE); } static boolean ps_data_stream (file_descr *f, int size, byte sourceid) { stream_descr *s; ctrl_buffer *c; warn (LINF,"Data Stream",EPST,6,0,size); if ((f->u.ps.stream[sourceid] == NULL) && (f->automatic)) { f->u.ps.stream[sourceid] = connect_streamprog (f,f->auto_programnb,sourceid,-sourceid, guess_streamtype(sourceid),NULL,f->u.ps.stream[0],FALSE); } s = f->u.ps.stream[sourceid]; if (s != NULL) { if ((!list_full (s->ctrl)) && (list_free (s->data) >= 2*size-1)) { c = &s->ctrl.ptr[s->ctrl.in]; c->length = size; f->payload += size; f->total += size; c->index = pes_transfer (&f->data,&s->data,size); warn (LDEB,"Sequence",EPST,6,1,f->sequence); c->sequence = f->sequence++; c->scramble = 0; c->msecread = msec_now (); c->msecpush = f->u.ps.stream[0]->u.m.msectime; c->pcr.valid = FALSE; c->opcr.valid = FALSE; list_incr (s->ctrl.in,s->ctrl,1); return (TRUE); } return (FALSE); } f->total += size; list_incr (f->data.out,f->data,size); return (TRUE); } /* Split data from a PS stream. * Precondition: f!=NULL * Return: TRUE, if something was processed, FALSE if no data/space available */ boolean split_ps (file_descr *f) { int l, p; byte a; warn (LDEB,"Split PS",EPST,0,0,f); if (pes_skip_to_prefix (f)) { l = list_size (f->data); if (l >= PES_HDCODE_SIZE) { a = pes_stream_id (&f->data); if (a >= PS_CODE_SYST_HDR) { if (l >= PES_HEADER_SIZE) { p = pes_packet_length (&f->data); p += PES_HEADER_SIZE; if (l >= p) { switch (a) { case PS_CODE_SYST_HDR: if (!ps_system_header (f,p)) { p = PES_SYNC_SIZE; } break; case PES_CODE_STR_MAP: if (!ps_stream_map (f,p)) { p = PES_SYNC_SIZE; } break; case PES_CODE_PADDING: break; case PES_CODE_PRIVATE2: /* p = PES_SYNC_SIZE; */ break; case PES_CODE_ECM: /* p = PES_SYNC_SIZE; */ break; case PES_CODE_EMM: /* p = PES_SYNC_SIZE; */ break; case PES_CODE_DSMCC: /* p = PES_SYNC_SIZE; */ break; case PES_CODE_ITU222E: /* p = PES_SYNC_SIZE; */ break; case PES_CODE_STR_DIR: if (!ps_stream_directory (f,p)) { p = PES_SYNC_SIZE; } break; default: return (ps_data_stream (f,p,a)); break; } } else { p = 0; } } else { p = 0; } } else if (a == PS_CODE_END) { p = ps_program_end_code (f); } else if (a == PS_CODE_PACK_HDR) { p = ps_pack_header (f,l); } else { warn (LWAR,"Unknown Stream Id",EPST,0,1,a); p = PES_SYNC_SIZE; } if (p > 0) { f->total += p; list_incr (f->data.out,f->data,p); return (TRUE); } } } return (FALSE); }