]> git.sesse.net Git - vlc/blobdiff - plugins/mp4/mp4.c
* ALL: new module API. Makes a few things a lot simpler, and we gain
[vlc] / plugins / mp4 / mp4.c
index 1f378e17a9bde64e9fbc0db012bacfcf5c21631d..25ee6699814ecedc318a0196d695d903c31b628e 100644 (file)
@@ -2,7 +2,7 @@
  * mp4.c : MP4 file input module for vlc
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: mp4.c,v 1.1 2002/07/17 21:37:27 fenrir Exp $
+ * $Id: mp4.c,v 1.8 2002/07/31 20:56:52 sam Exp $
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  * 
  * This program is free software; you can redistribute it and/or modify
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static void input_getfunctions( function_list_t * );
-static int  MP4Demux         ( input_thread_t * );
-static int  MP4Init          ( input_thread_t * );
-static void MP4End           ( input_thread_t * );
+static int    MP4Init    ( vlc_object_t * );
+static void __MP4End     ( vlc_object_t * );
+static int    MP4Demux   ( input_thread_t * );
 
-/*****************************************************************************
- * Build configuration tree.
- *****************************************************************************/
-MODULE_CONFIG_START
-MODULE_CONFIG_STOP
-
-MODULE_INIT_START
-    SET_DESCRIPTION( "MP4 file input" )
-    ADD_CAPABILITY( DEMUX, 242 )
-MODULE_INIT_STOP
-
-MODULE_ACTIVATE_START
-    input_getfunctions( &p_module->p_functions->demux );
-MODULE_ACTIVATE_STOP
-
-MODULE_DEACTIVATE_START
-MODULE_DEACTIVATE_STOP
+#define MP4End(a) __MP4End(VLC_OBJECT(a))
 
 /*****************************************************************************
- * Functions exported as capabilities. They are declared as static so that
- * we don't pollute the namespace too much.
+ * Module descriptor
  *****************************************************************************/
-static void input_getfunctions( function_list_t * p_function_list )
-{
-#define input p_function_list->functions.demux
-    input.pf_init             = MP4Init;
-    input.pf_end              = MP4End;
-    input.pf_demux            = MP4Demux;
-    input.pf_rewind           = NULL;
-#undef input
-}
+vlc_module_begin();
+    set_description( "MP4 demuxer" );
+    set_capability( "demux", 242 );
+    set_callbacks( MP4Init, __MP4End );
+vlc_module_end();
 
 /*****************************************************************************
  * Declaration of local function 
@@ -89,7 +67,7 @@ static int  MP4_ReadSample();
 static int  MP4_DecodeSample();
 
 #define MP4_Set4BytesLE( p, dw ) \
-    *((u8*)p) = ( dw&0xff ); \
+    *((u8*)p)   = ( (dw)&0xff ); \
     *((u8*)p+1) = ( ((dw)>> 8)&0xff ); \
     *((u8*)p+2) = ( ((dw)>>16)&0xff ); \
     *((u8*)p+3) = ( ((dw)>>24)&0xff )
@@ -102,8 +80,9 @@ static int  MP4_DecodeSample();
 /*****************************************************************************
  * MP4Init: check file and initializes MP4 structures
  *****************************************************************************/
