--- /dev/null
+/*****************************************************************************
+ * mpeg4_iod.c: ISO 14496-1 IOD and parsers
+ *****************************************************************************
+ * Copyright (C) 2004-2015 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+
+#include "mpeg4_iod.h"
+
+//#define IOD_DEBUG 1
+static void iod_debug( vlc_object_t *p_object, const char *format, ... )
+{
+#ifdef IOD_DEBUG
+ va_list ap;
+ va_start(ap, format);
+ msg_GenericVa( p_object, VLC_MSG_DBG, format, ap );
+ va_end(ap);
+#else
+ VLC_UNUSED(format);
+ VLC_UNUSED(p_object);
+#endif
+}
+
+/*****************************************************************************
+ * MP4 specific functions (IOD parser)
+ *****************************************************************************/
+static unsigned IODDescriptorLength( unsigned *pi_data, const uint8_t **pp_data )
+{
+ unsigned int i_b;
+ unsigned int i_len = 0;
+
+ if(*pi_data == 0)
+ return 0;
+
+ do
+ {
+ i_b = **pp_data;
+ (*pp_data)++;
+ (*pi_data)--;
+ i_len = ( i_len << 7 ) + ( i_b&0x7f );
+
+ } while( i_b&0x80 && *pi_data > 0 );
+
+ if (i_len > *pi_data)
+ i_len = *pi_data;
+
+ return i_len;
+}
+
+static unsigned IODGetBytes( unsigned *pi_data, const uint8_t **pp_data, size_t bytes )
+{
+ unsigned res = 0;
+ while( *pi_data > 0 && bytes-- )
+ {
+ res <<= 8;
+ res |= **pp_data;
+ (*pp_data)++;
+ (*pi_data)--;
+ }
+
+ return res;
+}
+
+static char* IODGetURL( unsigned *pi_data, const uint8_t **pp_data )
+{
+ unsigned len = IODGetBytes( pi_data, pp_data, 1 );
+ if (len > *pi_data)
+ len = *pi_data;
+ char *url = strndup( (char*)*pp_data, len );
+ *pp_data += len;
+ *pi_data -= len;
+ return url;
+}
+
+#define IODTag_ObjectDescr 0x01
+#define IODTag_InitialObjectDescr 0x02
+#define IODTag_ESDescr 0x03
+#define IODTag_DecConfigDescr 0x04
+#define IODTag_DecSpecificDescr 0x05
+#define IODTag_SLDescr 0x06
+
+/* Unified pointer for read helper */
+typedef union
+{
+ iod_descriptor_t *p_iod;
+ es_mpeg4_descriptor_t *es_descr;
+ decoder_config_descriptor_t *p_dec_config;
+} iod_read_params_t;
+
+static uint8_t IOD_Desc_Read( vlc_object_t *, unsigned *, const uint8_t **, uint8_t, uint8_t, iod_read_params_t params );
+
+static bool IOD_SLDesc_Read( vlc_object_t *p_object, unsigned i_data, const uint8_t *p_data,
+ iod_read_params_t params )
+{
+ VLC_UNUSED(p_object);
+ VLC_UNUSED(i_data);
+ VLC_UNUSED(p_data);
+ VLC_UNUSED(params);
+ return true;
+}
+
+static bool IOD_DecSpecificDesc_Read( vlc_object_t *p_object, unsigned i_data, const uint8_t *p_data,
+ iod_read_params_t params )
+{
+ VLC_UNUSED(p_object);
+ decoder_config_descriptor_t *p_dec_config = params.p_dec_config;
+
+ p_dec_config->p_extra = malloc( i_data );
+ if( p_dec_config->p_extra )
+ {
+ p_dec_config->i_extra = i_data;
+ memcpy( p_dec_config->p_extra, p_data, p_dec_config->i_extra );
+ }
+
+ return !!p_dec_config->i_extra;
+}
+
+static bool IOD_DecConfigDesc_Read( vlc_object_t *p_object, unsigned i_data, const uint8_t *p_data,
+ iod_read_params_t params )
+{
+ decoder_config_descriptor_t *p_dec_config = params.p_dec_config;
+
+ if( i_data < 13 )
+ return false;
+
+ p_dec_config->i_objectTypeIndication = IODGetBytes( &i_data, &p_data, 1 );
+ uint8_t i_flags = IODGetBytes( &i_data, &p_data, 1 );
+ p_dec_config->i_streamType = i_flags >> 2;
+
+ IODGetBytes( &i_data, &p_data, 3 ); /* bufferSizeDB */
+ IODGetBytes( &i_data, &p_data, 4 ); /* maxBitrate */
+ IODGetBytes( &i_data, &p_data, 4 ); /* avgBitrate */
+
+ /* DecoderSpecificDescr */
+ IOD_Desc_Read( p_object, &i_data, &p_data,
+ IODTag_DecSpecificDescr, 1, params );
+
+ iod_debug( p_object, " * read decoder objecttype: %x streamtype:%x extra: %u",
+ p_dec_config->i_objectTypeIndication, p_dec_config->i_streamType, p_dec_config->i_extra );
+ /* ProfileLevelIndicator [0..255] */
+ return true;
+}
+
+static bool IOD_ESDesc_Read( vlc_object_t *p_object, unsigned i_data, const uint8_t *p_data,
+ iod_read_params_t params )
+{
+ es_mpeg4_descriptor_t *es_descr = params.es_descr;
+
+ if ( i_data < 3 )
+ return false;
+ es_descr->i_es_id = IODGetBytes( &i_data, &p_data, 2 );
+ uint8_t i_flags = IODGetBytes( &i_data, &p_data, 1 );
+
+ if( ( i_flags >> 7 )&0x01 )
+ {
+ if ( i_data < 2 )
+ return false;
+ IODGetBytes( &i_data, &p_data, 2 ); /* dependOn_es_id */
+ }
+
+ if( (i_flags >> 6) & 0x01 )
+ es_descr->psz_url = IODGetURL( &i_data, &p_data );
+
+ if( ( i_flags >> 5 )&0x01 )
+ {
+ if ( i_data < 2 )
+ return false;
+ IODGetBytes( &i_data, &p_data, 2 ); /* OCR_es_id */
+ }
+
+ iod_debug( p_object, " * read ES Descriptor for es id %"PRIx16, es_descr->i_es_id );
+
+ /* DecoderConfigDescr */
+ params.p_dec_config = &es_descr->dec_descr;
+ if ( 1 != IOD_Desc_Read( p_object, &i_data, &p_data,
+ IODTag_DecConfigDescr, 1, params ) )
+ return false;
+
+ /* SLDescr */
+ IOD_Desc_Read( p_object, &i_data, &p_data, IODTag_SLDescr, 1, params );
+
+ /* IPI / IP / IPMP ... */
+
+ es_descr->b_ok = true;
+
+ return true;
+}
+
+static bool IOD_InitialObjectDesc_Read( vlc_object_t *p_object, unsigned i_data,
+ const uint8_t *p_data, iod_read_params_t params )
+{
+ iod_descriptor_t *p_iod = params.p_iod;
+ if( i_data < 3 + 5 + 2 )
+ return false;
+
+ uint16_t i_object_descriptor_id = ( IODGetBytes( &i_data, &p_data, 1 ) << 2 );
+ uint8_t i_flags = IODGetBytes( &i_data, &p_data, 1 );
+ i_object_descriptor_id |= i_flags >> 6;
+
+ iod_debug( p_object, " * ObjectDescriptorID: %"PRIu16, i_object_descriptor_id );
+ iod_debug( p_object, " * includeInlineProfileLevel flag: 0x%"PRIx8, ( i_flags >> 4 )&0x01 );
+ if ( (i_flags >> 5) & 0x01 )
+ {
+ p_iod->psz_url = IODGetURL( &i_data, &p_data );
+ iod_debug( p_object, " * URL: %s", p_iod->psz_url );
+ return true; /* leaves out unparsed remaining extdescr */
+ }
+
+ if( i_data < 5 + 2 ) /* at least one ES desc */
+ return false;
+
+ /* Profile Level Indication */
+ IODGetBytes( &i_data, &p_data, 1 ); /* OD */
+ IODGetBytes( &i_data, &p_data, 1 ); /* scene */
+ IODGetBytes( &i_data, &p_data, 1 ); /* audio */
+ IODGetBytes( &i_data, &p_data, 1 ); /* visual */
+ IODGetBytes( &i_data, &p_data, 1 ); /* graphics */
+
+ /* Now read */
+ /* 1..255 ESdescr */
+ uint8_t i_desc_count = IOD_Desc_Read( p_object, &i_data, &p_data,
+ IODTag_ESDescr, ES_DESCRIPTOR_COUNT, params );
+ if( i_desc_count == 0 )
+ {
+ iod_debug( p_object, " * missing ES Descriptor" );
+ return false;
+ }
+
+ /* 0..255 OCIdescr */
+ /* 0..255 IPMPdescpointer */
+ /* 0..255 IPMPdesc */
+ /* 0..1 IPMPtoollistdesc */
+ /* 0..255 Extensiondescr */
+
+ return true;
+}
+
+static uint8_t IOD_Desc_Read( vlc_object_t *p_object, unsigned *pi_data, const uint8_t **pp_data,
+ uint8_t i_target_tag, uint8_t i_max_desc, iod_read_params_t params )
+{
+ uint8_t i_read_count = 0;
+
+ for (unsigned i = 0; *pi_data > 2 && i < i_max_desc; i++)
+ {
+ const uint8_t i_tag = IODGetBytes( pi_data, pp_data, 1 );
+ const unsigned i_length = IODDescriptorLength( pi_data, pp_data );
+ if( i_target_tag != i_tag || i_length > *pi_data )
+ break;
+
+ unsigned i_descriptor_data = i_length;
+ const uint8_t *p_descriptor_data = *pp_data;
+
+ iod_debug( p_object, " Reading descriptor 0x%"PRIx8": found tag 0x%"PRIx8" left %d",
+ i_target_tag, i_tag, *pi_data );
+ switch( i_tag )
+ {
+ case IODTag_InitialObjectDescr:
+ {
+ /* iod_descriptor_t *p_iod = (iod_descriptor_t *) param; */
+ if ( !IOD_InitialObjectDesc_Read( p_object, i_descriptor_data,
+ p_descriptor_data, params ) )
+ {};
+ break;
+ }
+
+ case IODTag_ESDescr: /**/
+ {
+ iod_descriptor_t *p_iod = params.p_iod;
+ params.es_descr = &p_iod->es_descr[i_read_count];
+ if ( !IOD_ESDesc_Read( p_object, i_descriptor_data,
+ p_descriptor_data, params ) )
+ {};
+ break;
+ }
+
+ case IODTag_DecConfigDescr:
+ {
+ if ( !IOD_DecConfigDesc_Read( p_object, i_descriptor_data,
+ p_descriptor_data, params ) )
+ {};
+ break;
+ }
+
+ case IODTag_DecSpecificDescr:
+ {
+ if ( !IOD_DecSpecificDesc_Read( p_object, i_descriptor_data,
+ p_descriptor_data, params ) )
+ {};
+ break;
+ }
+
+ case IODTag_SLDescr:
+ {
+ if ( !IOD_SLDesc_Read( p_object, i_descriptor_data,
+ p_descriptor_data, params ) )
+ {};
+ break;
+ }
+
+ default:
+ iod_debug( p_object, "trying to read unsupported descriptor" );
+ break;
+ }
+
+ *pp_data += i_length;
+ *pi_data -= i_length;
+
+ i_read_count++;
+ }
+
+ return i_read_count;
+}
+
+iod_descriptor_t *IODNew( vlc_object_t *p_object, unsigned i_data, const uint8_t *p_data )
+{
+ if( i_data < 4 )
+ return NULL;
+
+ uint8_t i_iod_scope = IODGetBytes( &i_data, &p_data, 1 ); /* scope */
+ uint8_t i_iod_label = IODGetBytes( &i_data, &p_data, 1 );
+ if( i_iod_label == 0x02 ) /* old vlc's buggy implementation of the IOD_descriptor */
+ {
+ i_iod_label = i_iod_scope;
+ i_iod_scope = 0x10; /* Add the missing front iod scope byte */
+ i_data++; p_data--; /* next byte must be tag */
+ }
+
+ iod_debug( p_object, " * iod label:0x%"PRIx8" scope:0x%"PRIx8,
+ i_iod_label, i_iod_scope );
+
+ if( i_iod_scope != 0x10 && i_iod_scope != 0x11 ) /* Uniqueness in program or transport */
+ {
+ iod_debug( p_object, " * can't handle reserved scope 0x%"PRIx8, i_iod_scope );
+ return NULL;
+ }
+
+ /* Initial Object Descriptor must follow */
+ iod_descriptor_t *p_iod = calloc( 1, sizeof( iod_descriptor_t ) );
+ if( !p_iod )
+ return NULL;
+
+ /* IOD_InitialObjectDescrTag Parsing */
+ iod_read_params_t params;
+ params.p_iod = p_iod;
+ if ( 1 != IOD_Desc_Read( p_object, &i_data, &p_data,
+ IODTag_InitialObjectDescr, 1, params ) )
+ {
+ iod_debug( p_object, " cannot read InitialObjectDescr" );
+ free( p_iod );
+ return NULL;
+ }
+
+ return p_iod;
+}
+
+void IODFree( iod_descriptor_t *p_iod )
+{
+ if( p_iod->psz_url )
+ {
+ free( p_iod->psz_url );
+ free( p_iod );
+ return;
+ }
+
+ for( int i = 0; i < 255; i++ )
+ {
+#define es_descr p_iod->es_descr[i]
+ if( es_descr.b_ok )
+ {
+ if( es_descr.psz_url )
+ free( es_descr.psz_url );
+ else
+ free( es_descr.dec_descr.p_extra );
+ }
+#undef es_descr
+ }
+ free( p_iod );
+}
#include "opus.h"
-#undef TS_DEBUG
-VLC_FORMAT(1, 2) static void ts_debug(const char *format, ...)
-{
-#ifdef TS_DEBUG
- va_list ap;
- va_start(ap, format);
- vfprintf(stderr, format, ap);
- va_end(ap);
-#else
- (void)format;
-#endif
-}
+#include "mpeg4_iod.h"
#ifdef HAVE_ARIBB24
#include <aribb24/aribb24.h>
typedef struct ts_pid_t ts_pid_t;
-typedef struct
-{
- uint8_t i_objectTypeIndication;
- uint8_t i_streamType;
-
- unsigned i_extra;
- uint8_t *p_extra;
-
-} decoder_config_descriptor_t;
-
-typedef struct
-{
- bool b_ok;
- uint16_t i_es_id;
-
- char *psz_url;
-
- decoder_config_descriptor_t dec_descr;
-
-} es_mpeg4_descriptor_t;
-
-#define ES_DESCRIPTOR_COUNT 255
-typedef struct
-{
- /* IOD */
- char *psz_url;
-
- es_mpeg4_descriptor_t es_descr[ES_DESCRIPTOR_COUNT];
-
-} iod_descriptor_t;
-
typedef struct
{
int i_version;
static void PCRFixHandle( demux_t *, ts_pmt_t *, block_t * );
static int64_t TimeStampWrapAround( ts_pmt_t *, int64_t );
-static void IODFree( iod_descriptor_t * );
-
#define TS_USER_PMT_NUMBER (0)
static int UserPmt( demux_t *p_demux, const char * );
fmt->b_packetized = false;
}
-/*****************************************************************************
- * MP4 specific functions (IOD parser)
- *****************************************************************************/
-static unsigned IODDescriptorLength( unsigned *pi_data, uint8_t **pp_data )
-{
- unsigned int i_b;
- unsigned int i_len = 0;
- do
- {
- i_b = **pp_data;
- (*pp_data)++;
- (*pi_data)--;
- i_len = ( i_len << 7 ) + ( i_b&0x7f );
-
- } while( i_b&0x80 && *pi_data > 0 );
-
- if (i_len > *pi_data)
- i_len = *pi_data;
-
- return i_len;
-}
-
-static unsigned IODGetBytes( unsigned *pi_data, uint8_t **pp_data, size_t bytes )
-{
- unsigned res = 0;
- while( *pi_data > 0 && bytes-- )
- {
- res <<= 8;
- res |= **pp_data;
- (*pp_data)++;
- (*pi_data)--;
- }
-
- return res;
-}
-
-static char* IODGetURL( unsigned *pi_data, uint8_t **pp_data )
-{
- unsigned len = IODGetBytes( pi_data, pp_data, 1 );
- if (len > *pi_data)
- len = *pi_data;
- char *url = strndup( (char*)*pp_data, len );
- *pp_data += len;
- *pi_data -= len;
- return url;
-}
-
-static iod_descriptor_t *IODNew( unsigned i_data, uint8_t *p_data )
-{
- uint8_t i_iod_tag, i_iod_label, byte1, byte2, byte3;
-
- iod_descriptor_t *p_iod = calloc( 1, sizeof( iod_descriptor_t ) );
- if( !p_iod )
- return NULL;
-
- if( i_data < 3 )
- {
- return p_iod;
- }
-
- byte1 = IODGetBytes( &i_data, &p_data, 1 );
- byte2 = IODGetBytes( &i_data, &p_data, 1 );
- byte3 = IODGetBytes( &i_data, &p_data, 1 );
- if( byte2 == 0x02 ) //old vlc's buggy implementation of the IOD_descriptor
- {
- i_iod_label = byte1;
- i_iod_tag = byte2;
- }
- else //correct implementation of the IOD_descriptor
- {
- i_iod_label = byte2;
- i_iod_tag = byte3;
- }
-
- ts_debug( "\n* iod label:%d tag:0x%x", i_iod_label, i_iod_tag );
-
- if( i_iod_tag != 0x02 )
- {
- ts_debug( "\n ERR: tag %02x != 0x02", i_iod_tag );
- return p_iod;
- }
-
- IODDescriptorLength( &i_data, &p_data );
-
- uint16_t i_od_id = ( IODGetBytes( &i_data, &p_data, 1 ) << 2 );
- uint8_t i_flags = IODGetBytes( &i_data, &p_data, 1 );
- i_od_id |= i_flags >> 6;
- ts_debug( "\n* od_id:%d", i_od_id );
- ts_debug( "\n* includeInlineProfileLevel flag:%d", ( i_flags >> 4 )&0x01 );
- if ((i_flags >> 5) & 0x01)
- {
- p_iod->psz_url = IODGetURL( &i_data, &p_data );
- ts_debug( "\n* url string:%s", p_iod->psz_url );
- ts_debug( "\n*****************************\n" );
- return p_iod;
- }
- else
- {
- p_iod->psz_url = NULL;
- }
-
- /* Profile Level Indication */
- IODGetBytes( &i_data, &p_data, 1 ); /* OD */
- IODGetBytes( &i_data, &p_data, 1 ); /* scene */
- IODGetBytes( &i_data, &p_data, 1 ); /* audio */
- IODGetBytes( &i_data, &p_data, 1 ); /* visual */
- IODGetBytes( &i_data, &p_data, 1 ); /* graphics */
-
- unsigned i_length = 0;
- unsigned i_data_sav = i_data;
- uint8_t *p_data_sav = p_data;
- for (unsigned i = 0; i_data > 0 && i < ES_DESCRIPTOR_COUNT; i++)
- {
- es_mpeg4_descriptor_t *es_descr = &p_iod->es_descr[i];
-
- p_data = p_data_sav + i_length;
- i_data = i_data_sav - i_length;
-
- uint8_t i_tag = IODGetBytes( &i_data, &p_data, 1 );
- i_length = IODDescriptorLength( &i_data, &p_data );
-
- i_data_sav = i_data;
- p_data_sav = p_data;
-
- i_data = i_length;
-
- if ( i_tag != 0x03 )
- {
- ts_debug( "\n* - OD tag:0x%x Unsupported", i_tag );
- continue;
- }
-
- es_descr->i_es_id = IODGetBytes( &i_data, &p_data, 2 );
- uint8_t i_flags = IODGetBytes( &i_data, &p_data, 1 );
- bool b_streamDependenceFlag = ( i_flags >> 7 )&0x01;
- if( b_streamDependenceFlag )
- IODGetBytes( &i_data, &p_data, 2 ); /* dependOn_es_id */
-
- if( (i_flags >> 6) & 0x01 )
- es_descr->psz_url = IODGetURL( &i_data, &p_data );
-
- bool b_OCRStreamFlag = ( i_flags >> 5 )&0x01;
- if( b_OCRStreamFlag )
- IODGetBytes( &i_data, &p_data, 2 ); /* OCR_es_id */
-
- if( IODGetBytes( &i_data, &p_data, 1 ) != 0x04 )
- {
- ts_debug( "\n* ERR missing DecoderConfigDescr" );
- continue;
- }
- unsigned i_config_desc_length = IODDescriptorLength( &i_data, &p_data ); /* DecoderConfigDescr_length */
- decoder_config_descriptor_t *dec_descr = &es_descr->dec_descr;
- dec_descr->i_objectTypeIndication = IODGetBytes( &i_data, &p_data, 1 );
- i_flags = IODGetBytes( &i_data, &p_data, 1 );
- dec_descr->i_streamType = i_flags >> 2;
-
- IODGetBytes( &i_data, &p_data, 3); /* bufferSizeDB */
- IODGetBytes( &i_data, &p_data, 4); /* maxBitrate */
- IODGetBytes( &i_data, &p_data, 4 ); /* avgBitrate */
-
- if( i_config_desc_length > 13 && IODGetBytes( &i_data, &p_data, 1 ) == 0x05 )
- {
- dec_descr->i_extra = IODDescriptorLength( &i_data, &p_data );
- if( dec_descr->i_extra > 0 )
- {
- dec_descr->p_extra = xmalloc( dec_descr->i_extra );
- memcpy(dec_descr->p_extra, p_data, dec_descr->i_extra);
- p_data += dec_descr->i_extra;
- i_data -= dec_descr->i_extra;
- }
- }
- else
- {
- dec_descr->i_extra = 0;
- dec_descr->p_extra = NULL;
- }
-
- if( IODGetBytes( &i_data, &p_data, 1 ) != 0x06 )
- {
- ts_debug( "\n* ERR missing SLConfigDescr" );
- continue;
- }
- IODDescriptorLength( &i_data, &p_data ); /* SLConfigDescr_length */
- switch( IODGetBytes( &i_data, &p_data, 1 ) /* predefined */ )
- {
- default:
- ts_debug( "\n* ERR unsupported SLConfigDescr predefined" );
- case 0x01:
- // FIXME
- break;
- }
- es_descr->b_ok = true;
- }
-
- return p_iod;
-}
-
-static void IODFree( iod_descriptor_t *p_iod )
-{
- if( p_iod->psz_url )
- {
- free( p_iod->psz_url );
- free( p_iod );
- return;
- }
-
- for( int i = 0; i < 255; i++ )
- {
-#define es_descr p_iod->es_descr[i]
- if( es_descr.b_ok )
- {
- if( es_descr.psz_url )
- free( es_descr.psz_url );
- else
- free( es_descr.dec_descr.p_extra );
- }
-#undef es_descr
- }
- free( p_iod );
-}
-
/****************************************************************************
****************************************************************************
** libdvbpsi callbacks
{
case 0x1d: /* We have found an IOD descriptor */
msg_Dbg( p_demux, " * PMT descriptor : IOD (0x1d)" );
- p_pmt->iod = IODNew( p_dr->i_length, p_dr->p_data );
+ p_pmt->iod = IODNew( VLC_OBJECT(p_demux), p_dr->i_length, p_dr->p_data );
break;
case 0x9: