* modules/codec/flac.c: added a FLAC encoder.
authorGildas Bazin <gbazin@videolan.org>
Fri, 21 Nov 2003 20:49:14 +0000 (20:49 +0000)
committerGildas Bazin <gbazin@videolan.org>
Fri, 21 Nov 2003 20:49:14 +0000 (20:49 +0000)
* modules/mux/dummy.c, modules/mux/ogg.c: you can output "normal" flac streams with the es/dummy muxer or output them with an Ogg container.

modules/codec/flac.c
modules/demux/flac.c
modules/mux/dummy.c
modules/mux/ogg.c

index 0b90e96..4d8689b 100644 (file)
@@ -1,8 +1,8 @@
 /*****************************************************************************
- * flac.c: flac decoder/packetizer module making use of libflac
+ * flac.c: flac decoder/packetizer/encoder module making use of libflac
  *****************************************************************************
  * Copyright (C) 1999-2001 VideoLAN
- * $Id: flac.c,v 1.2 2003/11/21 12:18:54 gbazin Exp $
+ * $Id: flac.c,v 1.3 2003/11/21 20:49:13 gbazin Exp $
  *
  * Authors: Gildas Bazin <gbazin@netcourrier.com>
  *          Sigmund Augdal <sigmunau@idi.ntnu.no>
@@ -30,6 +30,7 @@
 #include <vlc/input.h>
 
 #include <FLAC/stream_decoder.h>
+#include <FLAC/stream_encoder.h>
 
 #include "vlc_block_helper.h"
 
@@ -103,6 +104,9 @@ static int  OpenDecoder   ( vlc_object_t * );
 static int  OpenPacketizer( vlc_object_t * );
 static void CloseDecoder  ( vlc_object_t * );
 
+static int OpenEncoder   ( vlc_object_t * );
+static void CloseEncoder ( vlc_object_t * );
+
 static aout_buffer_t *DecodeBlock( decoder_t *, block_t ** );
 static block_t *PacketizeBlock( decoder_t *, block_t ** );
 
@@ -150,6 +154,11 @@ vlc_module_begin();
     set_capability( "packetizer", 100 );
     set_callbacks( OpenPacketizer, CloseDecoder );
 
+    add_submodule();
+    set_description( _("Flac audio encoder") );
+    set_capability( "encoder", 100 );
+    set_callbacks( OpenEncoder, CloseEncoder );
+
 vlc_module_end();
 
 /*****************************************************************************
@@ -211,10 +220,9 @@ static int OpenDecoder( vlc_object_t *p_this )
 
     /* Decode STREAMINFO */
     msg_Dbg( p_dec, "decode STREAMINFO" );
-    p_sys->p_block = block_New( p_dec, p_dec->fmt_in.i_extra + 4 );
-    memcpy( p_sys->p_block->p_buffer + 4, p_dec->fmt_in.p_extra,
+    p_sys->p_block = block_New( p_dec, p_dec->fmt_in.i_extra );
+    memcpy( p_sys->p_block->p_buffer, p_dec->fmt_in.p_extra,
             p_dec->fmt_in.i_extra );
-    memcpy( p_sys->p_block->p_buffer, "fLaC", 4 );
     FLAC__stream_decoder_process_until_end_of_metadata( p_sys->p_flac );
     msg_Dbg( p_dec, "STREAMINFO decoded" );
 
@@ -230,7 +238,7 @@ static int OpenPacketizer( vlc_object_t *p_this )
     if( i_ret == VLC_SUCCESS )
     {
         p_dec->p_sys->b_packetizer = VLC_TRUE;
-        p_dec->fmt_out.i_codec = VLC_FOURCC('f','l','a','c');
+        es_format_Copy( &p_dec->fmt_out, &p_dec->fmt_in );
     }
 
     return i_ret;
@@ -1009,3 +1017,205 @@ static uint8_t flac_crc8( const uint8_t *data, unsigned len )
 
     return crc;
 }