-static int MP4Init( input_thread_t *p_input )
-{
+static int MP4Init( vlc_object_t * p_this )
+{   
+    input_thread_t *p_input = (input_thread_t *)p_this;
     u8  *p_peek;
     u32 i_type;
     
@@ -131,14 +110,16 @@ static int MP4Init( input_thread_t *p_input )
         p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE ;
     }
 
+    p_input->pf_demux = MP4Demux;
+
     /* a little test to see if it could be a mp4 */
     if( input_Peek( p_input, &p_peek, 8 ) < 8 )
     {
         msg_Warn( p_input, "MP4 plugin discarded (cannot peek)" );
         return( -1 );
     }
-    i_type = ( p_peek[4] << 24 ) + ( p_peek[5] << 16 ) +
-                ( p_peek[6] << 8 ) + ( p_peek[7] );
+    i_type = ( p_peek[4] ) + ( p_peek[5] << 8 ) +
+                ( p_peek[6] << 16 ) + ( p_peek[7] << 24);
     switch( i_type )
     {
         case( FOURCC_ftyp ):
@@ -186,7 +167,11 @@ static int MP4Init( input_thread_t *p_input )
                 break;
             default:
                 msg_Info( p_input,
-                          "Unrecognize major file specification." );
+                          "Unrecognize major file specification (%c%c%c%c).",
+                           p_ftyp->data.p_ftyp->i_major_brand&0xff,
+                           ( p_ftyp->data.p_ftyp->i_major_brand >>  8)&0xff,
+                           ( p_ftyp->data.p_ftyp->i_major_brand >> 16 )&0xff,
+                           ( p_ftyp->data.p_ftyp->i_major_brand >> 24 )&0xff );
                 break;
         }
     }
@@ -385,11 +370,12 @@ static int MP4Demux( input_thread_t *p_input )
 /*****************************************************************************
  * MP4End: frees unused data
  *****************************************************************************/
-static void MP4End( input_thread_t *p_input )
+static void __MP4End ( vlc_object_t * p_this )
 {   
 #define FREE( p ) \
     if( p ) { free( p ); } 
     int i_track;
+    input_thread_t *  p_input = (input_thread_t *)p_this;
     demux_data_mp4_t *p_demux = p_input->p_demux_data;
     
     msg_Dbg( p_input, "Freeing all memory" );
@@ -406,11 +392,7 @@ static void MP4End( input_thread_t *p_input )
                FREE(p_demux->track[i_track].chunk[i_chunk].p_sample_delta_dts );
             }
         }
-        if( p_demux->track->p_data_init )
-        {
-            input_DeletePacket( p_input->p_method_data, 
-                                p_demux->track->p_data_init );
-        }
+
         if( !p_demux->track[i_track].i_sample_size )
         {
             FREE( p_demux->track[i_track].p_sample_size );
@@ -751,11 +733,12 @@ static void MP4_StartDecoder( input_thread_t *p_input,
     MP4_Box_t *p_sample;
     int i;
     int i_chunk;
-    u8  *p_bmih;
-    int i_codec;
-    char *psz_name;
+
+    int i_decoder_specific_info_len;
+    u8  *p_decoder_specific_info;
     
+    u8  *p_init;
     MP4_Box_t *p_esds;
 
     
@@ -772,20 +755,23 @@ static void MP4_StartDecoder( input_thread_t *p_input,
 
     if( !p_demux_track->chunk[i_chunk].i_sample_description_index )
     {
-        msg_Warn( p_input, "invalid SampleEntry index for this track i_cat %d" );
+        msg_Warn( p_input, 
+                  "invalid SampleEntry index (track ID 0x%x)",
+                  p_demux_track->i_track_ID );
         return;
     } 
     
     p_sample = MP4_FindNbBox( p_demux_track->p_stsd,
-                              p_demux_track->chunk[i_chunk].i_sample_description_index - 1);
+                 p_demux_track->chunk[i_chunk].i_sample_description_index - 1);
 
-    if( !p_sample )
+    if( ( !p_sample )||( !p_sample->data.p_data ) )
     {
-        msg_Warn( p_input, "cannot find SampleEntry for this track" );
+        msg_Warn( p_input, 
+                  "cannot find SampleEntry (track ID 0x%x)",
+                  p_demux_track->i_track_ID );
         return;
     }
 
-    
     vlc_mutex_lock( &p_input->stream.stream_lock );
     p_demux_track->p_es = input_AddES( p_input,
                                        p_input->stream.p_selected_program, 
@@ -801,97 +787,169 @@ static void MP4_StartDecoder( input_thread_t *p_input,
     
     p_demux_track->p_es->i_stream_id = p_demux_track->i_track_ID;
 
-    p_demux_track->p_es->i_type = UNKNOWN_ES;
-    p_demux_track->p_es->i_cat = p_demux_track->i_cat;
-
-    /* search for the codec */
-    if( !MP4_GetCodec( p_sample->i_type, &i_codec, &psz_name ) )
+    /* It's a little ugly but .. there are special cases */
+    switch( p_sample->i_type )
     {
-        msg_Warn( p_input, "%s (%c%c%c%c) unsupported", 
-                  psz_name,
-                  (p_sample->i_type >> 24)&0xff,
-                  (p_sample->i_type >> 16)&0xff,
-                  (p_sample->i_type >> 8)&0xff,
-                  (p_sample->i_type )&0xff);
-        p_demux_track->b_ok = 0;
-        return;
+        case( VLC_FOURCC( '.', 'm', 'p', '3' ) ):
+        case( VLC_FOURCC( 'm', 's', 0x00, 0x55 ) ):
+            p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'm', 'p', 'g', 'a' );
+            break;
+        default:
+            p_demux_track->p_es->i_fourcc = p_sample->i_type;
+            break;
     }
-    else
+    
+    p_demux_track->p_es->i_cat = p_demux_track->i_cat;
+    
+    i_decoder_specific_info_len = 0;
+    p_decoder_specific_info = NULL;
+
+    /* now see if esds is present and if so create a data packet 
+        with decoder_specific_info  */
+#define p_decconfig p_esds->data.p_esds->es_descriptor.p_decConfigDescr
+    if( ( p_esds = MP4_FindBox( p_sample, FOURCC_esds ) )&&
+        ( p_esds->data.p_esds )&&
+        ( p_decconfig ) )
     {
-        p_demux_track->p_es->i_type = i_codec;
-        msg_Info( p_input, "%s supported", psz_name );
+        /* First update information based on i_objectTypeIndication */
+        switch( p_decconfig->i_objectTypeIndication )
+        {
+            case( 0x20 ): /* MPEG4 VIDEO */
+                p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'm','p','4','v' );
+                break;
+            case( 0x40):
+                p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'm','p','4','a' );
+                break;
+            case( 0x60):
+            case( 0x61):
+            case( 0x62):
+            case( 0x63):
+            case( 0x64):
+            case( 0x65): /* MPEG2 video */
+                p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'm','p','g','v' );
+                break;
+            /* Theses are MPEG2-AAC (what is this codec ?) */
+            case( 0x66): /* main profile */
+            case( 0x67): /* Low complexity profile */
+            case( 0x68): /* Scaleable Sampling rate profile */
+                p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'm','p','4','a' );
+                break;
+            /* true MPEG 2 audio */
+            case( 0x69): 
+                p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'm','p','g','a' );
+                break;
+            case( 0x6a): /* MPEG1 video */
+                p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'm','p','g','v' );
+                break;
+            case( 0x6b): /* MPEG1 audio */
+                p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'm','p','g','a' );
+                break;
+            case( 0x6c ): /* jpeg */
+                p_demux_track->p_es->i_fourcc = VLC_FOURCC( 'j','p','e','g' );
+                break;
+            default:
+                /* Unknown entry, but don't touch i_fourcc */
+                msg_Warn( p_input, 
+                          "objectTypeIndication(0x%x) unknow (Track ID 0x%x)",
+                          p_decconfig->i_objectTypeIndication,
+                          p_demux_track->i_track_ID );
+                break;
+        }
+        i_decoder_specific_info_len = 
+                p_decconfig->i_decoder_specific_info_len;
+        p_decoder_specific_info = 
+                p_decconfig->p_decoder_specific_info;
     }
 
