Files
veejay/veejay-ng/libel/vj-avcodec.c
Niels Elburg 932f69e9f3 recover from hdd crash in veejay-ng
git-svn-id: svn://code.dyne.org/veejay/trunk@813 eb8d1916-c9e9-0310-b8de-cf0c9472ead5
2007-02-25 21:05:15 +00:00

422 lines
9.4 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.
*/
/** \defgroup avcodec FFmpeg AVCodec
*/
#include <config.h>
#include <stdint.h>
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avutil.h>
#include <libel/vj-avcodec.h>
#include <libel/vj-el.h>
#include <libvjmsg/vj-common.h>
#include <libvjmem/vjmem.h>
#include <string.h>
#include <libyuv/yuvconv.h>
#include <veejay/defs.h>
#ifdef SUPPORT_READ_DV2
#define __FALLBACK_LIBDV
#include <libel/vj-dv.h>
static vj_dv_encoder *dv_encoder = NULL;
#endif
//@@ FIXME
typedef struct
{
AVCodec *codec;
AVCodec *audiocodec;
AVFrame *frame;
AVCodecContext *context;
int out_fmt;
int uv_len;
int uv_width;
int len;
void *sampler;
int sampling_mode;
int encoder_id;
int width;
int height;
int64_t time_unit;
} vj_encoder;
#ifdef STRICT_CHECKING
#include <assert.h>
#endif
#define YUV420_ONLY_CODEC(id) ( ( id == CODEC_ID_MJPEG || id == CODEC_ID_MJPEGB || id == CODEC_ID_MSMPEG4V3 || id == CODEC_ID_MPEG4 ) ? 1: 0)
#define CODEC_ID_YUV420 998
#define CODEC_ID_YUV422 999
#define CODEC_ID_YUV444 1000
static struct
{
int encoder_id;
int avcodec_id;
char *name;
} encoder_list_[] = {
{ ENCODER_MJPEG, CODEC_ID_MJPEG, "Motion JPEG" },
{ ENCODER_MJPEGB, CODEC_ID_MJPEGB, "MJPEGB" },
{ ENCODER_DVVIDEO, CODEC_ID_DVVIDEO, "Digital Video" },
{ ENCODER_DIVX, CODEC_ID_MSMPEG4V3 , "Divx 3;-)"},
{ ENCODER_YUV420, 998, "YUV 4:2:0 planar" },
{ ENCODER_YUV422, 999, "YUV 4:2:2 planar" },
{ ENCODER_YUV444, 1000, "YUV 4:4:4 planar" },
{ ENCODER_LOSSLESS, CODEC_ID_LJPEG, "Lossless JPEG" },
{ ENCODER_HUFFYUV, CODEC_ID_HUFFYUV, "Lossless HuffYUV" },
{ ENCODER_MPEG4, CODEC_ID_MPEG4, "MPEG4" },
{ -1,-1, NULL }
};
static int get_codec_id( int id )
{
int i;
for( i =0; encoder_list_[i].encoder_id != -1 ; i ++ )
{
if( encoder_list_[i].encoder_id == id )
return encoder_list_[i].avcodec_id;
}
return -1;
}
char* get_codec_name(int id )
{
int i;
for( i =0; encoder_list_[i].encoder_id != -1 ; i ++ )
{
if( encoder_list_[i].encoder_id == id )
return encoder_list_[i].name;
}
return NULL;
}
void *vj_avcodec_new_encoder( int id, int w, int h, int pixel_format, double dfps)
{
int avcodec_id = get_codec_id( id );
char *descr = get_codec_name( id );
float fps = (float) dfps;
int sampling = 0;
if( avcodec_id == -1 )
{
veejay_msg(0, "Invalid codec '%d'", id );
return NULL;
}
vj_encoder *e = (vj_encoder*) vj_malloc(sizeof(vj_encoder));
if(!e) return NULL;
memset(e, 0, sizeof(vj_encoder));
//@quality bad!!
if( id != ENCODER_YUV420 && id != ENCODER_YUV422 && id != ENCODER_YUV444)
{
e->codec = avcodec_find_encoder( avcodec_id );
if(!e->codec )
{
free(e);
veejay_msg(0, "Cannot open codec '%s'",
descr );
return NULL;
}
e->context = avcodec_alloc_context();
// e->context->bit_rate = 5750 * 1024;
e->context->max_b_frames =0;
e->context->width = w;
e->context->height = h;
e->context->time_base.den = 1;
e->context->time_base.num = fps; // = (AVRational) { 1, fps };
e->context->qcompress = 0.0;
e->context->qblur = 0.0;
e->context->flags = CODEC_FLAG_QSCALE;
e->context->gop_size = 0;
e->context->b_frame_strategy = 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?
e->context->global_quality = 1;
e->context->strict_std_compliance = FF_COMPLIANCE_INOFFICIAL;
e->time_unit = 1000000 / e->context->time_base.num;
switch( avcodec_id )
{
case CODEC_ID_MJPEG:
case CODEC_ID_MJPEGB:
case CODEC_ID_LJPEG:
e->context->pix_fmt = PIX_FMT_YUVJ420P;
if( pixel_format != FMT_420 )
sampling = 1;
break;
case CODEC_ID_MPEG4:
case CODEC_ID_MSMPEG4V3:
e->context->pix_fmt = PIX_FMT_YUV420P;
if( pixel_format != FMT_420 )
sampling = 1;
break;
case CODEC_ID_HUFFYUV:
if(pixel_format == FMT_422 ) {
e->context->pix_fmt = PIX_FMT_YUV422P;
} else if ( pixel_format == FMT_420 ) {
e->context->pix_fmt = PIX_FMT_YUV420P;
} else if ( pixel_format == FMT_444 ) {
e->context->pix_fmt = PIX_FMT_YUV422P;
sampling = 1;
}
break;
}
if ( avcodec_open( e->context, e->codec ) < 0 )
{
free(e->context);
free( e );
return NULL;
}
}
switch( avcodec_id )
{
case CODEC_ID_YUV420:
case CODEC_ID_YUV422:
if( pixel_format == FMT_444 )
sampling = 1;
break;
case CODEC_ID_YUV444:
if( pixel_format != FMT_444 )
{
veejay_msg(0, "Please run veejay -P2 for YUV 4:4:4 planar support");
free(e);
return NULL;
}
break;
}
if(sampling)
{
e->sampler = subsample_init_copy( w,h );
switch(pixel_format)
{
case FMT_444:
e->sampling_mode = SSM_422_444;
e->uv_width = w;
break;
case FMT_422:
e->sampling_mode = SSM_420_422;
e->uv_width = w;
break;
default:
e->uv_width =w /2;
break;
}
}
if( avcodec_id == CODEC_ID_YUV420 )
{
switch( pixel_format )
{
case FMT_422:
e->sampling_mode = SSM_420_422;
break;
case FMT_444:
e->sampling_mode = SSM_420_JPEG_BOX;
break;
}
} else if (avcodec_id == CODEC_ID_YUV422 )
{
switch( pixel_format )
{
case FMT_420:
e->sampling_mode = SSM_420_422;
break;
case FMT_444:
e->sampling_mode = SSM_420_422;
break;
}
}
e->len = ( w * h );
switch( pixel_format )
{
case FMT_444:
e->uv_len = e->len; break;
case FMT_422:
e->uv_len = e->len/2; break;
case FMT_420:
e->uv_len = e->len/4;break;
}
e->width = w;
e->height = h;
e->out_fmt = pixel_format;
e->encoder_id = avcodec_id;
return (void*) e;
}
void vj_avcodec_close_encoder( vj_encoder *av )
{
if(av)
{
if(av->context)
{
avcodec_close( av->context );
free(av->context);
if(av->sampler)
subsample_free(av->sampler);
}
free(av);
}
av = NULL;
}
static int vj_avcodec_copy_frame( vj_encoder *av, uint8_t *src[3], uint8_t *dst )
{
uint8_t *yuv[3];
if(!av)
{
veejay_msg(VEEJAY_MSG_ERROR, "No encoder !!");
return 0;
}
if (av->encoder_id == CODEC_ID_YUV420 )
{
switch( av->out_fmt )
{
case FMT_420:
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);
break;
case FMT_422:
case FMT_444:
chroma_subsample_copy( av->sampling_mode,
av->sampler,
src,
av->width,
av->height,
yuv );
veejay_memcpy( dst, yuv[0], av->len );
veejay_memcpy( dst+av->len, yuv[1], av->len/4 );
veejay_memcpy( dst+av->len+(av->len/4),yuv[2], av->len/4);
return (av->len + (av->len/4) + (av->len/4) );
break;
}
}
else if ( av->encoder_id == CODEC_ID_YUV422 )
{
switch(av->out_fmt)
{
case FMT_422:
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);
case FMT_444:
case FMT_420:
chroma_subsample_copy( av->sampling_mode,
av->sampler,
src,
av->width,
av->height,
yuv );
veejay_memcpy( dst, yuv[0], av->len );
veejay_memcpy( dst+av->len, yuv[1], av->len/2 );
veejay_memcpy( dst+av->len+(av->len/2),yuv[2], av->len/2);
return (av->len + (av->len/2) + (av->len/2) );
}
} else if( av->encoder_id == CODEC_ID_YUV444 ){
switch(av->out_fmt)
{
case FMT_444:
veejay_memcpy( dst, src[0], av->len );
veejay_memcpy( dst + av->len, src[1], av->uv_len );
veejay_memcpy( dst + av->len + av->len, src[2], av->uv_len );
return (av->len + av->uv_len + av->uv_len);
default:
#ifdef STRICT_CHECKING
assert(0);
#endif
break;
}
}
return 0;
}
int vj_avcodec_encode_frame( void *codec, int format, void *dsrc, uint8_t *buf, int buf_len, uint64_t nframe)
{
AVFrame p;
VJFrame *src = (VJFrame*) dsrc;
uint8_t *yuv[3];
vj_encoder *av = (vj_encoder*) codec;
#ifdef STRICT_CHECKING
if( av == NULL )
veejay_msg(0 ,"Invalid format: %d",format );
assert( av != NULL );
#endif
int res=0;
if( av->encoder_id == CODEC_ID_YUV420 || av->encoder_id == CODEC_ID_YUV422 || av->encoder_id == CODEC_ID_YUV444 )
{
return vj_avcodec_copy_frame( av, src->data, buf );
}
memset( &p, 0, sizeof(AVFrame));
if(av->sampler)
{
chroma_subsample_copy( av->sampling_mode,
av->sampler,
src,
av->width,
av->height,
yuv );
}
p.data[0] = yuv[0];
p.data[1] = yuv[1];
p.data[2] = yuv[2];
p.linesize[0] = av->width;
p.linesize[1] = av->uv_width;
p.linesize[2] = av->uv_width;
p.pts = av->time_unit * nframe;
p.quality = 1;
res = avcodec_encode_video( av->context, buf, buf_len, &p );
return res;
}