Files
veejay/veejay-current/libel/vj-avcodec.c
Niels Elburg 0710e393b5 bugfixes
git-svn-id: svn://code.dyne.org/veejay/trunk@815 eb8d1916-c9e9-0310-b8de-cf0c9472ead5
2007-02-28 14:40:21 +00:00

482 lines
12 KiB
C

/* veejay - Linux VeeJay
* (C) 2002-2004 Niels Elburg <nelburg@looze.net>
*
*
* 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
* (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.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include <libel/vj-avcodec.h>
#include <libel/vj-el.h>
#include <libvjmsg/vj-common.h>
#include <libvjmem/vjmem.h>
#include <stdint.h>
#include <string.h>
#include <libyuv/yuvconv.h>
#include <liblzo/lzo.h>
#ifdef SUPPORT_READ_DV2
#define __FALLBACK_LIBDV
#include <libel/vj-dv.h>
#endif
#include <ffmpeg/avcodec.h>
#define YUV420_ONLY_CODEC(id) ( ( id == CODEC_ID_MJPEG || id == CODEC_ID_MJPEGB || id == CODEC_ID_MSMPEG4V3 || id == CODEC_ID_MPEG4) ? 1: 0)
static int out_pixel_format = FMT_420;
char* vj_avcodec_get_codec_name(int codec_id )
{
char name[20];
switch(codec_id)
{
case CODEC_ID_MJPEG: sprintf(name, "MJPEG"); break;
case CODEC_ID_MPEG4: sprintf(name, "MPEG4"); break;
case CODEC_ID_MSMPEG4V3: sprintf(name, "DIVX"); break;
case CODEC_ID_DVVIDEO: sprintf(name, "DVVideo"); break;
case 999 : sprintf(name, "RAW YUV 4:2:0 Planar"); break;
case 998 : sprintf(name, "RAW YUV 4:2:2 Planar"); break;
case 900 : sprintf(name, "LZO YUV 4:2:2 Planar"); break;
default:
sprintf(name, "Unknown"); break;
}
char *res = strdup(name);
return res;
}
static vj_encoder *vj_avcodec_new_encoder( int id, editlist *el, int pixel_format)
{
vj_encoder *e = (vj_encoder*) vj_malloc(sizeof(vj_encoder));
if(!e) return NULL;
memset(e, 0, sizeof(vj_encoder));
if( YUV420_ONLY_CODEC(id ))
{
e->data[0] = (uint8_t*) vj_malloc(sizeof(uint8_t) *
el->video_width * el->video_height );
e->data[1] = (uint8_t*) vj_malloc(sizeof(uint8_t) *
el->video_width * el->video_height /2 );
e->data[2] = (uint8_t*) vj_malloc(sizeof(uint8_t) *
el->video_width * el->video_height /2);
memset( e->data[0], 0, el->video_width * el->video_height );
memset( e->data[1], 0, el->video_width * el->video_height/2 );
memset( e->data[2], 0, el->video_width * el->video_height/2 );
}
if( id == 900 )
{
e->lzo = lzo_new();
}
if(id != 998 && id != 999 && id != 900)
{
#ifdef __FALLBACK_LIBDV
if(id != CODEC_ID_DVVIDEO)
{
#endif
e->codec = avcodec_find_encoder( id );
if(!e->codec)
{
char *descr = vj_avcodec_get_codec_name(id);
veejay_msg(VEEJAY_MSG_ERROR, "Cannot find Encoder codec %s", descr );
free(descr);
}
#ifdef __FALLBACK_LIBDV
}
#endif
}
if( id != 998 && id != 999 && id!= 900)
{
#ifdef __FALLBACK_LIBDV
if(id != CODEC_ID_DVVIDEO )
{
#endif
e->context = avcodec_alloc_context();
e->context->bit_rate = 2750 * 1024;
e->context->width = el->video_width;
e->context->height = el->video_height;
#if LIBAVCODEC_BUILD > 5010
e->context->time_base = (AVRational) { 1, el->video_fps };
#else
e->context->frame_rate = el->video_fps;
e->context->frame_rate_base = 1;
#endif
e->context->qcompress = 0.0;
e->context->qblur = 0.0;
e->context->max_b_frames = 0;
e->context->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
e->context->flags = CODEC_FLAG_QSCALE;
e->context->gop_size = 0;
e->context->sub_id = 0;
e->context->me_method = 0; // motion estimation algorithm
e->context->workaround_bugs = FF_BUG_AUTODETECT;
e->context->prediction_method = 0;
e->context->dct_algo = FF_DCT_AUTO; //global_quality?
switch(pixel_format)
{
case FMT_420:
e->context->pix_fmt = PIX_FMT_YUV420P;
break;
case FMT_420F:
e->context->pix_fmt = PIX_FMT_YUVJ420P;
break;
case FMT_422F:
e->context->pix_fmt = PIX_FMT_YUVJ422P;
break;
default:
e->context->pix_fmt = PIX_FMT_YUV422P;
break;
}
char *descr = vj_avcodec_get_codec_name( id );
if ( avcodec_open( e->context, e->codec ) < 0 )
{
veejay_msg(VEEJAY_MSG_DEBUG, "Cannot open codec '%s'" , descr );
if(e) free(e);
if(descr) free(descr);
return NULL;
}
else
{
veejay_msg(VEEJAY_MSG_DEBUG, "\tOpened encoder %s", descr );
free(descr);
}
#ifdef __FALLBACK_LIBDV
}
#endif
}
e->len = el->video_width * el->video_height;
if(el->pixel_format == FMT_422 || el->pixel_format == FMT_422F)
e->uv_len = e->len / 2;
else
e->uv_len = e->len / 4;
e->width = el->video_width;
e->height = el->video_height;
e->out_fmt = el->pixel_format;
e->encoder_id = id;
/*
if( el->has_audio )
{
e->audiocodec = avcodec_find_encoder( CODEC_ID_PCM_U8 );
if(!e->audiocodec)
{
veejay_msg(VEEJAY_MSG_ERROR, "Error initializing audio codec");
if(e) free(e);
}
e->context->sample_rate = el->audio_rate;
e->context->channels = el->audio_chans;
if( avcodec_open( e->context, e->audiocodec ) < 0)
{
veejay_msg(VEEJAY_MSG_ERROR, "Cannot open audio context");
if(e) free(e);
return NULL;
}
}
*/
return e;
}
void vj_avcodec_close_encoder( vj_encoder *av )
{
if(av)
{
if(av->context)
{
avcodec_close( av->context );
free(av->context);
}
if(av->data[0])
free(av->data[0]);
if(av->data[1])
free(av->data[1]);
if(av->data[2])
free(av->data[2]);
if(av->lzo)
lzo_free(av->lzo);
free(av);
}
av = NULL;
}
int vj_avcodec_find_codec( int encoder )
{
switch( encoder)
{
case ENCODER_MJPEG:
case ENCODER_QUICKTIME_MJPEG:
return CODEC_ID_MJPEG;
case ENCODER_DVVIDEO:
case ENCODER_QUICKTIME_DV:
return CODEC_ID_DVVIDEO;
case ENCODER_YUV420:
return 999;
case ENCODER_YUV422:
return 998;
case ENCODER_MPEG4:
return CODEC_ID_MPEG4;
case ENCODER_DIVX:
return CODEC_ID_MSMPEG4V3;
case ENCODER_LZO:
return 900;
default:
veejay_msg(VEEJAY_MSG_DEBUG, "Unknown format %d selected", encoder );
return 0;
}
return 0;
}
int vj_avcodec_stop( void *encoder , int fmt)
{
if(!encoder)
return 0;
#ifdef SUPPORT_READ_DV2
if( fmt == CODEC_ID_DVVIDEO )
{
vj_dv_free_encoder(encoder);
encoder = NULL;
return 1;
}
#endif
if( fmt == 900 )
{
return 1;
}
vj_encoder *env = (vj_encoder*) encoder;
vj_avcodec_close_encoder( env );
encoder = NULL;
return 1;
}
void *vj_avcodec_start( editlist *el, int encoder )
{
int codec_id = vj_avcodec_find_codec( encoder );
void *ee = NULL;
#ifdef SUPPORT_READ_DV2
if(codec_id == CODEC_ID_DVVIDEO )
{
if(!is_dv_resolution(el->video_width, el->video_height ))
{
veejay_msg(VEEJAY_MSG_ERROR,"\tVideo dimensions do not match required resolution");
return NULL;
}
else
{
ee = (void*)vj_dv_init_encoder( (void*)el , out_pixel_format);
return ee;
}
}
#else
if( codec_id == CODEC_ID_DVVIDEO )
return NULL;
#endif
ee = vj_avcodec_new_encoder( codec_id, el , encoder );
if(!ee)
{
veejay_msg(VEEJAY_MSG_ERROR, "\tFailed to start encoder %x",encoder);
return NULL;
}
return ee;
}
int vj_avcodec_init( int pixel_format)
{
out_pixel_format = pixel_format;
//av_log_set_level( AV_LOG_INFO );
avcodec_init();
avcodec_register_all();
veejay_msg(VEEJAY_MSG_INFO, "FFmpeg AVCodec initialized (http://ffmpeg.sourceforge.net)");
return 1;
}
int vj_avcodec_free()
{
return 1;
}
static void long2str(unsigned char *dst, int32_t n)
{
dst[0] = (n )&0xff;
dst[1] = (n>> 8)&0xff;
dst[2] = (n>>16)&0xff;
dst[3] = (n>>24)&0xff;
}
static int vj_avcodec_lzo( vj_encoder *av, uint8_t *src[3], uint8_t *dst , int buf_len )
{
uint8_t *dstI = dst + (3 * 4);
int size1 = 0, size2=0,size3=0;
int i;
i = lzo_compress( av->lzo, src[0], dstI, &size1 , av->len);
if( i == 0 )
{
veejay_msg(0,"\tunable to compress Y plane");
return 0;
}
dstI += size1;
i = lzo_compress( av->lzo, src[1], dstI, &size2 , av->uv_len );
if( i == 0 )
{
veejay_msg(0,"\tunable to compress U plane");
return 0;
}
dstI += size2;
i = lzo_compress( av->lzo, src[2], dstI, &size3 , av->uv_len );
if( i == 0 )
{
veejay_msg(0,"\tunable to compress V plane");
return 0;
}
long2str( dst, size1 );
long2str( dst+4,size2);
long2str( dst+8,size3);
return (size1 + size2 + size3 + 12);
}
static int vj_avcodec_copy_frame( vj_encoder *av, uint8_t *src[3], uint8_t *dst )
{
if(!av)
{
veejay_msg(VEEJAY_MSG_ERROR, "No encoder !!");
return 0;
}
if( (av->encoder_id == 999 && (av->out_fmt == FMT_420 ||av->out_fmt == FMT_420F)) || (av->encoder_id == 998 && (av->out_fmt == FMT_422||av->out_fmt == FMT_422F)))
{
/* copy */
veejay_memcpy( dst, src[0], av->len );
veejay_memcpy( dst+(av->len), src[1], av->uv_len );
veejay_memcpy( dst+(av->len+av->uv_len) , src[2], av->uv_len);
return ( av->len + av->uv_len + av->uv_len );
}
/* copy by converting */
if( av->encoder_id == 999 && (av->out_fmt == FMT_422 || av->out_fmt==FMT_422F))
{
VJFrame *srci= yuv_yuv_template( src[0],src[1],src[2], av->width,av->height, get_ffmpeg_pixfmt( av->out_fmt));
VJFrame *dsti= yuv_yuv_template( dst,dst+av->len,dst+av->len+(av->len/4), av->width,av->height, PIX_FMT_YUV420P );
yuv_convert_any( srci,dsti, srci->format, dsti->format );
free(srci);
free(dsti);
return ( av->len + (av->len/4) + (av->len/4));
}
if( av->encoder_id == 998 && (av->out_fmt == FMT_420||av->out_fmt==FMT_420F))
{
VJFrame *srci = yuv_yuv_template( src[0],src[1],src[2], av->width,av->height,get_ffmpeg_pixfmt(av->out_fmt));
VJFrame *dsti = yuv_yuv_template( dst, dst + av->len, dst + (av->len + (av->len/2)),
av->width,av->height, PIX_FMT_YUV422P);
free(srci);
free(dsti);
return ( av->len + av->len );
}
return 0;
}
int vj_avcodec_encode_frame(void *encoder, int nframe,int format, uint8_t *src[3], uint8_t *buf, int buf_len)
{
AVFrame pict;
int res=0;
memset( &pict, 0, sizeof(pict));
if(format == ENCODER_LZO )
return vj_avcodec_lzo( encoder, src, buf, buf_len );
if(format == ENCODER_YUV420 || format == ENCODER_YUV422) // no compression, just copy
return vj_avcodec_copy_frame( encoder,src, buf );
#ifdef __FALLBACK_LIBDV
if(format == ENCODER_DVVIDEO || format == ENCODER_QUICKTIME_DV )
return vj_dv_encode_frame( encoder,src, buf );
#endif
vj_encoder *av = (vj_encoder*) encoder;
pict.quality = 1;
pict.pts = (int64_t)( (int64_t)nframe );
veejay_msg(0, "context pixfmt = %d, out_format = %d",
av->context->pix_fmt, get_ffmpeg_pixfmt( out_pixel_format ));
int src_fmt = get_ffmpeg_pixfmt( out_pixel_format );
if(av->context->pix_fmt != src_fmt )
{
pict.data[0] = av->data[0];
pict.data[1] = av->data[1];
pict.data[2] = av->data[2];
pict.linesize[0] = av->context->width;
pict.linesize[1] = av->context->width /2;
pict.linesize[2] = av->context->width /2;
VJFrame *srci = yuv_yuv_template( src[0],src[1],src[2], av->context->width,av->context->height, src_fmt );
VJFrame *dsti = yuv_yuv_template( av->data[0],av->data[1],av->data[2],av->context->width,av->context->height,
av->context->pix_fmt );
yuv_convert_any( srci,dsti, srci->format, dsti->format );
free(srci);
free(dsti);
}
else
{
pict.data[0] = src[0];
pict.data[1] = src[1];
pict.data[2] = src[2];
pict.linesize[0] = av->context->width;
pict.linesize[1] = pict.linesize[0]/2;
pict.linesize[2] = pict.linesize[0]/2;
}
res = avcodec_encode_video( av->context, buf, buf_len, &pict );
return res;
}
int vj_avcodec_encode_audio( void *encoder, int format, uint8_t *src, uint8_t *dst, int len, int nsamples )
{
if(format == ENCODER_YUV420 || ENCODER_YUV422 == format)
return 0;
vj_encoder *av = encoder;
int ret = avcodec_encode_audio( av->context, src, len, nsamples );
return ret;
}