+#undef p_decconfig
+
+    /* some last initialisation */
+    /* XXX I create a bitmapinfoheader_t or 
+       waveformatex_t for each stream, up to now it's the best thing 
+       I've found but it could exist a better solution :) as something 
+       like adding some new fields in p_es ...
+
+       XXX I don't set all values, only thoses that are interesting or known
+        --> bitmapinfoheader_t : width and height 
+        --> waveformatex_t : channels, samplerate, bitspersample
+        and at the end I add p_decoder_specific_info 
+        
+        TODO set more values
+     
+     */
+
     switch( p_demux_track->i_cat )
     {
         case( VIDEO_ES ):    
-            p_demux_track->p_es->b_audio = 0;
-
-            /* now create a bitmapinfoheader_t for decoder */
-            p_bmih = malloc( 40 );
-            memset( p_bmih, 0, 40);
-            MP4_Set4BytesLE( p_bmih, 40 );
-            if( p_sample->data.p_sample_mp4v->i_width )
+            /* now create a bitmapinfoheader_t for decoder and 
+               add information found in p_esds */
+            p_init = malloc( 40 + i_decoder_specific_info_len);
+            memset( p_init, 0, 40 + i_decoder_specific_info_len);
+            MP4_Set4BytesLE( p_init, 40 + i_decoder_specific_info_len );
+            if( p_sample->data.p_sample_vide->i_width )
             {
-                MP4_Set4BytesLE( p_bmih + 4, p_sample->data.p_sample_mp4v->i_width );
+                MP4_Set4BytesLE( p_init + 4, 
+                                 p_sample->data.p_sample_vide->i_width );
             }
             else
             {
                 /* use display size */
-                MP4_Set4BytesLE( p_bmih + 4, p_demux_track->i_width );
+                MP4_Set4BytesLE( p_init + 4, p_demux_track->i_width );
             }
-            if( p_sample->data.p_sample_mp4v->i_height )
+            if( p_sample->data.p_sample_vide->i_height )
             {
-                MP4_Set4BytesLE( p_bmih + 8, p_sample->data.p_sample_mp4v->i_height );
+                MP4_Set4BytesLE( p_init + 8, 
+                                 p_sample->data.p_sample_vide->i_height );
             }
             else
             {
-                MP4_Set4BytesLE( p_bmih + 8, p_demux_track->i_height );
+                MP4_Set4BytesLE( p_init + 8, p_demux_track->i_height );
+            }
+            if( i_decoder_specific_info_len )
+            {
+                memcpy( p_init + 40, 
+                        p_decoder_specific_info,
+                        i_decoder_specific_info_len);
             }
-
-            p_demux_track->p_es->p_demux_data = p_bmih;
             break;
+
         case( AUDIO_ES ):
-            p_demux_track->p_es->b_audio = 1;
+            p_init = malloc( 18 + i_decoder_specific_info_len);
+            memset( p_init, 0, 18 + i_decoder_specific_info_len);
+            MP4_Set2BytesLE( p_init + 2, /* i_channel */
+                             p_sample->data.p_sample_soun->i_channelcount );
+            MP4_Set4BytesLE( p_init + 4, /* samplepersec */
+                             p_sample->data.p_sample_soun->i_sampleratehi );
+            MP4_Set4BytesLE( p_init + 8, /* avgbytespersec */
+                             p_sample->data.p_sample_soun->i_channelcount *
+                                p_sample->data.p_sample_soun->i_sampleratehi *
+                             (p_sample->data.p_sample_soun->i_samplesize/8) );
+            MP4_Set2BytesLE( p_init + 14, /* bits/sample */
+                             p_sample->data.p_sample_soun->i_samplesize );
+
+            MP4_Set2BytesLE( p_init + 16, /* i_size, specific info len*/
+                             i_decoder_specific_info_len );
+            if( i_decoder_specific_info_len )
+            {
+                memcpy( p_init + 18, 
+                        p_decoder_specific_info,
+                        i_decoder_specific_info_len);
+            }
             break;
+
         default:
+            p_init = NULL;
             break;
     }
 
+    p_demux_track->p_es->p_demux_data = p_init;
     vlc_mutex_lock( &p_input->stream.stream_lock );
     input_SelectES( p_input, p_demux_track->p_es );
     vlc_mutex_unlock( &p_input->stream.stream_lock );
-    
 
     p_demux_track->b_ok = 1;
