From 09d997200c05de78bb05ea4e2219fec752aef5a9 Mon Sep 17 00:00:00 2001 From: Gildas Bazin Date: Fri, 21 Nov 2003 20:49:14 +0000 Subject: [PATCH] * modules/codec/flac.c: added a FLAC encoder. * 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 | 222 +++++++++++++++++++++++++++++++++++++++++-- modules/demux/flac.c | 24 +++-- modules/mux/dummy.c | 49 ++++++++-- modules/mux/ogg.c | 35 ++++++- 4 files changed, 297 insertions(+), 33 deletions(-) diff --git a/modules/codec/flac.c b/modules/codec/flac.c index 0b90e96ca1..4d8689b281 100644 --- a/modules/codec/flac.c +++ b/modules/codec/flac.c @@ -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 * Sigmund Augdal @@ -30,6 +30,7 @@ #include #include +#include #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; +} diff --git a/modules/demux/flac.c b/modules/demux/flac.c index 08440e8041..9809dfc37d 100644 --- a/modules/demux/flac.c +++ b/modules/demux/flac.c @@ -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 * @@ -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 ); diff --git a/modules/mux/dummy.c b/modules/mux/dummy.c index 0dfc747187..29a5903884 100644 --- a/modules/mux/dummy.c +++ b/modules/mux/dummy.c @@ -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 * Eric Petit @@ -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; +} diff --git a/modules/mux/ogg.c b/modules/mux/ogg.c index 3e84a6f615..33c7914f58 100644 --- a/modules/mux/ogg.c +++ b/modules/mux/ogg.c @@ -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 * Gildas Bazin @@ -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; -- 2.39.2