+
+/*****************************************************************************
+ * encoder_sys_t : flac encoder descriptor
+ *****************************************************************************/
+struct encoder_sys_t
+{
+    /*
+     * Input properties
+     */
+    int i_headers;
+
+    int i_samples_delay;
+    int i_channels;
+
+    FLAC__int32 *p_buffer;
+    int         i_buffer;
+
+    block_t *p_chain;
+
+    /*
+     * FLAC properties
+     */
+    FLAC__StreamEncoder *p_flac;
+    FLAC__StreamMetadata_StreamInfo stream_info;
+
+    /*
+     * Common properties
+     */
+    mtime_t i_pts;
+};
+
+#define STREAMINFO_SIZE 38
+
+static block_t *Encode( encoder_t *, aout_buffer_t * );
+
+static FLAC__StreamEncoderWriteStatus
+EncoderWriteCallback( const FLAC__StreamEncoder *encoder,
+                      const FLAC__byte buffer[],
+                      unsigned bytes, unsigned samples,
+                      unsigned current_frame, void *client_data );
+
+static void EncoderMetadataCallback( const FLAC__StreamEncoder *encoder,
+                                     const FLAC__StreamMetadata *metadata,
+                                     void *client_data );
+
+/*****************************************************************************
+ * OpenEncoder: probe the encoder and return score
+ *****************************************************************************/
+static int OpenEncoder( vlc_object_t *p_this )
+{
+    encoder_t *p_enc = (encoder_t *)p_this;
+    encoder_sys_t *p_sys;
+
+    if( p_enc->fmt_out.i_codec != VLC_FOURCC('f','l','a','c') )
+    {
+        return VLC_EGENERIC;
+    }
+
+    /* Allocate the memory needed to store the decoder's structure */
+    if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL )
+    {
+        msg_Err( p_enc, "out of memory" );
+        return VLC_EGENERIC;
+    }
+    p_enc->p_sys = p_sys;
+    p_enc->pf_encode_audio = Encode;
+    p_sys->i_headers = 0;
+    p_sys->p_buffer = 0;
+    p_sys->i_buffer = 0;
+
+    /* Create flac encoder */
+    p_sys->p_flac = FLAC__stream_encoder_new();
+
+    FLAC__stream_encoder_set_streamable_subset( p_sys->p_flac, 1 );
+    FLAC__stream_encoder_set_channels( p_sys->p_flac,
+                                       p_enc->fmt_in.audio.i_channels );
+    FLAC__stream_encoder_set_sample_rate( p_sys->p_flac,
+                                          p_enc->fmt_in.audio.i_rate );
+    FLAC__stream_encoder_set_bits_per_sample( p_sys->p_flac, 16 );
+    p_enc->fmt_in.i_codec = AOUT_FMT_S16_NE;
+
+    FLAC__stream_encoder_set_write_callback( p_sys->p_flac,
+        EncoderWriteCallback );
+    FLAC__stream_encoder_set_metadata_callback( p_sys->p_flac,
+        EncoderMetadataCallback );
+    FLAC__stream_encoder_set_client_data( p_sys->p_flac, p_enc );
+
+    /* Get and store the STREAMINFO metadata block as a p_extra */
+    p_sys->p_chain = 0;
+    FLAC__stream_encoder_init( p_sys->p_flac );
+
+    return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * Encode: the whole thing
+ ****************************************************************************
+ * This function spits out ogg packets.
+ ****************************************************************************/
+static block_t *Encode( encoder_t *p_enc, aout_buffer_t *p_aout_buf )
+{
+    encoder_sys_t *p_sys = p_enc->p_sys;
+    block_t *p_chain;
+    int i;
+
+    p_sys->i_pts = p_aout_buf->start_date -
+                (mtime_t)1000000 * (mtime_t)p_sys->i_samples_delay /
+                (mtime_t)p_enc->fmt_in.audio.i_rate;
+
+    p_sys->i_samples_delay += p_aout_buf->i_nb_samples;
+
+    /* Convert samples to FLAC__int32 */
+    if( p_sys->i_buffer < p_aout_buf->i_nb_bytes * 2 )
+    {
+        p_sys->p_buffer =
+            realloc( p_sys->p_buffer, p_aout_buf->i_nb_bytes * 2 );
+        p_sys->i_buffer = p_aout_buf->i_nb_bytes * 2;
+    }
+
+    for( i = 0 ; i < p_aout_buf->i_nb_bytes / 2 ; i++ )
+    {
+        p_sys->p_buffer[i]= ((int16_t *)p_aout_buf->p_buffer)[i];
+    }
+
+    FLAC__stream_encoder_process_interleaved( p_sys->p_flac, p_sys->p_buffer,
+                                              p_aout_buf->i_nb_samples );
+
+    p_chain = p_sys->p_chain;
+    p_sys->p_chain = 0;
+
+    return p_chain;
+}
+
+/*****************************************************************************
+ * CloseEncoder: encoder destruction
+ *****************************************************************************/
+static void CloseEncoder( vlc_object_t *p_this )
+{
+    encoder_t *p_enc = (encoder_t *)p_this;
+    encoder_sys_t *p_sys = p_enc->p_sys;
+
+    FLAC__stream_encoder_delete( p_sys->p_flac );
+
+    if( p_sys->p_buffer ) free( p_sys->p_buffer );
+    free( p_sys );
+}
+
+/*****************************************************************************
+ * EncoderMetadataCallback: called by libflac to output metadata
+ *****************************************************************************/
+static void EncoderMetadataCallback( const FLAC__StreamEncoder *encoder,
+                                     const FLAC__StreamMetadata *metadata,
+                                     void *client_data )
+{
+    encoder_t *p_enc = (encoder_t *)client_data;
+
+    msg_Err( p_enc, "MetadataCallback: %i", metadata->type );
+    return;
+}
+
+/*****************************************************************************
+ * EncoderWriteCallback: called by libflac to output encoded samples
+ *****************************************************************************/
+static FLAC__StreamEncoderWriteStatus
+EncoderWriteCallback( const FLAC__StreamEncoder *encoder,
+                      const FLAC__byte buffer[],
+                      unsigned bytes, unsigned samples,
+                      unsigned current_frame, void *client_data )
+{
+    encoder_t *p_enc = (encoder_t *)client_data;
+    encoder_sys_t *p_sys = p_enc->p_sys;
+    block_t *p_block;
+
+    if( samples == 0 && p_sys->i_headers <= 1 )
+    {
+        if( p_sys->i_headers == 1 )
+        {
+            msg_Err( p_enc, "Writing STREAMINFO: %i", bytes );
+
+            /* Backup the STREAMINFO metadata block */
+            p_enc->fmt_out.i_extra = STREAMINFO_SIZE + 4;
+            p_enc->fmt_out.p_extra = malloc( STREAMINFO_SIZE + 4 );
+            memcpy( p_enc->fmt_out.p_extra, "fLaC", 4 );
+            memcpy( ((uint8_t *)p_enc->fmt_out.p_extra) + 4, buffer,
+                    STREAMINFO_SIZE + 4 );
+
+            /* Fake this as the last metadata block */
+            ((uint8_t*)p_enc->fmt_out.p_extra)[4] |= 0x80;
+        }
+        p_sys->i_headers++;
+        return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+    }
+
+    p_block = block_New( p_enc, bytes );
+    memcpy( p_block->p_buffer, buffer, bytes );
+
+    p_block->i_dts = p_block->i_pts = p_block->i_length = 0;
+
+    block_ChainAppend( &p_sys->p_chain, p_block );
+
+    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
index 08440e8..9809dfc 100644 (file)
@@ -2,7 +2,7 @@
  * flac.c : FLAC demux module for vlc
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: flac.c,v 1.7 2003/11/21 12:18:54 gbazin Exp $
+ * $Id: flac.c,v 1.8 2003/11/21 20:49:13 gbazin Exp $
  *
  * Authors: Gildas Bazin <gbazin@netcourrier.com>
  *
@@ -99,18 +99,15 @@ static int Open( vlc_object_t * p_this )
     es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'f', 'l', 'a', 'c' ) );
     p_sys->b_start = VLC_TRUE;
 