-
-    /* now see if esds is present and i so create a data packet 
-        with decoder_specific_info  */
-    if( ( p_esds = MP4_FindBox( p_sample, FOURCC_esds ) )&&
-        ( p_esds->data.p_esds->es_descriptor.p_decConfigDescr )&&
-        ( p_esds->data.p_esds->es_descriptor.p_decConfigDescr->i_decoder_specific_info_len ) )
-    {
-        data_packet_t *p_data;
-        int i_size = p_esds->data.p_esds->es_descriptor.p_decConfigDescr->i_decoder_specific_info_len;
-
-        /* data packet for the data */
-        if( !(p_data = input_NewPacket( p_input->p_method_data, i_size ) ) )
-        {
-            return;
-        }
-
-        /* initialisation of all the field */
-        memcpy( p_data->p_payload_start,
-                p_esds->data.p_esds->es_descriptor.p_decConfigDescr->p_decoder_specific_info,
-                i_size ); 
-        p_demux_track->p_data_init = p_data;
-    }
-                           
-    
-    return;
 }
 
-
 static void MP4_StopDecoder( input_thread_t *p_input,
                              track_data_mp4_t *p_demux_track )
 {
@@ -900,13 +958,6 @@ static void MP4_StopDecoder( input_thread_t *p_input,
 
     input_UnselectES( p_input, p_demux_track->p_es );
     p_demux_track->p_es = NULL;
-    if( p_demux_track->p_data_init )
-    {
-        input_DeletePacket( p_input->p_method_data, 
-                            p_demux_track->p_data_init );
-        p_demux_track->p_data_init = NULL;
-    }
-    
 }
 
 static int  MP4_ReadSample( input_thread_t *p_input,
@@ -927,7 +978,8 @@ static int  MP4_ReadSample( input_thread_t *p_input,
     }
     /* caculate size and position for this sample */
     i_size = p_demux_track->i_sample_size ? 
-        p_demux_track->i_sample_size : p_demux_track->p_sample_size[p_demux_track->i_sample];
+                    p_demux_track->i_sample_size : 
+                    p_demux_track->p_sample_size[p_demux_track->i_sample];
     /* TODO */
     i_pos  = MP4_GetTrackPos( p_demux_track );
 
@@ -993,28 +1045,6 @@ static int  MP4_DecodeSample( input_thread_t *p_input,
                                          p_pes->i_pts * 9/100);
 
     
-    if( p_demux_track->p_data_init )
-    {
-        pes_packet_t *p_pes_init;
-        /* create a pes packet containing decoder initialisation 
-           with the one we will send to decoder */
-        if( !(p_pes_init = input_NewPES( p_input->p_method_data ) ) )
-        {
-            msg_Err( p_input, "out of memory" );
-            return( 0 );
-        }
-        
-        p_pes_init->p_first = 
-            p_pes_init->p_last = p_demux_track->p_data_init;
-
-        p_pes_init->i_pes_size = p_demux_track->p_data_init->p_payload_end - 
-                                   p_demux_track->p_data_init->p_payload_start;
-        p_pes_init->i_nb_data = 1;
-
-        input_DecodePES( p_demux_track->p_es->p_decoder_fifo, p_pes_init );
-        p_demux_track->p_data_init = NULL;
-    }
-
     input_DecodePES( p_demux_track->p_es->p_decoder_fifo, p_pes );
     
     /* now update sample position */
@@ -1039,8 +1069,10 @@ static int  MP4_DecodeSample( input_thread_t *p_input,
               != p_demux_track->chunk[p_demux_track->i_chunk].i_sample_description_index  )
         {
             /* FIXME */
-            msg_Err( p_input, "I need to change the decoder but not yet implemented" );
-            return( 0 );
+            msg_Warn( p_input, 
+                      "SampleEntry have changed, starting a new decoder" );
+            MP4_StopDecoder( p_input, p_demux_track );
+            MP4_StartDecoder( p_input, p_demux_track );
         }
     }