Hi, the attached patch (against current SVN) adds three new options to ffmpeg2theora: --keyint (set keyframe interval) --smoothness (set the theora_info::sharpness encoding parameter) and --noautosync (disable the new frame dropping/duplicating code). It also fixes a bug with the processing of --cropright that sometimes segfaulted. The sync adjustment code is now tuned to be more reliable. BTW is ffmpeg2theora off-topic to the theora-dev list? Please tell me if these patches were better posted to some other list... David -- GnuPG public key: http://user.cs.tu-berlin.de/~dvdkhlng/dk.gpg Fingerprint: B17A DC95 D293 657B 4205 D016 7DEF 5323 C174 7D40 -------------- next part -------------- Index: ffmpeg2theora.c ==================================================================--- ffmpeg2theora.c (revision 8373) +++ ffmpeg2theora.c (working copy) @@ -1,3 +1,4 @@ +/* -*- tab-width:4;c-file-style:"cc-mode"; -*- */ /* * ffmpeg2theora.c -- Convert ffmpeg supported a/v files to Ogg Theora * Copyright (C) 2003-2004 <j@v2v.cc> @@ -54,6 +55,9 @@ int disable_audio; float audio_quality; int audio_bitrate; + int smoothness; + int keyint; + int noautosync; int output_width; int output_height; double fps; @@ -123,6 +127,9 @@ this->video_bitrate=0; this->audio_quality=0.297;// audio quality 3 this->audio_bitrate=0; + this->smoothness=0; + this->keyint=64; + this->noautosync=0; this->force_input_fps=0; this->aspect_numerator=0; this->aspect_denominator=0; @@ -150,6 +157,21 @@ int64_t frame_number=0; double fps = 0.0; + /* 20041216/DK variables that help us keep track of A/V sync */ + double v_pts_out = 0.0; + double v_pts_in = 0.0; + double a_pts_out = 0.0; + double v_pts_delta = 1e99; + double v_pts_delta_new = 0.0; + double a_pts_delta = 0.0; + double framesync = 0.0; + double was_framesync = 0.0; + int frameadjust = 0; + const double v_pts_smoothness = 8; + double max_good_framesync_change = 0.5; + const double framesync_thresh = 0.6; + int j; + for (i = 0; i < this->context->nb_streams; i++){ AVCodecContext *enc = &this->context->streams[i]->codec; switch (enc->codec_type){ @@ -414,16 +436,18 @@ info.ti.dropframes_p = 0; info.ti.quick_p = 1; info.ti.keyframe_auto_p = 1; - info.ti.keyframe_frequency = 64; - info.ti.keyframe_frequency_force = 64; + info.ti.keyframe_frequency = this->keyint; + info.ti.keyframe_frequency_force = this->keyint; info.ti.keyframe_data_target_bitrate = info.ti.target_bitrate * 1.5; info.ti.keyframe_auto_threshold = 80; info.ti.keyframe_mindistance = 8; info.ti.noise_sensitivity = 1; + info.ti.sharpness = this->smoothness; // range 0-2, 0 sharp, 2 less sharp,less bandwidth - if(info.preset == V2V_PRESET_PREVIEW) + if(info.preset == V2V_PRESET_PREVIEW) { + fprintf (stderr, "WARNING: --v2v-preset overrides --smoothness"); info.ti.sharpness=2; - + } } /* audio settings here */ info.channels = this->channels; @@ -520,14 +544,69 @@ len -= len1; } first=0; + + /* 20041216/DK Check and correct A/V sync */ + /* sometimes negative video-timestamps occur. what do + * they mean? for now we will just ignore them, since + * they would get us out of sync for quite some time, + * especially with the v_pts-smoothing code. */ + was_framesync = framesync; + framesync = 0.0; + + if (pkt.pts >= 0) { + double was_framesync = framesync; + + v_pts_in = (double)pkt.pts / AV_TIME_BASE; + v_pts_delta_new = v_pts_in - v_pts_out; + + /* for the video-streams (MPEG-TS) I tested with, + * video-pts values were non-equidistant, without + * proper smoothing their values, ffmpeg2theora would + * keep dropping and duplicating frames...*/ + if (v_pts_delta == 1e99) + v_pts_delta = v_pts_delta_new; + else + v_pts_delta = ((v_pts_smoothness*v_pts_delta + v_pts_delta_new) / + (v_pts_smoothness+1.0)); + + framesync = ((v_pts_delta - a_pts_delta) * this->fps); + } + + if (this->noautosync) + frameadjust = 0; + else if (fabs (framesync - was_framesync) > max_good_framesync_change) { + fprintf (stderr, "\nNo sync adjust at %.1f, sync change %.2f too large\n", + v_pts_out, framesync-was_framesync, max_good_framesync_change); + frameadjust = 0; + } + else if (framesync < -framesync_thresh) { + fprintf (stderr, "\nDropping frame at %.1f: sync off by %.2f frames\n", + v_pts_out, framesync); + frameadjust = -1; + } + else if (framesync > framesync_thresh) { + fprintf (stderr, "\nDuplicating frame at %.1f: sync off by %.2f frames\n", + v_pts_out, framesync); + frameadjust = 1; + } + else + frameadjust = 0; + //now output_resized - if(theoraframes_add_video(&info, output_resized->data[0], - this->video_x,this->video_y,output_resized->linesize[0],e_o_s)){ - //this->output_width,this->output_height,output_resized->linesize[0],e_o_s)){ - ret = -1; - fprintf (stderr,"No theora frames available\n"); - break; + for (j = 0; j < 1 + frameadjust ; j++) + { + v_pts_out += (1/fps); + if(theoraframes_add_video(&info, output_resized->data[0], + this->video_x,this->video_y,output_resized->linesize[0],e_o_s)){ + //this->output_width,this->output_height,output_resized->linesize[0],e_o_s)){ + ret = -1; + fprintf (stderr,"No theora frames available\n"); + break; + } } + + if (ret == -1) + break; if(e_o_s){ break; } @@ -564,6 +643,9 @@ ret = -1; fprintf (stderr,"No audio frames available\n"); } + /* 20041216/DK keep track of audio timing for A/V sync */ + a_pts_delta = (double)pkt.pts / AV_TIME_BASE - a_pts_out; + a_pts_out += (double)samples_out / this->sample_rate; if(e_o_s && len <= 0){ break; } @@ -670,6 +752,11 @@ "\t --crop[top|bottom|left|right]\tcrop input before resizing\n" "\t --videoquality,-v\t[0 to 10] encoding quality for video\n" "\t --videobitrate,-V\t[45 to 2000] encoding bitrate for video\n" + "\t --smoothness,-S \t[0 to 2] smoothness of images, high values\n" + "\t \trecommended for low-bitrate/high-res video\n" + "\t --keyint,-K \t[8 to 65536] keyframe interval (default: 64)\n" + "\t --noautosync \tdo not adjust sync by duplicating and dropping\n" + "\t \tframes\n" "\t --audioquality,-a\t[-1 to 10] encoding quality for audio\n" "\t --audiobitrate,-A\t[45 to 2000] encoding bitrate for audio\n" "\t --samplerate,-H\tset output samplerate in Hz\n" @@ -727,6 +814,7 @@ static int cropright_flag=0; static int cropleft_flag=0; static int nosound_flag=0; + static int noautosync_flag=0; static int aspect_flag=0; static int inputfps_flag=0; static int metadata_flag=0; @@ -736,7 +824,7 @@ av_register_all (); int c,long_option_index; - const char *optstring = "o:f:x:y:v:V:a:s:e:A:d:H:c:p:N:D:h::"; + const char *optstring = "o:f:x:y:v:V:a:s:e:A:S:F:d:H:c:p:N:D:h::"; struct option options [] = { {"output",required_argument,NULL,'o'}, {"format",required_argument,NULL,'f'}, @@ -746,6 +834,9 @@ {"videobitrate",required_argument,NULL,'V'}, {"audioquality",required_argument,NULL,'a'}, {"audiobitrate",required_argument,NULL,'A'}, + {"smoothness",required_argument,NULL,'S'}, + {"keyint",required_argument,NULL,'K'}, + {"noautosync",0,&noautosync_flag,1}, {"deinterlace",required_argument,NULL,'d'}, {"samplerate",required_argument,NULL,'H'}, {"channels",required_argument,NULL,'c'}, @@ -788,6 +879,10 @@ convert->disable_audio=1; nosound_flag=0; } + if (noautosync_flag) { + convert->noautosync = 1; + noautosync_flag=0; + } /* crop */ if (croptop_flag){ convert->frame_topBand=crop_check(convert,"top",optarg); @@ -799,7 +894,7 @@ } if (cropright_flag){ convert->frame_rightBand=crop_check(convert,"right",optarg); - cropleft_flag=0; + cropright_flag=0; } if (cropleft_flag){ convert->frame_leftBand=crop_check(convert,"left",optarg); @@ -891,7 +986,21 @@ exit(1); } convert->audio_quality=-99; - break; + break; + case 'S': + convert->smoothness = atoi(optarg); + if (convert->smoothness < 0 || convert->smoothness > 2) { + fprintf (stderr, "Illegal smoothness (valid: 0..2)\n"); + exit(1); + } + break; + case 'K': + convert->keyint = atoi(optarg); + if (convert->keyint < 8 || convert->keyint > 65536) { + fprintf (stderr, "Illegal keyframe interval (valid: 8..65536)\n"); + exit(1); + } + break; case 'd': if(!strcmp(optarg,"off")) convert->deinterlace=0;