X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Flpcm.c;h=4d1eb04f13c45733f0a64ca211661a5d26c41e8a;hb=661c20f5694da6ff372c7819c1b6cb828bb66962;hp=be2c1f86d269c0f3b842c6511ca1c1557c4079d4;hpb=c98f127bc4bebb8d39ac1d8922e045a9c1682d66;p=vlc diff --git a/modules/codec/lpcm.c b/modules/codec/lpcm.c index be2c1f86d2..4d1eb04f13 100644 --- a/modules/codec/lpcm.c +++ b/modules/codec/lpcm.c @@ -45,6 +45,12 @@ static int OpenDecoder ( vlc_object_t * ); static int OpenPacketizer( vlc_object_t * ); static void CloseCommon ( vlc_object_t * ); +#ifdef ENABLE_SOUT +static int OpenEncoder ( vlc_object_t * ); +static void CloseEncoder ( vlc_object_t * ); +static block_t *EncodeFrames( encoder_t *, aout_buffer_t * ); +#endif + vlc_module_begin () set_category( CAT_INPUT ) @@ -58,6 +64,14 @@ vlc_module_begin () set_capability( "packetizer", 100 ) set_callbacks( OpenPacketizer, CloseCommon ) +#ifdef ENABLE_SOUT + add_submodule () + set_description( N_("Linear PCM audio encoder") ) + set_capability( "encoder", 100 ) + set_callbacks( OpenEncoder, CloseEncoder ) + add_shortcut( "lpcm" ) +#endif + vlc_module_end () @@ -79,17 +93,32 @@ struct decoder_sys_t int i_type; }; +#ifdef ENABLE_SOUT +struct encoder_sys_t +{ + int i_channels; + int i_rate; + + int i_frame_samples; + uint8_t *p_buffer; + int i_buffer_used; + int i_frame_num; +}; +#endif + /* * LPCM DVD header : - * - frame number (8 bits) - * - unknown (16 bits) == 0x0003 ? - * - unknown (4 bits) - * - current frame (4 bits) - * - unknown (2 bits) - * - frequency (2 bits) 0 == 48 kHz, 1 == 32 kHz, 2 == ?, 3 == ? - * - unknown (1 bit) + * - number of frames in this packet (8 bits) + * - first access unit (16 bits) == 0x0003 ? + * - emphasis (1 bit) + * - mute (1 bit) + * - reserved (1 bit) + * - current frame (5 bits) + * - quantisation (2 bits) 0 == 16bps, 1 == 20bps, 2 == 24bps, 3 == illegal + * - frequency (2 bits) 0 == 48 kHz, 1 == 96 kHz, 2 == 44.1 kHz, 3 == 32 kHz + * - reserved (1 bit) * - number of channels - 1 (3 bits) 1 == 2 channels - * - start code (8 bits) == 0x80 + * - dynamic range (8 bits) 0x80 == neutral * * LPCM DVD-A header (http://dvd-audio.sourceforge.net/spec/aob.shtml) * - continuity counter (8 bits, clipped to 0x00-0x1f) @@ -307,8 +336,7 @@ static void *DecodeFrame( decoder_t *p_dec, block_t **pp_block ) p_block->p_buffer ); break; default: - assert(0); - i_ret = VLC_EGENERIC; + abort(); } if( i_ret || p_block->i_buffer <= p_sys->i_header_size + i_padding ) @@ -398,6 +426,145 @@ static void CloseCommon( vlc_object_t *p_this ) free( p_dec->p_sys ); } +#ifdef ENABLE_SOUT +/***************************************************************************** + * OpenEncoder: lpcm encoder construction + *****************************************************************************/ +static int OpenEncoder( vlc_object_t *p_this ) +{ + encoder_t *p_enc = (encoder_t *)p_this; + encoder_sys_t *p_sys; + + /* We only support DVD LPCM yet. */ + if( p_enc->fmt_out.i_codec != VLC_CODEC_DVD_LPCM || + ( p_enc->fmt_in.audio.i_rate != 48000 && + p_enc->fmt_in.audio.i_rate != 96000 && + p_enc->fmt_in.audio.i_rate != 44100 && + p_enc->fmt_in.audio.i_rate != 32000 ) || + p_enc->fmt_in.audio.i_channels > 8 ) + return VLC_EGENERIC; + + /* Allocate the memory needed to store the encoder's structure */ + if( ( p_enc->p_sys = p_sys = + (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL ) + return VLC_ENOMEM; + + /* In DVD LCPM, a frame is always 150 PTS ticks. */ + p_sys->i_frame_samples = p_enc->fmt_in.audio.i_rate * 150 / 90000; + p_sys->p_buffer = (uint8_t *)malloc( + p_sys->i_frame_samples * + p_enc->fmt_in.audio.i_channels * + p_enc->fmt_in.audio.i_bitspersample); + p_sys->i_buffer_used = 0; + p_sys->i_frame_num = 0; + + p_sys->i_channels = p_enc->fmt_in.audio.i_channels; + p_sys->i_rate = p_enc->fmt_in.audio.i_rate; + + p_enc->pf_encode_audio = EncodeFrames; + p_enc->fmt_in.i_codec = p_enc->fmt_out.i_codec; + + p_enc->fmt_in.audio.i_bitspersample = 16; + p_enc->fmt_in.i_codec = VLC_CODEC_S16B; + + p_enc->fmt_out.i_bitrate = + p_enc->fmt_in.audio.i_channels * + p_enc->fmt_in.audio.i_rate * + p_enc->fmt_in.audio.i_bitspersample * + (p_sys->i_frame_samples + LPCM_VOB_HEADER_LEN) / + p_sys->i_frame_samples; + + return VLC_SUCCESS; +} + +/***************************************************************************** + * CloseEncoder: lpcm encoder destruction + *****************************************************************************/ +static void CloseEncoder ( vlc_object_t *p_this ) +{ + VLC_UNUSED(p_this); +} + +/***************************************************************************** + * EncodeFrames: encode zero or more LCPM audio packets + *****************************************************************************/ +static block_t *EncodeFrames( encoder_t *p_enc, aout_buffer_t *p_aout_buf ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + block_t *p_first_block = NULL, *p_last_block = NULL; + + if( !p_aout_buf || !p_aout_buf->i_buffer ) return NULL; + + const int i_num_frames = ( p_sys->i_buffer_used + p_aout_buf->i_nb_samples ) / + p_sys->i_frame_samples; + const int i_leftover_samples = ( p_sys->i_buffer_used + p_aout_buf->i_nb_samples ) % + p_sys->i_frame_samples; + const int i_frame_size = p_sys->i_frame_samples * p_sys->i_channels * 2 + LPCM_VOB_HEADER_LEN; + const int i_start_offset = -p_sys->i_buffer_used; + + uint8_t i_freq_code = 0; + + switch( p_sys->i_rate ) { + case 48000: + i_freq_code = 0; + break; + case 96000: + i_freq_code = 1; + break; + case 44100: + i_freq_code = 2; + break; + case 32000: + i_freq_code = 3; + break; + } + + int i_bytes_consumed = 0; + + for ( int i = 0; i < i_num_frames; ++i ) + { + block_t *p_block = block_New( p_enc, i_frame_size ); + if( !p_block ) + return NULL; + + uint8_t *frame = (uint8_t *)p_block->p_buffer; + frame[0] = 1; /* one frame in packet */ + frame[1] = 0; + frame[2] = 0; /* no first access unit */ + frame[3] = (p_sys->i_frame_num + i) & 0x1f; /* no emphasis, no mute */ + frame[4] = (i_freq_code << 4) | (p_sys->i_channels - 1); + frame[5] = 0x80; /* neutral dynamic range */ + + const int i_consume_samples = p_sys->i_frame_samples - p_sys->i_buffer_used; + const int i_kept_bytes = p_sys->i_buffer_used * p_sys->i_channels * 2; + const int i_consume_bytes = i_consume_samples * p_sys->i_channels * 2; + + memcpy( frame + 6, p_sys->p_buffer, i_kept_bytes ); + memcpy( frame + 6 + i_kept_bytes, p_aout_buf->p_buffer + i_bytes_consumed, i_consume_bytes ); + + p_sys->i_frame_num++; + p_sys->i_buffer_used = 0; + i_bytes_consumed += i_consume_bytes; + + p_block->i_dts = p_block->i_pts = p_aout_buf->i_pts + + (i * p_sys->i_frame_samples + i_start_offset) * CLOCK_FREQ / p_sys->i_rate; + p_block->i_length = p_sys->i_frame_samples * CLOCK_FREQ / p_sys->i_rate; + + if( !p_first_block ) + p_first_block = p_last_block = p_block; + else + p_last_block = p_last_block->p_next = p_block; + } + + memcpy( p_sys->p_buffer, + p_aout_buf->p_buffer + i_bytes_consumed, + i_leftover_samples * p_sys->i_channels * 2 ); + p_sys->i_buffer_used = i_leftover_samples; + + return p_first_block; +} +#endif + /***************************************************************************** * *****************************************************************************/