/***************************************************************************** * ts_sl.c : MPEG SL/FMC handling for TS demuxer ***************************************************************************** * Copyright (C) 2014-2016 - VideoLAN Authors * * 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_streams.h" #include "ts_pid.h" #include "ts_streams_private.h" #include "ts.h" #include "ts_sl.h" const es_mpeg4_descriptor_t * GetMPEG4DescByEsId( const ts_pmt_t *pmt, uint16_t i_es_id ) { for( int i = 0; i < ES_DESCRIPTOR_COUNT; i++ ) { const es_mpeg4_descriptor_t *es_descr = &pmt->iod->es_descr[i]; if( es_descr->i_es_id == i_es_id && es_descr->b_ok ) return es_descr; } for( int i=0; iod.objects.i_size; i++ ) { const od_descriptor_t *od = pmt->od.objects.p_elems[i]; for( int j = 0; j < ES_DESCRIPTOR_COUNT; j++ ) { const es_mpeg4_descriptor_t *es_descr = &od->es_descr[j]; if( es_descr->i_es_id == i_es_id && es_descr->b_ok ) return es_descr; } } return NULL; } static ts_es_t * GetPMTESBySLEsId( ts_pmt_t *pmt, uint16_t i_sl_es_id ) { for( int i=0; i< pmt->e_streams.i_size; i++ ) { ts_es_t *p_es = pmt->e_streams.p_elems[i]->u.p_stream->p_es; if( p_es->i_sl_es_id == i_sl_es_id ) return p_es; } return NULL; } bool SetupISO14496LogicalStream( demux_t *p_demux, const decoder_config_descriptor_t *dcd, es_format_t *p_fmt ) { msg_Dbg( p_demux, " - IOD objecttype: %"PRIx8" streamtype:%"PRIx8, dcd->i_objectTypeIndication, dcd->i_streamType ); if( dcd->i_streamType == 0x04 ) /* VisualStream */ { switch( dcd->i_objectTypeIndication ) { case 0x0B: /* mpeg4 sub */ es_format_Change( p_fmt, SPU_ES, VLC_CODEC_SUBT ); break; case 0x20: /* mpeg4 */ es_format_Change( p_fmt, VIDEO_ES, VLC_CODEC_MP4V ); break; case 0x21: /* h264 */ es_format_Change( p_fmt, VIDEO_ES, VLC_CODEC_H264 ); break; case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: /* mpeg2 */ case 0x6a: /* mpeg1 */ es_format_Change( p_fmt, VIDEO_ES, VLC_CODEC_MPGV ); break; case 0x6c: /* mpeg1 */ es_format_Change( p_fmt, VIDEO_ES, VLC_CODEC_JPEG ); break; default: break; } } else if( dcd->i_streamType == 0x05 ) /* AudioStream */ { switch( dcd->i_objectTypeIndication ) { case 0x40: /* mpeg4 */ case 0x66: case 0x67: case 0x68: /* mpeg2 aac */ es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_MP4A ); break; case 0x69: /* mpeg2 */ case 0x6b: /* mpeg1 */ es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_MPGA ); break; default: break; } } if( p_fmt->i_cat != UNKNOWN_ES ) { p_fmt->i_extra = __MIN(dcd->i_extra, INT32_MAX); if( p_fmt->i_extra > 0 ) { p_fmt->p_extra = malloc( p_fmt->i_extra ); if( p_fmt->p_extra ) memcpy( p_fmt->p_extra, dcd->p_extra, p_fmt->i_extra ); else p_fmt->i_extra = 0; } } return true; } /* Object stream SL in table sections */ void SLPackets_Section_Handler( demux_t *p_demux, const uint8_t *p_sectiondata, size_t i_sectiondata, const uint8_t *p_payloaddata, size_t i_payloaddata, void *p_pes_cbdata ) { VLC_UNUSED(p_sectiondata); VLC_UNUSED(i_sectiondata); demux_sys_t *p_sys = p_demux->p_sys; ts_stream_t *p_pes = (ts_stream_t *) p_pes_cbdata; ts_pmt_t *p_pmt = p_pes->p_es->p_program; const es_mpeg4_descriptor_t *p_mpeg4desc = GetMPEG4DescByEsId( p_pmt, p_pes->p_es->i_sl_es_id ); if( p_mpeg4desc && p_mpeg4desc->dec_descr.i_objectTypeIndication == 0x01 && p_mpeg4desc->dec_descr.i_streamType == 0x01 /* Object */ ) { const uint8_t *p_data = p_payloaddata; size_t i_data = i_payloaddata; od_descriptors_t *p_ods = &p_pmt->od; sl_header_data header = DecodeSLHeader( i_data, p_data, &p_mpeg4desc->sl_descr ); DecodeODCommand( VLC_OBJECT(p_demux), p_ods, i_data - header.i_size, &p_data[header.i_size] ); bool b_changed = false; for( int i=0; iobjects.i_size; i++ ) { od_descriptor_t *p_od = p_ods->objects.p_elems[i]; for( int j = 0; j < ES_DESCRIPTOR_COUNT && p_od->es_descr[j].b_ok; j++ ) { p_mpeg4desc = &p_od->es_descr[j]; ts_es_t *p_es = GetPMTESBySLEsId( p_pmt, p_mpeg4desc->i_es_id ); es_format_t fmt; es_format_Init( &fmt, UNKNOWN_ES, 0 ); if ( p_mpeg4desc && p_mpeg4desc->b_ok && p_es && SetupISO14496LogicalStream( p_demux, &p_mpeg4desc->dec_descr, &fmt ) && !es_format_IsSimilar( &fmt, &p_es->fmt ) ) { fmt.i_id = p_es->fmt.i_id; fmt.i_group = p_es->fmt.i_group; es_format_Clean( &p_es->fmt ); p_es->fmt = fmt; if( p_es->id ) { es_out_Del( p_demux->out, p_es->id ); p_sys->i_pmt_es--; } p_es->fmt.b_packetized = true; /* Split by access unit, no sync code */ p_es->id = es_out_Add( p_demux->out, &p_es->fmt ); if( p_es->id ) p_sys->i_pmt_es++; b_changed = true; } else es_format_Clean( &fmt ); } } if( b_changed ) UpdatePESFilters( p_demux, p_demux->p_sys->seltype == PROGRAM_ALL ); } } typedef struct { block_t *p_au; block_t **pp_au_last; ts_stream_t *p_stream; } SL_stream_processor_context_t; static void SL_stream_processor_Delete( ts_stream_processor_t *h ) { SL_stream_processor_context_t *ctx = (SL_stream_processor_context_t *) h->priv; block_ChainRelease( ctx->p_au ); free( ctx ); free( h ); } static void SL_stream_processor_Reset( ts_stream_processor_t *h ) { SL_stream_processor_context_t *ctx = (SL_stream_processor_context_t *) h->priv; block_ChainRelease( ctx->p_au ); ctx->p_au = NULL; ctx->pp_au_last = &ctx->p_au; } static block_t * SL_stream_processor_Push( ts_stream_processor_t *h, uint8_t i_stream_id, block_t *p_block ) { SL_stream_processor_context_t *ctx = (SL_stream_processor_context_t *) h->priv; ts_es_t *p_es = ctx->p_stream->p_es; ts_pmt_t *p_pmt = p_es->p_program; if( ((i_stream_id & 0xFE) != 0xFA) /* 0xFA || 0xFB */ ) { block_Release( p_block ); return NULL; } const es_mpeg4_descriptor_t *p_desc = GetMPEG4DescByEsId( p_pmt, p_es->i_sl_es_id ); if(!p_desc) { block_Release( p_block ); p_block = NULL; } else { sl_header_data header = DecodeSLHeader( p_block->i_buffer, p_block->p_buffer, &p_desc->sl_descr ); p_block->i_buffer -= header.i_size; p_block->p_buffer += header.i_size; p_block->i_dts = header.i_dts ? header.i_dts : p_block->i_dts; p_block->i_pts = header.i_pts ? header.i_pts : p_block->i_pts; /* Assemble access units */ if( header.b_au_start && ctx->p_au ) { block_ChainRelease( ctx->p_au ); ctx->p_au = NULL; ctx->pp_au_last = &ctx->p_au; } block_ChainLastAppend( &ctx->pp_au_last, p_block ); p_block = NULL; if( header.b_au_end && ctx->p_au ) { p_block = block_ChainGather( ctx->p_au ); ctx->p_au = NULL; ctx->pp_au_last = &ctx->p_au; } } return p_block; } ts_stream_processor_t *SL_stream_processor_New( ts_stream_t *p_stream ) { ts_stream_processor_t *h = malloc(sizeof(*h)); if(!h) return NULL; SL_stream_processor_context_t *ctx = malloc( sizeof(SL_stream_processor_context_t) ); if(!ctx) { free(h); return NULL; } ctx->p_au = NULL; ctx->pp_au_last = &ctx->p_au; ctx->p_stream = p_stream; h->priv = ctx; h->pf_delete = SL_stream_processor_Delete; h->pf_push = SL_stream_processor_Push; h->pf_reset = SL_stream_processor_Reset; return h; }