Files
veejay/veejay-current/libel/vj-avformat.c
Niels Elburg a276f3bce8 bugfixes
git-svn-id: svn://code.dyne.org/veejay/trunk@787 eb8d1916-c9e9-0310-b8de-cf0c9472ead5
2007-02-12 21:10:30 +00:00

431 lines
10 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 <stdint.h>
#include <libel/vj-avformat.h>
#include <libvjmsg/vj-common.h>
#include <string.h>
#include <libel/vj-el.h>
#if LIBAVFORMAT_BUILD >= 4620
#define m_av_seek_frame( a,b,c,d ) \
( av_seek_frame( a,b,c,d ) )
#else
#define m_av_seek_frame(a,b,c,d ) \
( av_seek_frame( a,b,c ) )
#endif
void vj_avformat_init(void)
{
av_register_all();
av_log_set_level(AV_LOG_QUIET);
}
static int64_t vj_avformat_get_timestamp(vj_avformat *av, long nframe )
{
int64_t ret = ((int64_t)av->time_unit * (int64_t)nframe); // duration of frame in microseconds * nframe
return ret;
}
/*
static int64_t vj_avformat_get_master_clock(vj_avformat *av)
{
int64_t delta = ( av_gettime() - av->current_video_pts_time) / 1000000.0;
return (av->current_video_pts + delta);
}
*/
static int vj_avformat_set_video_position( vj_avformat *av, long nframe )
{
int64_t pos = vj_avformat_get_timestamp(av, nframe );
if( av->seekable )
{
if(m_av_seek_frame( av->context, av->video_index, pos,0 )==0)
{
return 1;
}
return 0;
}
return 0;
}
static void vj_avformat_err(int err, char *arg)
{
switch(err)
{
case AVERROR_NUMEXPECTED:
veejay_msg(VEEJAY_MSG_ERROR, "Incorrect image filename syntax.");
break;
case AVERROR_INVALIDDATA:
veejay_msg(VEEJAY_MSG_ERROR, "Error while parsing header");
break;
case AVERROR_NOFMT:
veejay_msg(VEEJAY_MSG_ERROR, "Unknown format");
break;
default:
veejay_msg(VEEJAY_MSG_ERROR,"Error while opening file '%s'",arg);
break;
}
}
int vj_avformat_get_audio_rate(vj_avformat *av)
{
return (int) av->audiocct->sample_rate;
}
int vj_avformat_get_audio_channels(vj_avformat *av)
{
return (int) av->audiocct->channels;
}
int vj_avformat_get_video_width(vj_avformat *av)
{
return (int)av->cct->width;
}
int vj_avformat_get_video_height(vj_avformat *av)
{
return (int)av->cct->height;
}
int vj_avformat_get_video_inter(vj_avformat *av)
{
return 0;
}
int vj_avformat_get_video_pixfmt(vj_avformat *av)
{
if(!av->cct) return -1;
return (int) (av->cct->pix_fmt);
}
int vj_avformat_get_video_codec(vj_avformat *av)
{
if(!av->cct) return -1;
return (int) (av->cct->codec_id);
}
int vj_avformat_get_video_gop_size(vj_avformat *av)
{
if(!av->cct) return -1;
return (int) (av->cct->gop_size);
}
float vj_avformat_get_video_fps(vj_avformat *av)
{
return (float) (av->frame_rate/av->frame_rate_base);
}
float vj_avformat_get_sar_ratio( vj_avformat *av )
{
return 0.5;
}
long vj_avformat_get_video_frames( vj_avformat *av )
{
return (long)( (double)av->stream->duration / 1345.2944 );
}
vj_avformat *vj_avformat_open_input(const char *filename)
{
int i;
vj_avformat *av = (vj_avformat*) vj_malloc(sizeof(struct vj_avformat_t));
//AVFormatParameters *ap = av->av_format_par;
int err;
if(!av) return NULL;
av->av_input_format = NULL;
av->av_format_par = NULL;
av->stream = NULL;
av->cct = NULL;
av->start_time = AV_NOPTS_VALUE;
av->time_unit = 0;
av->video_clock = 0;
av->current_video_pts_time = 0;
av->current_video_pts = 0;
av->video_last_P_pts = 0;
av->expected_timecode = 0;
av->video_index = -1;
av->audio_index = -1;
/*
if( strcasecmp(filename, "/dev/dv1394" )==0)
{
av->av_input_format = av_find_input_format("dv1394");
av->av_format_par = (AVFormatParameters*) vj_malloc(sizeof(AVFormatParameters));
av->av_format_par->width = 720;
av->av_format_par->height = 576;
av->av_format_par->frame_rate = 25;
av->av_format_par->frame_rate_base=1;
av->av_format_par->device = filename;
av->av_format_par->standard = "PAL";
av->av_format_par->channel = 0;
err = av_open_input_file(
&(av->context),
filename,
av->av_input_format,
0,
av->av_format_par );
}
else
{
*/ err = av_open_input_file(
&(av->context),
filename,
av->av_input_format,
0,
av->av_format_par );
// }
if(err < 0 )
{
//vj_avformat_err(err,filename);
free(av);
return NULL;
}
err = av_find_stream_info( av->context );
if( err < 0 )
{
vj_avformat_err(err,NULL);
}
for(i =0 ; i < av->context->nb_streams; i ++)
{
AVStream *stream = av->context->streams[i];
AVCodecContext *cct = &stream->codec;
if(cct->codec_type == CODEC_TYPE_VIDEO)
{
av->video_index = i;
av->stream = av->context->streams[av->video_index];
av->cct = &stream->codec;
av->codec = avcodec_find_decoder(av->cct->codec_id);
if(!av->codec)
{
veejay_msg(VEEJAY_MSG_DEBUG, "Cannot use AVFormat to open this file");
free(av);
return NULL;
}
if(av->codec->capabilities & CODEC_CAP_TRUNCATED)
av->cct->flags |= CODEC_FLAG_TRUNCATED;
if(avcodec_open( av->cct, av->codec ) < 0)
{
veejay_msg(VEEJAY_MSG_ERROR, "Cannot open codec");
}
av->seekable = 0;
if(strcmp(av->codec->name, "mjpeg")==0)
av->seekable = 1;
if(strcmp(av->codec->name, "dvvideo") == 0)
av->seekable = 1;
#if LIBAVFORMAT_BUILD > 5010
av->frame_rate = av->cct->time_base.den;
av->frame_rate_base = av->cct->time_base.num;
if( av->cct->time_base.den > 1000 && av->cct->time_base.num == 1 )
av->frame_rate_base = 1000;
if( av->cct->time_base.num == 1 && av->cct->time_base.den == 25 )
{
int base = av->cct->time_base.num;
// Assuming codec sets frame_rate and frame_rate_base incorrectly
av->frame_rate = 1000000;
av->frame_rate_base = av->frame_rate / base;
}
if(av->frame_rate > 1000)
av->time_unit = (1000000.0 / (float)av->frame_rate) * av->frame_rate_base;
else
av->time_unit = av->frame_rate_base;
#else
av->frame_rate = av->cct->frame_rate;
av->frame_rate_base = av->cct->frame_rate_base;
/* wrong frame rates that seem to be generated by some codecs */
if( av->cct->frame_rate > 1000 && av->cct->frame_rate_base==1)
{
av->frame_rate_base=1000;
}
if( av->cct->frame_rate_base == 1 && av->cct->frame_rate == 25)
{
int base = av->cct->frame_rate;
// Assuming codec sets frame_rate and frame_rate_base incorrectly
av->frame_rate = 1000000;
av->frame_rate_base = av->frame_rate / base;
}
if(av->frame_rate > 1000)
av->time_unit = (1000000.0 / (float)av->frame_rate) * av->frame_rate_base;
else
av->time_unit = av->frame_rate_base;
#endif
}
if( cct->codec_type == CODEC_TYPE_AUDIO )
{
av->audio_index = i;
av->audio_stream = av->context->streams[av->audio_index];
av->audiocct = &stream->codec;
av->audiocodec = avcodec_find_decoder( av->audiocct->codec_id );
if(!av->audiocodec)
{
veejay_msg(VEEJAY_MSG_DEBUG, "Unknown audio format");
free(av);
return NULL;
}
if(avcodec_open( av->audiocct, av->audiocodec ) < 0 )
{
veejay_msg(VEEJAY_MSG_ERROR, "Cannot open codec");
}
veejay_msg(VEEJAY_MSG_DEBUG,
"Found audio stream at %d, codec %d", i, av->audiocct->codec_id);
}
}
if(av->video_index == -1)
{
if(av) free(av);
return NULL;
}
return av;
}
void vj_avformat_close_input( vj_avformat *av )
{
if(av)
{
av_close_input_file( av->context );
if(av) free(av);
}
}
int vj_avformat_get_video_frame( vj_avformat *av, uint8_t *yuv420[3], long nframe, int fmt )
{
AVPacket packet;
AVFrame frame;
int got_picture = 0;
int ret = 0;
int delay = (int) av->time_unit;
double pts = 0;
// what frame do we want ?
if(nframe == -1)
av->requested_timecode = av->expected_timecode + av->time_unit;
else
av->requested_timecode = vj_avformat_get_timestamp(av,nframe);
if(av->seekable && av->requested_timecode != (av->time_unit + av->expected_timecode) )
{
if( av->requested_timecode != (av->expected_timecode-av->time_unit))
{
if(!vj_avformat_set_video_position( av, nframe ))
{
return 0;
}
}
}
veejay_memset( &packet, 0, sizeof( packet ) );
veejay_memset( &frame, 0, sizeof( packet ) );
while( !got_picture && ret>= 0 )
{
// read one packet from the media and put in in packet
pts = 0;
ret = av_read_frame( av->context, &packet );
if( ret < 0)
{
if( m_av_seek_frame( av->context,av->video_index ,av->context->start_time,0 ) != 0)
return 0;
ret = av_read_frame( av->context, &packet );
if(ret < 0) return 0;
}
if( packet.pts != AV_NOPTS_VALUE )
pts = (double) packet.pts;
// deal with video from video_index
if ( ret >= 0 && packet.stream_index == av->video_index && packet.size > 0)
{
int video_len = packet.size;
int tmp_len = 0;
uint8_t *src_ptr = packet.data;
if(av->cct->codec_id != CODEC_ID_RAWVIDEO)
{
while( video_len > 0)
{
tmp_len = avcodec_decode_video( av->cct, &frame, &got_picture,
src_ptr, video_len );
if(tmp_len < 0)
{
return 0;
}
video_len -= tmp_len;
src_ptr += tmp_len;
}
if(!got_picture)
return 0;
}
else
{
avpicture_fill( (AVPicture *)&frame, src_ptr, av->cct->pix_fmt, av->cct->width, av->cct->height);
frame.pict_type = FF_I_TYPE;
}
if ( pts != 0 )
{
av->expected_timecode = pts;
}
else
{
pts = av->expected_timecode;
}
if ( frame.repeat_pict )
{
delay += ( frame.repeat_pict * delay );
}
//av->expected_timecode += delay;
}
av_free_packet( &packet );
}
if(got_picture)
{
int dst_fmt = get_ffmpeg_pixfmt(fmt);
veejay_msg(0, "img_convert %d -> %d", src_fmt, dst_fmt );
if( av->cct->pix_fmt == PIX_FMT_RGB24 || av->cct->pix_fmt == PIX_FMT_RGBA )
/*img_convert( &pict,
dst_fmt,
(const AVPicture*) &frame,
av->cct->pix_fmt,
av->cct->width,
av->cct->height );*/
}
return 1;
}
int vj_avformat_get_audio( vj_avformat *av, uint8_t *dst, long nframe )
{
return 0;
}