]> git.sesse.net Git - vlc/blobdiff - plugins/mpeg_system/mpeg_audio.c
* ALL: new module API. Makes a few things a lot simpler, and we gain
[vlc] / plugins / mpeg_system / mpeg_audio.c
index 7943245fa79654acfcf4ebdfee3f339e5c8aa2c2..15c4dd2d88caccbbe1565308000347c5d4203a3d 100644 (file)
@@ -2,7 +2,8 @@
  * mpeg_audio.c : mpeg_audio Stream input module for vlc
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: mpeg_audio.c,v 1.2 2002/05/13 16:28:44 fenrir Exp $
+ * $Id: mpeg_audio.c,v 1.14 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
  * Preamble
  *****************************************************************************/
 #include <stdlib.h>                                      /* malloc(), free() */
+#include <string.h>
 
-#include <videolan/vlc.h>
+#include <vlc/vlc.h>
+#include <vlc/input.h>
 
 #include <sys/types.h>
 
-#include "stream_control.h"
-#include "input_ext-intf.h"
-#include "input_ext-dec.h"
-#include "input_ext-plugins.h"
-
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static void input_getfunctions( function_list_t * p_function_list );
-static int  MPEGAudioDemux         ( struct input_thread_s * );
-static int  MPEGAudioInit          ( struct input_thread_s * );
-static void MPEGAudioEnd           ( struct input_thread_s * );
+static int  Activate ( vlc_object_t * );
+static int  Demux ( input_thread_t * );
 
 /* TODO: support MPEG-2.5, not difficult */
 
 /*****************************************************************************
- * Build configuration tree.
+ * Module descriptor
  *****************************************************************************/
-MODULE_CONFIG_START
-MODULE_CONFIG_STOP
-
-MODULE_INIT_START
-    SET_DESCRIPTION( "MPEG I/II Audio stream demux" )
-    ADD_CAPABILITY( DEMUX, 110 )
-    ADD_SHORTCUT( "mpeg_audio" )
-MODULE_INIT_STOP
-
-MODULE_ACTIVATE_START
-    input_getfunctions( &p_module->p_functions->demux );
-MODULE_ACTIVATE_STOP
-
-MODULE_DEACTIVATE_START
-MODULE_DEACTIVATE_STOP
-
-/*****************************************************************************
- * Functions exported as capabilities. They are declared as static so that
- * we don't pollute the namespace too much.
- *****************************************************************************/
-static void input_getfunctions( function_list_t * p_function_list )
-{
-#define input p_function_list->functions.demux
-    input.pf_init             = MPEGAudioInit;
-    input.pf_end              = MPEGAudioEnd;
-    input.pf_demux            = MPEGAudioDemux;
-    input.pf_rewind           = NULL;
-#undef input
-}
+vlc_module_begin();
+    set_description( _("ISO 13818-3 MPEG I/II audio stream demux" ) );
+    set_capability( "demux", 100 );
+    set_callbacks( Activate, NULL );
+    add_shortcut( "mpegaudio" );
+vlc_module_end();
 
 /*****************************************************************************
  * Definitions of structures  and functions used by this plugins 
  *****************************************************************************/
 
+/* XXX set this to 0 to avoid problem with PS XXX */
+/* but with some file or web radio will failed to detect */
+/* it's you to choose */
+#define MPEGAUDIO_MAXTESTPOS    0
+
+#define MPEGAUDIO_MAXFRAMESIZE  1500 /* no exactly */
+
 typedef struct mpegaudio_format_s
 {
     u32 i_header;
@@ -154,12 +134,30 @@ static char* mpegaudio_mode[4] =
     "stereo", "joint stereo", "dual channel", "mono"
 };
 
-static __inline__ u32 __GetDWBE( byte_t *p_buff )
+static inline u32 __GetDWBE( byte_t *p_buff )
 {
     return( ( (*(p_buff)) << 24 ) + ( (*(p_buff+1)) << 16 ) +
                     ( (*(p_buff+2)) << 8 ) +  ( (*(p_buff+3)) ) );
 }
 
