+
+/* Program stream map handling */
+typedef struct ps_es_t
+{
+ int i_type;
+ int i_id;
+
+ int i_descriptor;
+ uint8_t *p_descriptor;
+
+ /* Language is iso639-2T */
+ uint8_t lang[3];
+
+} ps_es_t;
+
+struct ps_psm_t
+{
+ int i_version;
+
+ int i_es;
+ ps_es_t **es;
+};
+
+static inline int ps_id_to_type( const ps_psm_t *p_psm, int i_id )
+{
+ int i;
+ for( i = 0; p_psm && i < p_psm->i_es; i++ )
+ {
+ if( p_psm->es[i]->i_id == i_id ) return p_psm->es[i]->i_type;
+ }
+ return 0;
+}
+
+static inline const uint8_t *ps_id_to_lang( const ps_psm_t *p_psm, int i_id )
+{
+ int i;
+ for( i = 0; p_psm && i < p_psm->i_es; i++ )
+ {
+ if( p_psm->es[i]->i_id == i_id ) return p_psm->es[i]->lang;
+ }
+ return 0;
+}
+
+static inline void ps_psm_init( ps_psm_t *p_psm )
+{
+ p_psm->i_version = 0xFFFF;
+ p_psm->i_es = 0;
+ p_psm->es = 0;
+}
+
+static inline void ps_psm_destroy( ps_psm_t *p_psm )
+{
+ while( p_psm->i_es-- )
+ {
+ free( p_psm->es[p_psm->i_es]->p_descriptor );
+ free( p_psm->es[p_psm->i_es] );
+ }
+ free( p_psm->es );
+
+ p_psm->es = 0;
+ p_psm->i_es = 0;
+}
+
+static inline int ps_psm_fill( ps_psm_t *p_psm, block_t *p_pkt,
+ ps_track_t tk[PS_TK_COUNT], es_out_t *out )
+{
+ int i_buffer = p_pkt->i_buffer;
+ uint8_t *p_buffer = p_pkt->p_buffer;
+ int i_length, i_version, i_info_length, i_esm_length, i_es_base, i;
+
+ if( !p_psm || p_buffer[3] != 0xbc ) return VLC_EGENERIC;
+
+ i_length = (uint16_t)(p_buffer[4] << 8) + p_buffer[5] + 6;
+ if( i_length > i_buffer ) return VLC_EGENERIC;
+
+ //i_current_next_indicator = (p_buffer[6] && 0x01);
+ i_version = (p_buffer[6] && 0xf8);
+
+ if( p_psm->i_version == i_version ) return VLC_EGENERIC;
+
+ ps_psm_destroy( p_psm );
+
+ i_info_length = (uint16_t)(p_buffer[8] << 8) + p_buffer[9];
+ if( i_info_length + 10 > i_length ) return VLC_EGENERIC;
+
+ /* Elementary stream map */
+ i_esm_length = (uint16_t)(p_buffer[ 10 + i_info_length ] << 8) +
+ p_buffer[ 11 + i_info_length];
+ i_es_base = 12 + i_info_length;
+
+ while( i_es_base + 4 < i_length )
+ {
+ ps_es_t es;
+ es.lang[0] = es.lang[1] = es.lang[2] = 0;
+
+ es.i_type = p_buffer[ i_es_base ];
+ es.i_id = p_buffer[ i_es_base + 1 ];
+ i_info_length = (uint16_t)(p_buffer[ i_es_base + 2 ] << 8) +
+ p_buffer[ i_es_base + 3 ];
+
+ if( i_es_base + 4 + i_info_length > i_length ) break;
+
+ /* TODO Add support for VC-1 stream:
+ * stream_type=0xea, stream_id=0xfd AND registration
+ * descriptor 0x5 with format_identifier == 0x56432D31 (VC-1)
+ * (I need a sample that use PSM with VC-1) */
+
+ es.p_descriptor = 0;
+ es.i_descriptor = i_info_length;
+ if( i_info_length > 0 )
+ {
+ int i = 0;
+
+ es.p_descriptor = malloc( i_info_length );
+ memcpy( es.p_descriptor, p_buffer + i_es_base + 4, i_info_length);
+
+ while( i <= es.i_descriptor - 2 )
+ {
+ /* Look for the ISO639 language descriptor */
+ if( es.p_descriptor[i] != 0x0a )
+ {
+ i += es.p_descriptor[i+1] + 2;
+ continue;
+ }
+
+ if( i <= es.i_descriptor - 6 )
+ {
+ es.lang[0] = es.p_descriptor[i+2];
+ es.lang[1] = es.p_descriptor[i+3];
+ es.lang[2] = es.p_descriptor[i+4];
+ }
+ break;
+ }
+ }
+
+ p_psm->es = realloc( p_psm->es, sizeof(ps_es_t *) * (p_psm->i_es+1) );
+ p_psm->es[p_psm->i_es] = malloc( sizeof(ps_es_t) );
+ *p_psm->es[p_psm->i_es++] = es;
+ i_es_base += 4 + i_info_length;
+ }
+
+ /* TODO: CRC */
+
+ p_psm->i_version = i_version;
+
+ /* Check/Modify our existing tracks */
+ for( i = 0; i < PS_TK_COUNT; i++ )
+ {
+ ps_track_t tk_tmp;
+
+ if( !tk[i].b_seen || !tk[i].es ) continue;
+
+ if( ps_track_fill( &tk_tmp, p_psm, tk[i].i_id ) != VLC_SUCCESS )
+ continue;
+
+ if( tk_tmp.fmt.i_codec == tk[i].fmt.i_codec ) continue;
+
+ es_out_Del( out, tk[i].es );
+ tk[i] = tk_tmp;
+ tk[i].b_seen = true;
+ tk[i].es = es_out_Add( out, &tk[i].fmt );
+ }
+
+ return VLC_SUCCESS;
+}