/* veejay - Linux VeeJay * (C) 2002-2004 Niels Elburg * * * 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 #include #include #include #include #include #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; }