-    /* Skip stream marker */
-    stream_Read( p_input->s, NULL, 4 );
-
     /* We need to read and store the STREAMINFO metadata */
-    i_peek = stream_Peek( p_input->s, &p_peek, 4 );
-    if( p_peek[0] & 0x7F )
+    i_peek = stream_Peek( p_input->s, &p_peek, 8 );
+    if( p_peek[4] & 0x7F )
     {
         msg_Err( p_input, "This isn't a STREAMINFO metadata block" );
         return VLC_EGENERIC;
     }
 
-    if( ((p_peek[1]<<16)+(p_peek[2]<<8)+p_peek[3]) != (STREAMINFO_SIZE - 4) )
+    if( ((p_peek[5]<<16)+(p_peek[6]<<8)+p_peek[7]) != (STREAMINFO_SIZE - 4) )
     {
         msg_Err( p_input, "Invalid size for a STREAMINFO metadata block" );
         return VLC_EGENERIC;
@@ -132,15 +129,16 @@ static int Open( vlc_object_t * p_this )
                     VLC_FOURCC( 'f', 'l', 'a', 'c' ) );
 
     /* Store STREAMINFO for the decoder and packetizer */
-    p_sys->p_packetizer->fmt_in.i_extra = fmt.i_extra = STREAMINFO_SIZE;
-    p_sys->p_packetizer->fmt_in.p_extra = malloc( STREAMINFO_SIZE );
+    p_sys->p_packetizer->fmt_in.i_extra = fmt.i_extra = STREAMINFO_SIZE + 4;
+    p_sys->p_packetizer->fmt_in.p_extra = malloc( STREAMINFO_SIZE + 4 );
     stream_Read( p_input->s, p_sys->p_packetizer->fmt_in.p_extra,
-                 STREAMINFO_SIZE );
+                 STREAMINFO_SIZE + 4 );
 
     /* Fake this as the last metadata block */
