[Theora-dev] [patch] more ffmpeg2theora improvements

David Kuehling dvdkhlng at gmx.de
Sun Dec 19 05:15:28 PST 2004


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 at 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;


More information about the Theora-dev mailing list