/*****************************************************************************
* encoder.c: video and audio encoder using the libavcodec library
*****************************************************************************
- * Copyright (C) 1999-2004 the VideoLAN team
+ * Copyright (C) 1999-2004 VLC authors and VideoLAN
* $Id$
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Part of the file Copyright (C) FFmpeg Project Developers
* (mpeg4_default matrixes)
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
AVCodecContext *p_context;
/*
- * Common properties
+ * Common buffer mainly for audio as frame size in there needs usually be constant
*/
char *p_buffer;
size_t i_buffer_out;
if( ( p_sys = calloc( 1, sizeof(encoder_sys_t) ) ) == NULL )
return VLC_ENOMEM;
p_enc->p_sys = p_sys;
+ p_sys->i_samples_delay = 0;
p_sys->p_codec = p_codec;
- p_enc->pf_encode_video = EncodeVideo;
- p_enc->pf_encode_audio = EncodeAudio;
-
p_sys->p_buffer = NULL;
p_sys->i_buffer_out = 0;
p_context->sample_fmt = p_codec->sample_fmts ?
p_codec->sample_fmts[0] :
AV_SAMPLE_FMT_S16;
- p_enc->fmt_in.i_codec = VLC_CODEC_S16N;
+
+ // Try if we can use interleaved format for codec input as VLC doesn't really do planar audio yet
+ // FIXME: Remove when planar/interleaved audio in vlc is equally supported
+ if( av_sample_fmt_is_planar( p_context->sample_fmt ) )
+ {
+ msg_Dbg( p_enc, "Trying to find packet sample format instead of %s", av_get_sample_fmt_name( p_context->sample_fmt ) );
+ for( unsigned int i=0; p_codec->sample_fmts[i] != -1; i++ )
+ {
+ if( !av_sample_fmt_is_planar( p_codec->sample_fmts[i] ) )
+ {
+ p_context->sample_fmt = p_codec->sample_fmts[i];
+ msg_Dbg( p_enc, "Using %s as new sample format", av_get_sample_fmt_name( p_context->sample_fmt ) );
+ break;
+ }
+ }
+ }
+ p_enc->fmt_in.i_codec = GetVlcAudioFormat( p_context->sample_fmt );
+
p_context->sample_rate = p_enc->fmt_out.audio.i_rate;
p_context->time_base.num = 1;
p_context->time_base.den = p_context->sample_rate;
if( p_enc->fmt_in.i_cat == AUDIO_ES )
{
- GetVlcAudioFormat( &p_enc->fmt_in.i_codec,
- &p_enc->fmt_in.audio.i_bitspersample,
- p_sys->p_context->sample_fmt );
+ p_enc->fmt_in.i_codec = GetVlcAudioFormat( p_sys->p_context->sample_fmt );
+ p_enc->fmt_in.audio.i_bitspersample = aout_BitsPerSample( p_enc->fmt_in.i_codec );
+
p_sys->i_sample_bytes = (p_enc->fmt_in.audio.i_bitspersample / 8) *
p_context->channels;
p_sys->i_frame_size = p_context->frame_size > 1 ?
goto error;
}
p_enc->fmt_out.audio.i_blockalign = p_context->block_align;
- p_enc->fmt_out.audio.i_bitspersample = aout_BitsPerSample( vlc_fourcc_GetCodec( AUDIO_ES, p_enc->fmt_out.i_codec ) );
+ p_enc->fmt_out.audio.i_bitspersample = aout_BitsPerSample( p_enc->fmt_out.i_codec );
if( p_context->frame_size > 1 )
p_sys->i_buffer_out = 8 * AVCODEC_MAX_AUDIO_FRAME_SIZE;
}
msg_Dbg( p_enc, "found encoder %s", psz_namecodec );
+ p_enc->pf_encode_video = EncodeVideo;
+ p_enc->pf_encode_audio = EncodeAudio;
+
return VLC_SUCCESS;
error:
const int bytesPerPixel = p_enc->fmt_out.video.i_bits_per_pixel ?
p_enc->fmt_out.video.i_bits_per_pixel / 8 : 3;
const int blocksize = __MAX( FF_MIN_BUFFER_SIZE,bytesPerPixel * p_sys->p_context->height * p_sys->p_context->width + 200 );
- block_t *p_block = block_New( p_enc, blocksize );
+ block_t *p_block = block_Alloc( blocksize );
if( likely(p_pict) ) {
AVFrame *frame;
encoder_sys_t *p_sys = p_enc->p_sys;
block_t *p_block, *p_chain = NULL;
- AVFrame *frame;
- AVPacket packet;
- int got_packet,i_out;
+ AVFrame *frame=NULL;
+ int got_packet,i_out,i_samples_left=0,i_data_offset = 0;
+
+ //i_samples_left is amount of samples we get
+ i_samples_left = p_aout_buf ? p_aout_buf->i_nb_samples : 0;
+
+ if( p_sys->i_samples_delay > 0 )
+ {
+ AVPacket packet;
+ //How much we need to copy from new packet
+ const int leftover = __MAX(0,__MIN(i_samples_left, (p_sys->i_frame_size - p_sys->i_samples_delay)));
+
+ frame = avcodec_alloc_frame();
+ frame->nb_samples = p_sys->i_samples_delay + leftover;
+ frame->format = p_sys->p_context->sample_fmt;
+
+ //Copy samples from new packet to buffer to get frame size
+ if( likely( leftover ) )
+ {
+ if( av_sample_fmt_is_planar( p_sys->p_context->sample_fmt ) )
+ aout_Deinterleave( p_sys->p_buffer+(p_sys->i_samples_delay*p_sys->i_sample_bytes*p_enc->fmt_in.audio.i_channels ),
+ p_aout_buf->p_buffer, leftover, p_enc->fmt_in.audio.i_channels, p_enc->fmt_in.i_codec );
+ else
+ memcpy( p_sys->p_buffer+(p_sys->i_samples_delay*p_sys->i_sample_bytes),
+ p_aout_buf->p_buffer,
+ leftover * p_sys->i_sample_bytes * p_enc->fmt_in.audio.i_channels
+ );
+ }
+
+ if( avcodec_fill_audio_frame( frame, p_enc->fmt_in.audio.i_channels,
+ p_sys->p_context->sample_fmt,
+ p_sys->p_buffer,
+ (p_sys->i_samples_delay + leftover) * p_sys->i_sample_bytes * p_enc->fmt_in.audio.i_channels,
+ 0) < 0 )
+ msg_Err( p_enc, "Filling on leftovers error i_leftover %d i_samples_left %d samples_delay %d frame size %d", leftover, i_samples_left, p_sys->i_samples_delay, p_sys->i_frame_size );
+
+ if( likely( p_aout_buf ) )
+ frame->pts = p_aout_buf->i_pts -
+ (mtime_t)1000000 * (mtime_t)p_sys->i_samples_delay /
+ (mtime_t)p_enc->fmt_in.audio.i_rate;
+
+ p_sys->i_samples_delay = 0;
+ i_data_offset += leftover * p_sys->i_sample_bytes * p_enc->fmt_in.audio.i_channels;
+ i_samples_left -= leftover;
+
+ p_block = block_Alloc( p_sys->i_buffer_out );
+ av_init_packet( &packet );
+ packet.data = p_block->p_buffer;
+ packet.size = p_block->i_buffer;
+
+ i_out = avcodec_encode_audio2( p_sys->p_context, &packet, frame, &got_packet );
+ p_block->i_buffer = packet.size;
+
+
+ /*FIXME: same as avcodec_free_frame, but we don't require so new avcodec that has it*/
+ if( frame->extended_data != frame->data )
+ av_freep( frame->extended_data );
+ av_freep( &frame );
+ if( unlikely( !got_packet || ( i_out < 0 ) ) )
+ {
+ if( i_out < 0 )
+ {
+ msg_Err( p_enc,"Encoding problem...");
+ return p_chain;
+ }
+ } else {
+ p_block->i_buffer = packet.size;
+
+ p_block->i_length = (mtime_t)1000000 *
+ (mtime_t)p_sys->i_frame_size /
+ (mtime_t)p_sys->p_context->sample_rate;
+
+ p_block->i_dts = p_block->i_pts = packet.pts;
+
+ block_ChainAppend( &p_chain, p_block );
+ }
+ }
- /*FIXME: change to use avcodec_encode_audio2 to be able to flush*/
if( unlikely( !p_aout_buf ) )
{
msg_Dbg(p_enc,"Flushing..");
do {
- p_block = block_New( p_enc, p_sys->i_buffer_out );
+ AVPacket packet;
+ p_block = block_Alloc( p_sys->i_buffer_out );
av_init_packet( &packet );
packet.data = p_block->p_buffer;
packet.size = p_block->i_buffer;
i_out = avcodec_encode_audio2( p_sys->p_context, &packet, NULL, &got_packet );
+ p_block->i_buffer = packet.size;
+
+ p_block->i_length = (mtime_t)1000000 *
+ (mtime_t)p_sys->i_frame_size /
+ (mtime_t)p_sys->p_context->sample_rate;
+
+ p_block->i_dts = p_block->i_pts = packet.pts;
+
if( !i_out && got_packet )
block_ChainAppend( &p_chain, p_block );
} while( got_packet && !i_out );
return p_chain;
}
- frame = avcodec_alloc_frame();
+ while( i_samples_left >= p_sys->i_frame_size )
+ {
+ AVPacket packet = {0};
+ frame = avcodec_alloc_frame();
- frame->nb_samples = p_aout_buf->i_nb_samples;
- avcodec_fill_audio_frame( frame, p_enc->fmt_in.audio.i_channels,
- p_sys->p_context->sample_fmt,
- p_aout_buf->p_buffer, p_aout_buf->i_buffer,
- 0);
+ frame->nb_samples = p_sys->i_frame_size;
+ frame->format = p_sys->p_context->sample_fmt;
+ if( av_sample_fmt_is_planar( p_sys->p_context->sample_fmt ) )
+ {
+ aout_Deinterleave( p_sys->p_buffer, p_aout_buf->p_buffer+i_data_offset,
+ p_sys->i_frame_size, p_enc->fmt_in.audio.i_channels, p_enc->fmt_in.i_codec );
+ if( avcodec_fill_audio_frame( frame, p_enc->fmt_in.audio.i_channels,
+ p_sys->p_context->sample_fmt,
+ p_sys->p_buffer,
+ p_sys->i_frame_size * p_sys->i_sample_bytes * p_enc->fmt_in.audio.i_channels,
+ 0) < 0 )
+ msg_Err( p_enc, "filling error on encode" );
+ } else {
+ if( avcodec_fill_audio_frame( frame, p_enc->fmt_in.audio.i_channels,
+ p_sys->p_context->sample_fmt,
+ p_aout_buf->p_buffer+i_data_offset,
+ p_sys->i_frame_size * p_sys->i_sample_bytes * p_enc->fmt_in.audio.i_channels,
+ 0) < 0 )
+ msg_Err( p_enc, "filling error on encode" );
+ }
- frame->pts = p_aout_buf->i_pts;
+ i_samples_left -= p_sys->i_frame_size;
+ i_data_offset += p_sys->i_frame_size * p_sys->i_sample_bytes * p_enc->fmt_in.audio.i_channels;
- p_block = block_New( p_enc, p_sys->i_buffer_out );
- av_init_packet( &packet );
- packet.data = p_block->p_buffer;
- packet.size = p_block->i_buffer;
+ frame->pts = p_aout_buf->i_pts;
- i_out = avcodec_encode_audio2( p_sys->p_context, &packet, frame, &got_packet );
- p_block->i_buffer = packet.size;
-
- av_freep( &frame );
- if( unlikely( !got_packet || i_out ) )
- {
- if( i_out )
- msg_Err( p_enc,"Encoding problem..");
- block_Release( p_block );
- return NULL;
- }
+ p_block = block_Alloc( p_sys->i_buffer_out );
+ av_init_packet( &packet );
+ packet.data = p_block->p_buffer;
+ packet.size = p_block->i_buffer;
- p_block->i_buffer = packet.size;
+ i_out = avcodec_encode_audio2( p_sys->p_context, &packet, frame, &got_packet );
+ p_block->i_buffer = packet.size;
- p_block->i_length = (mtime_t)1000000 *
- (mtime_t)p_sys->i_frame_size /
- (mtime_t)p_sys->p_context->sample_rate;
+ if( frame->extended_data != frame->data )
+ av_freep( frame->extended_data );
+ av_freep( &frame );
+ if( unlikely( !got_packet || ( i_out < 0 ) ) )
+ {
+ if( i_out < 0 )
+ {
+ msg_Err( p_enc,"Encoding problem..");
+ return p_chain;
+ }
+ block_Release( p_block );
+ continue;
+ }
- p_block->i_dts = p_block->i_pts = packet.pts;
+ p_block->i_buffer = packet.size;
- return p_block;
+ p_block->i_length = (mtime_t)1000000 *
+ (mtime_t)p_sys->i_frame_size /
+ (mtime_t)p_sys->p_context->sample_rate;
+
+ p_block->i_dts = p_block->i_pts = packet.pts;
+
+ block_ChainAppend( &p_chain, p_block );
+ }
+ if( i_samples_left < 0 )
+ msg_Err( p_enc, "I_data_left overflow");
+
+ // We have leftover samples that don't fill frame_size, and libavcodec doesn't seem to like
+ // that frame has more data than p_sys->i_frame_size most of the cases currently.
+ if( i_samples_left > 0 )
+ {
+ if( av_sample_fmt_is_planar( p_sys->p_context->sample_fmt ) )
+ aout_Deinterleave( p_sys->p_buffer, p_aout_buf->p_buffer+i_data_offset, i_samples_left, p_enc->fmt_in.audio.i_channels, p_enc->fmt_in.i_codec );
+ else
+ memcpy( p_sys->p_buffer, p_aout_buf->p_buffer+i_data_offset , i_samples_left*p_sys->i_sample_bytes*p_enc->fmt_in.audio.i_channels);
+ p_sys->i_samples_delay = i_samples_left;
+ }
+
+ return p_chain;
}
/*****************************************************************************