-    ((uint8_t*)p_sys->p_packetizer->fmt_in.p_extra)[0] |= 0x80;
-    fmt.p_extra = malloc( STREAMINFO_SIZE );
-    memcpy( fmt.p_extra, p_sys->p_packetizer->fmt_in.p_extra, STREAMINFO_SIZE);
+    ((uint8_t*)p_sys->p_packetizer->fmt_in.p_extra)[4] |= 0x80;
+    fmt.p_extra = malloc( STREAMINFO_SIZE + 4 );
+    memcpy( fmt.p_extra, p_sys->p_packetizer->fmt_in.p_extra,
+            STREAMINFO_SIZE + 4 );
 
     p_sys->p_packetizer->p_module =
         module_Need( p_sys->p_packetizer, "packetizer", NULL );
index 0dfc747..29a5903 100644 (file)
@@ -1,8 +1,8 @@
 /*****************************************************************************
- * dummy.c
+ * dummy.c: dummy muxer module for vlc
  *****************************************************************************
  * Copyright (C) 2001, 2002 VideoLAN
- * $Id: dummy.c,v 1.8 2003/08/17 18:44:26 fenrir Exp $
+ * $Id: dummy.c,v 1.9 2003/11/21 20:49:14 gbazin Exp $
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *          Eric Petit <titer@videolan.org>
@@ -47,7 +47,6 @@ vlc_module_begin();
     set_callbacks( Open, Close );
 vlc_module_end();
 
-
 /*****************************************************************************
  * Exported prototypes
  *****************************************************************************/
@@ -56,12 +55,20 @@ static int  AddStream( sout_mux_t *, sout_input_t * );
 static int  DelStream( sout_mux_t *, sout_input_t * );
 static int  Mux      ( sout_mux_t * );
 
+struct sout_mux_sys_t
+{
+    /* Some streams have special initialization data, we'll output this
+     * data as an header in the stream. */
+    vlc_bool_t b_header;
+};
+
 /*****************************************************************************
  * Open:
  *****************************************************************************/
 static int Open( vlc_object_t *p_this )
 {
     sout_mux_t *p_mux = (sout_mux_t*)p_this;
+    sout_mux_sys_t  *p_sys;
 
     msg_Dbg( p_mux, "Dummy/Raw muxer opened" );
     msg_Info( p_mux, "Open" );
@@ -71,6 +78,9 @@ static int Open( vlc_object_t *p_this )
     p_mux->pf_delstream = DelStream;
     p_mux->pf_mux       = Mux;
 
+    p_mux->p_sys = p_sys = malloc( sizeof( sout_mux_sys_t ) );
+    p_sys->b_header      = VLC_TRUE;
+
     return VLC_SUCCESS;
 }
 