+static int __CheckPS( input_thread_t *p_input )
+{
+    byte_t *p_buff;
+    int i_size = input_Peek( p_input, &p_buff, 8196 );
+
+    while( i_size > 0 )
+    {
+        if( !(*p_buff) && !(*(p_buff + 1)) 
+                && (*(p_buff + 2) == 1 ) && (*(p_buff + 3) >= 0xB9 ) )
+        {
+            return( 1 );  /* it could be ps so ...*/
+        }
+        p_buff++;
+        i_size--;
+    }
+    return( 0 );
+}
+
 /*
 #define __GetDWBE( p_buff ) \
     ( ( (*(p_buff)) << 24 ) + ( (*(p_buff+1)) << 16 ) + \
@@ -241,6 +239,58 @@ static int MPEGAudio_DecodedFrameSize( mpegaudio_format_t *p_mpeg )
     return( 0 );
 }
 
+static int MPEGAudio_SkipID3Tag( input_thread_t *p_input )
+{
+    int count;
+    byte_t *p_peek;
+    byte_t version, revision;
+    int b_footer;
+    int i_size;
+
+    msg_Dbg( p_input, "Checking for ID3 tag" );
+    /* get 10 byte id3 header */    
+    if( ( count = input_Peek( p_input, &p_peek, 10 ) ) < 10 )
+    {
+        msg_Err( p_input, "cannot peek()" );
+        return( -1 );
+    }
+/*
+    msg_Info( p_input, "Three first bytes are: %d %d %d",
+              p_peek[0],
+              p_peek[1],
+              p_peek[2]  
+              );
+*/
+    if ( !( (p_peek[0] == 0x49) && (p_peek[1] == 0x44) && (p_peek[2] == 0x33)))
+    {
+        return( 0 );
+    }
+    
+    version = p_peek[3];  /* These may become usfull later, */
+    revision = p_peek[4]; /* but we ignore them for now */
+
+    b_footer = p_peek[5] & 0x10;
+    i_size = (p_peek[6] << 21) +
+             (p_peek[7] << 14) +
+             (p_peek[8] << 7) +
+             p_peek[9];  //Is this safe?
+    if ( b_footer )
+    {
+        i_size += 10;
+    }
+    i_size += 10;
+    msg_Dbg( p_input, "ID3 tag found, skiping %d bytes", i_size );
+    if ( input_Peek( p_input, &p_peek, i_size ) < i_size )
+    {
+        msg_Err( p_input, "cannot peek()" );
+        return( -1 );
+    }
+        
+    p_input->p_current_data += i_size; //seek passed end of ID3 tag
+
+    return (0);
+}
+
 /*****************************************************************************
  * MPEGAudio_FindFrame : Find a header that could be valid. 
  *****************************************************************************
@@ -250,28 +300,36 @@ static int MPEGAudio_DecodedFrameSize( mpegaudio_format_t *p_mpeg )
  *****************************************************************************/
 static int MPEGAudio_FindFrame( input_thread_t *p_input, 
                                  int *pi_pos, 
-                                 mpegaudio_format_t *p_mpeg )
+                                 mpegaudio_format_t *p_mpeg,
+                                 int i_posmax )
 {
     byte_t *p_buff;
     u32 i_header;
     int i_framesize;
 
     int i_pos = 0;
-    int i_size = input_Peek( p_input, &p_buff, 8192 );
+    int i_size = input_Peek( p_input, &p_buff, i_posmax+MPEGAUDIO_MAXFRAMESIZE);
 
-    while( i_pos + 4 <= i_size ) /* need at least 4 bytes */
+    while( i_pos <= __MIN( i_posmax, i_size - 4) )
     {
         i_header = __GetDWBE( p_buff );
         if( MPEGAudio_CheckHeader( i_header ) )
         {
             MPEGAudio_ParseHeader( i_header, p_mpeg );
             i_framesize = MPEGAudio_FrameSize( p_mpeg );
-            if( ( i_pos + i_framesize + 4 > i_size )
-                ||( MPEGAudio_CheckHeader( __GetDWBE( p_buff + i_framesize ) ) ) )
+            if(  i_pos + i_framesize + 4 > i_size )
             {
                 *pi_pos = i_pos;
                 return( 1 );
             }
+            else
+            {
+                if( MPEGAudio_CheckHeader( __GetDWBE( p_buff + i_framesize ) ) )
+                {
+                    *pi_pos = i_pos;
+                    return( 2 );
+                }
+            }
         }
         p_buff++;
         i_pos++;
@@ -296,13 +354,13 @@ static void MPEGAudio_ExtractXingHeader( input_thread_t *p_input,
     byte_t  *p_buff;
     
     p_xh->i_flags = 0;  /* nothing present */
-    if( !(MPEGAudio_FindFrame( p_input, &i_pos, &mpeg )) )
+    if( !(MPEGAudio_FindFrame( p_input, &i_pos, &mpeg, 2024 )) )
     {
         return; /* failed , can't */
     }
     p_xh->i_avgbitrate = mpeg.i_bitrate * 1000; /* default */
 
-    /* 1024 is very very enougth */
+    /* 1024 is enougth */
     if( ( i_size = input_Peek( p_input, &p_buff, 1024 + i_pos ) ) < 8 )
     {
         return;
@@ -341,7 +399,7 @@ static void MPEGAudio_ExtractXingHeader( input_thread_t *p_input,
     }
     if( p_xh->i_flags&TOC_FLAG ) 
     {
-        FAST_MEMCPY( p_xh->i_toc, p_buff, 100 );
+        p_input->p_vlc->pf_memcpy( p_xh->i_toc, p_buff, 100 );
         p_buff += 100;
     }
     if( p_xh->i_flags&VBR_SCALE_FLAG ) 
@@ -359,14 +417,20 @@ static void MPEGAudio_ExtractXingHeader( input_thread_t *p_input,
                                     
 
 /*****************************************************************************
- * MPEGaudioInit : initializes MPEGaudio structures
+ * Activate: initializes MPEGaudio structures
  *****************************************************************************/
-static int MPEGAudioInit( input_thread_t * p_input )
+static int Activate( vlc_object_t * p_this )
 {
-    demux_data_mpegaudio_t *p_mpegaudio;
+    input_thread_t * p_input = (input_thread_t *)p_this;
+    demux_data_mpegaudio_t * p_mpegaudio;
     mpegaudio_format_t mpeg;
     es_descriptor_t * p_es;
     int i_pos;
+    int b_forced;
+    input_info_category_t * p_category;
+
+    /* Set the demux function */
+    p_input->pf_demux = Demux;
 
     /* XXX: i don't know what it's supposed to do, copied from ESInit */
     /* Initialize access plug-in structures. */
@@ -375,27 +439,52 @@ static int MPEGAudioInit( input_thread_t * p_input )
     /* Improve speed. */
         p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
     }
+    if( ( *p_input->psz_demux )
+        &&( !strncmp( p_input->psz_demux, "mpegaudio", 10 ) ) )
+    {
+        b_forced = 1;
+    }
+    else
+    {
+        b_forced = 0;
+    }
+
+    if ( MPEGAudio_SkipID3Tag( p_input ) )
+    {
+        return -1;
+    }
     
-    if( !(MPEGAudio_FindFrame( p_input, &i_pos, &mpeg )) )
+    /* check if it can be a ps stream */
+    if( __CheckPS(  p_input ) && !b_forced )
     {
-        intf_ErrMsg( "input: MPEGAudio plug-in discarded (no MPEG header)" );
+        return( -1 );
+    }
+
+    /* must be sure that is mpeg audio stream */
+    if( MPEGAudio_FindFrame( p_input, 
+                             &i_pos, 
+                             &mpeg, 
+                             (b_forced ? 2 * MPEGAUDIO_MAXFRAMESIZE : 
+                                             MPEGAUDIO_MAXTESTPOS) ) 
+                    < (b_forced ? 1 : 2)  )
+    {
+        msg_Warn( p_input, "MPEGAudio module discarded (no frame found)" );
         return( -1 );
     }
     
     vlc_mutex_lock( &p_input->stream.stream_lock );
     if( input_InitStream( p_input, 0 ) == -1)
     {
-        intf_ErrMsg( "input error: cannot init stream" );
+        msg_Err( p_input, "cannot init stream" );
         return( -1 );
     }    
     if( input_AddProgram( p_input, 0, 0) == NULL )
     {
-        intf_ErrMsg( "input error: cannot add program" );
+        msg_Err( p_input, "cannot add program" );
         return( -1 );
     }
     p_input->stream.pp_programs[0]->b_is_ok = 0;
-    p_input->stream.p_selected_program = 
-            p_input->stream.p_new_program = p_input->stream.pp_programs[0];
+    p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
     
     /* create our ES */ 
     p_es = input_AddES( p_input, 
@@ -405,13 +494,13 @@ static int MPEGAudioInit( input_thread_t * p_input )
     if( !p_es )
     {
         vlc_mutex_unlock( &p_input->stream.stream_lock );
-        intf_ErrMsg( "input error: not enough memory." );
+        msg_Err( p_input, "out of memory" );
         return( -1 );
     }
     p_es->i_stream_id = 1;
-    p_es->i_type = !mpeg.i_layer ? MPEG1_AUDIO_ES : MPEG2_AUDIO_ES;
+    p_es->i_fourcc = !mpeg.i_layer ? VLC_FOURCC('m','p','g','a') /* layer 1 */
+                                   : VLC_FOURCC('m','p','g','a'); /* layer 2 */
     p_es->i_cat = AUDIO_ES;
-    p_es->b_audio = 1;
     input_SelectES( p_input, p_es );
 
     p_input->stream.p_selected_program->b_is_ok = 1;
@@ -423,7 +512,7 @@ static int MPEGAudioInit( input_thread_t * p_input )
 
     if( !p_mpegaudio )
     {
-        intf_ErrMsg( "input error: not enough memory." );
+        msg_Err( p_input, "out of memory" );
         return( -1 );
     }
 
@@ -451,34 +540,36 @@ static int MPEGAudioInit( input_thread_t * p_input )
     /* FIXME FIXME FIXME FIXME FIXME FIXME FIXME */
 
     /* all is ok :)) */
-    intf_Msg( "input init: Audio MPEG-%d layer %d %s %dHz %dKb/s %s",
+    msg_Dbg( p_input, "audio MPEG-%d layer %d %s %dHz %dKb/s %s",
                 mpeg.i_version + 1,
-                mpeg.i_layer +,
+                mpeg.i_layer + 1,
                 mpegaudio_mode[mpeg.i_mode],
                 mpeg.i_samplingfreq,
                 p_mpegaudio->xingheader.i_avgbitrate / 1000,
                 p_mpegaudio->xingheader.i_flags ?
                         "VBR (Xing)" : "" 
                     );
-    return( 0 );
-}
-
 
-/*****************************************************************************
- * MPEGAudioEnd: frees unused data
- *****************************************************************************/
-static void MPEGAudioEnd( input_thread_t * p_input )
-{
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+    p_category = input_InfoCategory( p_input, "mpeg" );
+    input_AddInfo( p_category, "input type", "audio MPEG-%d",
+                   mpeg.i_version +1 );
+    input_AddInfo( p_category, "layer", "%d", mpeg.i_layer + 1 );
+    input_AddInfo( p_category, "mode", mpegaudio_mode[mpeg.i_mode] );
+    input_AddInfo( p_category, "sample rate", "%dHz", mpeg.i_samplingfreq );
+    input_AddInfo( p_category, "average bitrate", "%dKb/s",
+                   p_mpegaudio->xingheader.i_avgbitrate / 1000 );
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
     
+    return( 0 );
 }
 
-
 /*****************************************************************************
- * MPEGAudioDemux: reads and demuxes data packets
+ * Demux: reads and demuxes data packets
  *****************************************************************************
  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
  *****************************************************************************/
-static int MPEGAudioDemux( input_thread_t * p_input )
+static int Demux( input_thread_t * p_input )
 {
     int i_pos;
     int i_toread;
@@ -486,11 +577,10 @@ static int MPEGAudioDemux( input_thread_t * p_input )
     mpegaudio_format_t mpeg;
     demux_data_mpegaudio_t *p_mpegaudio = 
                         (demux_data_mpegaudio_t*) p_input->p_demux_data;
-
     /*  look for a frame */
-    if( !MPEGAudio_FindFrame( p_input, &i_pos, &mpeg ) )
+    if( !MPEGAudio_FindFrame( p_input, &i_pos, &mpeg, 4096 ) )
     {
-        intf_WarnMsg( 1, "input error: cannot found next frame");
+        msg_Warn( p_input, "cannot find next frame" );
         return( 0 );
     }
     
@@ -499,7 +589,7 @@ static int MPEGAudioDemux( input_thread_t * p_input )
         ||( mpeg.i_layer != p_mpegaudio->mpeg.i_layer )
         ||( mpeg.i_samplingfreq != p_mpegaudio->mpeg.i_samplingfreq ) )
     {
-        intf_WarnMsg( 1, "input demux: stream has changed" );
+        msg_Dbg( p_input, "stream has changed" );
         p_mpegaudio->i_framecount = 0;
         p_mpegaudio->i_pts = 0;
     }
@@ -515,7 +605,7 @@ static int MPEGAudioDemux( input_thread_t * p_input )
     /* create one pes */
     if( !(p_pes = input_NewPES( p_input->p_method_data )) )
     {
-        intf_ErrMsg( "input demux: out of memory" );
+        msg_Err( p_input, "cannot allocate new PES" );
         return( -1 );
     }
 
@@ -543,11 +633,6 @@ static int MPEGAudioDemux( input_thread_t * p_input )
         p_pes->p_last  = p_data;
         i_toread -= i_read;
     }
-    /* XXX VERY STRANGE need to *90000 and not *1000000 WHYYYY ??? 
-        I know that for mpeg system clock unit is based on 90000 but 
-        pts is not supposed to be in microsec for vlc ? 
-        At least it's welcome to write it somewhere in input_clock.c 
-        i've wasted time because of it :( */
     p_mpegaudio->i_pts = (mtime_t)90000 * 
                                (mtime_t)p_mpegaudio->i_framecount * 
                                (mtime_t)MPEGAudio_DecodedFrameSize( &mpeg ) /
@@ -559,7 +644,7 @@ static int MPEGAudioDemux( input_thread_t * p_input )
 
     if( !p_mpegaudio->p_es->p_decoder_fifo )
     {
-        intf_ErrMsg( "input demux: no audio decoder" );
+        msg_Err( p_input, "no audio decoder" );
         input_DeletePES( p_input->p_method_data, p_pes );
         return( -1 ); /* perhaps not, it's my choice */
     }