/***************************************************************************** * ts_pid.c: Transport Stream input module for VLC. ***************************************************************************** * Copyright (C) 2004-2016 VLC authors and VideoLAN * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "ts_pid.h" #include "ts_streams.h" #include "ts.h" #include #include #define PID_ALLOC_CHUNK 16 void ts_pid_list_Init( ts_pid_list_t *p_list ) { p_list->dummy.i_pid = 8191; p_list->dummy.i_flags = FLAG_SEEN; p_list->base_si.i_pid = 0x1FFB; p_list->pp_all = NULL; p_list->i_all = 0; p_list->i_all_alloc = 0; p_list->i_last_pid = 0; p_list->p_last = NULL; } void ts_pid_list_Release( demux_t *p_demux, ts_pid_list_t *p_list ) { for( int i = 0; i < p_list->i_all; i++ ) { ts_pid_t *pid = p_list->pp_all[i]; #ifndef NDEBUG if( pid->type != TYPE_FREE ) msg_Err( p_demux, "PID %d type %d not freed refcount %d", pid->i_pid, pid->type, pid->i_refcount ); #else VLC_UNUSED(p_demux); #endif free( pid ); } free( p_list->pp_all ); } struct searchkey { int16_t i_pid; ts_pid_t *const *pp_last; }; static int ts_bsearch_searchkey_Compare( const void *key, const void *other ) { struct searchkey *p_key = (void *)key; ts_pid_t *const *pp_pid = other; ts_pid_t *p_pid = *pp_pid; p_key->pp_last = other; return ( p_key->i_pid >= p_pid->i_pid ) ? p_key->i_pid - p_pid->i_pid : -1; } ts_pid_t * ts_pid_Get( ts_pid_list_t *p_list, uint16_t i_pid ) { switch( i_pid ) { case 0: return &p_list->pat; case 0x1FFB: return &p_list->base_si; case 0x1FFF: return &p_list->dummy; default: if( p_list->i_last_pid == i_pid ) return p_list->p_last; break; } size_t i_index = 0; ts_pid_t *p_pid = NULL; if( p_list->pp_all ) { struct searchkey pidkey; pidkey.i_pid = i_pid; pidkey.pp_last = NULL; ts_pid_t **pp_pidk = bsearch( &pidkey, p_list->pp_all, p_list->i_all, sizeof(ts_pid_t *), ts_bsearch_searchkey_Compare ); if ( pp_pidk ) p_pid = *pp_pidk; else i_index = (pidkey.pp_last - p_list->pp_all); /* Last visited index */ } if( p_pid == NULL ) { if( p_list->i_all >= p_list->i_all_alloc ) { ts_pid_t **p_realloc = realloc( p_list->pp_all, (p_list->i_all_alloc + PID_ALLOC_CHUNK) * sizeof(ts_pid_t *) ); if( !p_realloc ) { abort(); //return NULL; } p_list->pp_all = p_realloc; p_list->i_all_alloc += PID_ALLOC_CHUNK; } p_pid = calloc( 1, sizeof(*p_pid) ); if( !p_pid ) { abort(); //return NULL; } p_pid->i_cc = 0xff; p_pid->i_pid = i_pid; /* Do insertion based on last bsearch mid point */ if( p_list->i_all ) { if( p_list->pp_all[i_index]->i_pid < i_pid ) i_index++; memmove( &p_list->pp_all[i_index + 1], &p_list->pp_all[i_index], (p_list->i_all - i_index) * sizeof(ts_pid_t *) ); } p_list->pp_all[i_index] = p_pid; p_list->i_all++; } p_list->p_last = p_pid; p_list->i_last_pid = i_pid; return p_pid; } ts_pid_t * ts_pid_Next( ts_pid_list_t *p_list, ts_pid_next_context_t *p_ctx ) { if( likely(p_list->i_all && p_ctx) ) { if( p_ctx->i_pos < p_list->i_all ) return p_list->pp_all[p_ctx->i_pos++]; } return NULL; } static void PIDReset( ts_pid_t *pid ) { assert(pid->i_refcount == 0); pid->i_cc = 0xff; pid->i_flags &= ~FLAG_SCRAMBLED; pid->type = TYPE_FREE; memset(pid->prevpktbytes, 0, PREVPKTKEEPBYTES); } bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *p_parent ) { if( pid == p_parent || pid->i_pid == 0x1FFF ) return false; if( pid->i_refcount == 0 ) { assert( pid->type == TYPE_FREE ); switch( i_type ) { case TYPE_FREE: /* nonsense ?*/ PIDReset( pid ); return true; case TYPE_CAT: return true; case TYPE_PAT: PIDReset( pid ); pid->u.p_pat = ts_pat_New( p_demux ); if( !pid->u.p_pat ) return false; break; case TYPE_PMT: PIDReset( pid ); pid->u.p_pmt = ts_pmt_New( p_demux ); if( !pid->u.p_pmt ) return false; break; case TYPE_STREAM: PIDReset( pid ); pid->u.p_stream = ts_stream_New( p_demux, p_parent->u.p_pmt ); if( !pid->u.p_stream ) return false; break; case TYPE_SI: PIDReset( pid ); pid->u.p_si = ts_si_New( p_demux ); if( !pid->u.p_si ) return false; break; case TYPE_PSIP: PIDReset( pid ); pid->u.p_psip = ts_psip_New( p_demux ); if( !pid->u.p_psip ) return false; break; default: assert(false); break; } pid->i_refcount++; pid->type = i_type; } else if( pid->type == i_type && pid->i_refcount < UINT16_MAX ) { pid->i_refcount++; } else { if( pid->type != TYPE_FREE ) msg_Warn( p_demux, "Tried to redeclare pid %d with another type", pid->i_pid ); return false; } return true; } void PIDRelease( demux_t *p_demux, ts_pid_t *pid ) { if( pid->i_refcount == 0 ) { assert( pid->type == TYPE_FREE ); return; } else if( pid->i_refcount == 1 ) { pid->i_refcount--; } else if( pid->i_refcount > 1 ) { assert( pid->type != TYPE_FREE && pid->type != TYPE_PAT ); pid->i_refcount--; } if( pid->i_refcount == 0 ) { switch( pid->type ) { default: case TYPE_FREE: /* nonsense ?*/ assert( pid->type != TYPE_FREE ); break; case TYPE_CAT: break; case TYPE_PAT: ts_pat_Del( p_demux, pid->u.p_pat ); pid->u.p_pat = NULL; break; case TYPE_PMT: ts_pmt_Del( p_demux, pid->u.p_pmt ); pid->u.p_pmt = NULL; break; case TYPE_STREAM: ts_stream_Del( p_demux, pid->u.p_stream ); pid->u.p_stream = NULL; break; case TYPE_SI: ts_si_Del( p_demux, pid->u.p_si ); pid->u.p_si = NULL; break; case TYPE_PSIP: ts_psip_Del( p_demux, pid->u.p_psip ); pid->u.p_psip = NULL; break; } SetPIDFilter( p_demux->p_sys, pid, false ); PIDReset( pid ); } } int UpdateHWFilter( demux_sys_t *p_sys, ts_pid_t *p_pid ) { if( !p_sys->b_access_control ) return VLC_EGENERIC; return vlc_stream_Control( p_sys->stream, STREAM_SET_PRIVATE_ID_STATE, p_pid->i_pid, !!(p_pid->i_flags & FLAG_FILTERED) ); } int SetPIDFilter( demux_sys_t *p_sys, ts_pid_t *p_pid, bool b_selected ) { if( b_selected ) p_pid->i_flags |= FLAG_FILTERED; else p_pid->i_flags &= ~FLAG_FILTERED; return UpdateHWFilter( p_sys, p_pid ); }