@@ -81,8 +91,10 @@ static int Open( vlc_object_t *p_this )
 static void Close( vlc_object_t * p_this )
 {
     sout_mux_t *p_mux = (sout_mux_t*)p_this;
+    sout_mux_sys_t *p_sys = p_mux->p_sys;
 
     msg_Dbg( p_mux, "Dummy/Raw muxer closed" );
+    free( p_sys );
 }
 
 static int Capability( sout_mux_t *p_mux, int i_query,
@@ -101,24 +113,42 @@ static int Capability( sout_mux_t *p_mux, int i_query,
 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
 {
     msg_Dbg( p_mux, "adding input" );
-    return( 0 );
+    return VLC_SUCCESS;
 }
 
 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
 {
-
     msg_Dbg( p_mux, "removing input" );
-    return( 0 );
+    return VLC_SUCCESS;
 }
 
-static int Mux      ( sout_mux_t *p_mux )
+static int Mux( sout_mux_t *p_mux )
 {
+    sout_mux_sys_t *p_sys = p_mux->p_sys;
     int i;
+
     for( i = 0; i < p_mux->i_nb_inputs; i++ )
     {
         int i_count;
         sout_fifo_t *p_fifo;
 
+        if( p_sys->b_header && p_mux->pp_inputs[i]->p_fmt->i_extra )
+        {
+            /* Write header data */
+            sout_buffer_t *p_data;
+            p_data = sout_BufferNew( p_mux->p_sout,
+                                     p_mux->pp_inputs[i]->p_fmt->i_extra );
+
+            memcpy( p_data->p_buffer, p_mux->pp_inputs[i]->p_fmt->p_extra,
+                    p_mux->pp_inputs[i]->p_fmt->i_extra );
+
+            p_data->i_size = p_mux->pp_inputs[i]->p_fmt->i_extra;
+            p_data->i_dts = p_data->i_pts = p_data->i_length = 0;
+
+            msg_Dbg( p_mux, "writing header data" );
+            sout_AccessOutWrite( p_mux->p_access, p_data );
+        }
+
         p_fifo = p_mux->pp_inputs[i]->p_fifo;
         i_count = p_fifo->i_depth;
         while( i_count > 0 )
@@ -133,6 +163,7 @@ static int Mux      ( sout_mux_t *p_mux )
         }
 
     }
-    return( 0 );
-}
+    p_sys->b_header = VLC_FALSE;
 
+    return VLC_SUCCESS;
+}
index 3e84a6f..33c7914 100644 (file)
@@ -2,7 +2,7 @@
  * ogg.c: ogg muxer module for vlc
  *****************************************************************************
  * Copyright (C) 2001, 2002 VideoLAN
- * $Id: ogg.c,v 1.21 2003/11/21 15:32:08 fenrir Exp $
+ * $Id: ogg.c,v 1.22 2003/11/21 20:49:14 gbazin Exp $
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *          Gildas Bazin <gbazin@netcourrier.com>
@@ -426,6 +426,10 @@ static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
             msg_Dbg( p_mux, "speex stream" );
             break;
 
+        case VLC_FOURCC( 'f', 'l', 'a', 'c' ):
+            msg_Dbg( p_mux, "flac stream" );
+            break;
+
         default:
             FREE( p_input->p_sys );
             return( VLC_EGENERIC );
@@ -638,6 +642,18 @@ static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
                 }
             }
         }
+        else if( p_stream->i_fourcc == VLC_FOURCC( 'f', 'l', 'a', 'c' ) )
+        {
+            /* flac stream marker (yeah, only that in the 1st packet) */
+            op.packet = "fLaC";
+            op.bytes  = 4;
+            op.b_o_s  = 1;
+            op.e_o_s  = 0;
+            op.granulepos = 0;
+            op.packetno = p_stream->i_packet_no++;
+            ogg_stream_packetin( &p_stream->os, &op );
+            p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
+        }
         else
         {
             /* ds header */
@@ -689,7 +705,7 @@ static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
                 sout_BufferChain( &p_hdr, p_og );
             }
         }
-        else
+        else if( p_stream->i_fourcc != VLC_FOURCC( 'f', 'l', 'a', 'c' ) )
         {
             uint8_t com[128];
             int     i_com;
@@ -708,13 +724,22 @@ static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
             sout_BufferChain( &p_hdr, p_og );
         }
 
-        /* Special case for mp4v */
-        if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p', '4', 'v' ) &&
+        /* Special case for mp4v and flac */
+        if( ( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p', '4', 'v' ) ||
+              p_stream->i_fourcc == VLC_FOURCC( 'f', 'l', 'a', 'c' ) ) &&
             p_mux->pp_inputs[i]->p_fmt->i_extra )
         {
-            /* Send a packet with the VOL data */
+            /* Send a packet with the VOL data for mp4v
+             * or STREAMINFO for flac */
+            msg_Dbg( p_mux, "writing extra data" );
             op.bytes  = p_mux->pp_inputs[i]->p_fmt->i_extra;
             op.packet = p_mux->pp_inputs[i]->p_fmt->p_extra;
+            if( p_stream->i_fourcc == VLC_FOURCC( 'f', 'l', 'a', 'c' ) )
+            {
+                /* Skip the flac stream marker */
+                ((uint8_t *)op.bytes) -= 4;
+                ((uint8_t *)op.packet) += 4;
+            }
             op.b_o_s  = 0;
             op.e_o_s  = 0;
             op.granulepos = 0;