[Theora] ffmpeg2theora start and end time support

Nilesh Bansal bansal at cs.ubc.ca
Sat Nov 20 13:54:46 PST 2004


For last few days I was trying to learn ffmpeg and libtheora API. In the 
process, I have modified ffmpeg2theora code to include support for start 
time and end time.

ffmpeg2theora -s 60 -e 130 file.avi

will produce file.ogg which will be from 60th to 130th second of input 
file (something like -ss and -endpos in mencoder). This is a useful 
feature for someone who wants to cut a part of video and encode it using 
theora.

I am attaching modified ffmpeg2theora.c. Just place this file in file from
http://www.v2v.cc/~j/ffmpeg2theora/

--
Nilesh Bansal
http://www.cse.iitb.ac.in/nilesh/
-------------- next part --------------
/*
 * ffmpeg2theora.c -- Convert ffmpeg supported a/v files to  Ogg Theora
 * Copyright (C) 2003-2004 <j at v2v.cc>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * 
 */
#include "common.h"
#include "avformat.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>

#include "theora/theora.h"
#include "vorbis/codec.h"
#include "vorbis/vorbisenc.h"

#include "theorautils.h"

static double rint(double x)
{
  if (x < 0.0)
    return (double)(int)(x - 0.5);
  else
    return (double)(int)(x + 0.5);
}

theoraframes_info info;

static int using_stdin = 0;

typedef struct ff2theora{
	AVFormatContext *context;
	int video_index;
	int audio_index;
	int deinterlace;
	int sample_rate;
	int channels;
	int disable_audio;
	float audio_quality;
	int output_width;
	int output_height;
	double fps;
	ImgReSampleContext *img_resample_ctx; /* for image resampling/resizing */
	ReSampleContext *audio_resample_ctx;
	ogg_uint32_t aspect_numerator;
	ogg_uint32_t aspect_denominator;
	double	frame_aspect;
	int video_quality;
	
	/* cropping */
	int frame_topBand;
	int frame_bottomBand;
	int frame_leftBand;
	int frame_rightBand;
	
	int video_x;
	int video_y;
	int frame_x_offset;
	int frame_y_offset;

	int start_time; /* In seconds */
	int end_time; /* In seconds */
}
*ff2theora;

/**
 * Allocate and initialise an AVFrame. 
 */
AVFrame *alloc_picture (int pix_fmt, int width, int height){
	AVFrame *picture;
	uint8_t *picture_buf;
	int size;

	picture = avcodec_alloc_frame ();
	if (!picture)
		return NULL;
	size = avpicture_get_size (pix_fmt, width, height);
	picture_buf = av_malloc (size);
	if (!picture_buf){
		av_free (picture);
		return NULL;
	}
	avpicture_fill ((AVPicture *) picture, picture_buf,
			pix_fmt, width, height);
	return picture;
}

/**
 * initialize ff2theora with default values
 * @return ff2theora struct
 */
ff2theora ff2theora_init (){
	ff2theora this = calloc (1, sizeof (*this));
	if (this != NULL){
		this->disable_audio=0;
		this->video_index = -1;
		this->audio_index = -1;
		this->sample_rate = 44100;  // samplerate hmhmhm
		this->channels = 2;
		this->output_width=0;	  // set to 0 to not resize the output
		this->output_height=0;	  // set to 0 to not resize the output
		this->video_quality=31.5; // video quality 5
		this->audio_quality=0.297;// audio quality 3
		this->aspect_numerator=0;
		this->aspect_denominator=0;
		this->frame_aspect=0;
		this->deinterlace=1;
		this->frame_topBand=0;
		this->frame_bottomBand=0;
		this->frame_leftBand=0;
		this->frame_rightBand=0;
		this->start_time=0;
		this->end_time=0; /* ZERO denotes no end time set */
	}
	return this;
}

void ff2theora_output(ff2theora this) {
	int i;
	AVCodecContext *aenc = NULL;
	AVCodecContext *venc = NULL;
	AVStream *astream = NULL;
	AVStream *vstream = NULL;
	AVCodec *acodec = NULL;
	AVCodec *vcodec = NULL;
	AVIndexEntry * index_entry;
	float frame_aspect;
	int frame_number=0;
	double fps = 0.0;

	for (i = 0; i < this->context->nb_streams; i++){
		AVCodecContext *enc = &this->context->streams[i]->codec;
		switch (enc->codec_type){
		case CODEC_TYPE_VIDEO:
			if (this->video_index < 0)
				this->video_index = i;
			break;
		case CODEC_TYPE_AUDIO:
			if (this->audio_index < 0 && !this->disable_audio)
				this->audio_index = i;
			break;
		default:
			break;
		}
	}

	if (this->video_index >= 0){
		vstream = this->context->streams[this->video_index];
		venc = &this->context->streams[this->video_index]->codec;
		vcodec = avcodec_find_decoder (venc->codec_id);

		fps = (double) venc->frame_rate / venc->frame_rate_base;
		if (fps > 10000)
			fps /= 1000;
		
		if (vcodec == NULL || avcodec_open (venc, vcodec) < 0)
			this->video_index = -1;
		this->fps = fps;
		
		if(info.preset == V2V_PRESET_PREVIEW){
			// possible sizes 384/288,320/240
			int pal_width=384;
			int pal_height=288;
			int ntsc_width=320;
			int ntsc_height=240;
			if(this->fps==25 && (venc->width!=pal_width || venc->height!=pal_height) ){
				this->output_width=pal_width;
				this->output_height=pal_height;
			}
			else if(abs(this->fps-30)<1 && (venc->width!=ntsc_width || venc->height!=ntsc_height) ){
				this->output_width=ntsc_width;
				this->output_height=ntsc_height;
			}
		}
		else if(info.preset == V2V_PRESET_PRO){
			if(this->fps==25 && (venc->width!=720 || venc->height!=576) ){
				this->output_width=720;
				this->output_height=576;
			}
			else if(abs(this->fps-30)<1 && (venc->width!=720 || venc->height!=480) ){
				this->output_width=720;
				this->output_height=480;
			}
		}
        if (this->deinterlace==1)
			fprintf(stderr,"  Deinterlace: on\n");
		else
			fprintf(stderr,"  Deinterlace: off\n");

		if(this->output_height==0 && 
			(this->frame_leftBand || this->frame_rightBand || this->frame_topBand || this->frame_bottomBand) ){
			this->output_height=venc->height-
					this->frame_topBand-this->frame_bottomBand;
		}
		if(this->output_width==0 && 
			(this->frame_leftBand || this->frame_rightBand || this->frame_topBand || this->frame_bottomBand) ){
			this->output_width=venc->width-
					this->frame_leftBand-this->frame_rightBand;
		}
		//so frame_aspect is set on the commandline
		if(this->frame_aspect!=0){
				if(this->output_height){
					this->aspect_numerator=10000*this->frame_aspect*this->output_height;
					this->aspect_denominator=10000*this->output_width;
				}
				else{
					this->aspect_numerator=10000*this->frame_aspect*venc->height;
					this->aspect_denominator=10000*venc->width;
				}
				av_reduce(&this->aspect_numerator,&this->aspect_denominator,this->aspect_numerator,this->aspect_denominator,10000);
				frame_aspect=this->frame_aspect;
		}
		if(venc->sample_aspect_ratio.num!=0 && this->frame_aspect==0){
			// just use the ratio from the input
			this->aspect_numerator=venc->sample_aspect_ratio.num;
			this->aspect_denominator=venc->sample_aspect_ratio.den;
			// or we use ratio for the output
			if(this->output_height){
				int width=venc->width-this->frame_leftBand-this->frame_rightBand;
				int height=venc->height-this->frame_topBand-this->frame_bottomBand;
				av_reduce(&this->aspect_numerator,&this->aspect_denominator,
				venc->sample_aspect_ratio.num*width*this->output_height,
				venc->sample_aspect_ratio.den*height*this->output_width,10000);
				frame_aspect=(float)(this->aspect_numerator*this->output_width)/
								(this->aspect_denominator*this->output_height);
			}
			else{
				frame_aspect=(float)(this->aspect_numerator*venc->width)/
								(this->aspect_denominator*venc->height);
			}
			
		}

		if(this->aspect_denominator && frame_aspect){
			//fprintf(stderr,"  Pixel Aspect Ratio: %d/%d ",this->aspect_numerator,this->aspect_denominator);
			fprintf(stderr,"  Pixel Aspect Ratio: %.2f/1 ",(float)this->aspect_numerator/this->aspect_denominator);
			fprintf(stderr,"  Frame Aspect Ratio: %.2f/1\n",frame_aspect);
		}

		
		/* Theora has a divisible-by-sixteen restriction for the encoded video size */  /* scale the frame size up to the nearest /16 and calculate offsets */
		this->video_x=((this->output_width + 15) >>4)<<4;
		this->video_y=((this->output_height + 15) >>4)<<4;
		this->frame_x_offset=(this->video_x-this->output_width);
		this->frame_y_offset=(this->video_y-this->output_height);
		
		if(this->video_x>0 || this->video_y>0){
			// we might need that for values other than /16?
			int frame_padtop=0, frame_padbottom=0;
			int frame_padleft=0, frame_padright=0;
			
			frame_padbottom=this->frame_x_offset;
			frame_padleft=this->frame_y_offset;

			this->img_resample_ctx = img_resample_full_init(
						  //this->output_width, this->output_height,
						  this->video_x, this->video_y,
						  venc->width, venc->height,
						  this->frame_topBand, this->frame_bottomBand,
						  this->frame_leftBand, this->frame_rightBand,
						  frame_padtop, frame_padbottom,
						  frame_padleft, frame_padright
		  	);
			fprintf(stderr,"  Resize: %dx%d",venc->width,venc->height);
			if(this->frame_topBand || this->frame_bottomBand ||
			this->frame_leftBand || this->frame_rightBand){
				fprintf(stderr," => %dx%d",venc->width-this->frame_leftBand- this->frame_rightBand,venc->height-this->frame_topBand-this->frame_bottomBand);
			}
			if(this->output_width!=(venc->width-this->frame_leftBand-this->frame_rightBand) ||
				this->output_height!=(venc->height-this->frame_topBand-this->frame_bottomBand))
				fprintf(stderr," => %dx%d",this->output_width,this->output_height);
			fprintf(stderr,"\n");
			
		}
		else{
			this->video_x = venc->width;
			this->video_y = venc->height;
			this->output_height=venc->height;
			this->output_width=venc->width;
		}
	}
	if (this->audio_index >= 0){
		astream = this->context->streams[this->audio_index];
		aenc = &this->context->streams[this->audio_index]->codec;
		acodec = avcodec_find_decoder (aenc->codec_id);
		if (this->channels != aenc->channels && aenc->codec_id == CODEC_ID_AC3)
			aenc->channels = this->channels;

		if (acodec != NULL && avcodec_open (aenc, acodec) >= 0){
			if(this->sample_rate!=aenc->sample_rate || this->channels!=aenc->channels){
				this->audio_resample_ctx = audio_resample_init (this->channels,aenc->channels,this->sample_rate,aenc->sample_rate);
				if(this->sample_rate!=aenc->sample_rate)
					fprintf(stderr,"  Resample: %dHz => %dHz\n",aenc->sample_rate,this->sample_rate);
				if(this->channels!=aenc->channels)
					fprintf(stderr,"  Channels: %d => %d\n",aenc->channels,this->channels);
			}
			else{
				this->audio_resample_ctx=NULL;
			}
		}
		else{
			this->audio_index = -1;
		}
	}
	
	if (this->video_index >= 0 || this->audio_index >=0){
		AVFrame *frame=NULL;
		AVFrame *frame_tmp=NULL;
		AVFrame *output=NULL;
		AVFrame *output_tmp=NULL;
		AVFrame *output_resized=NULL;
		AVFrame *output_buffered=NULL;
		
		AVPacket pkt;
		int len;
		int len1;
		int got_picture;
		int first = 1;
		int e_o_s=0;
		int ret;
		uint8_t *ptr;
		int16_t *audio_buf= av_malloc(4*AVCODEC_MAX_AUDIO_FRAME_SIZE);
		int16_t *resampled= av_malloc(4*AVCODEC_MAX_AUDIO_FRAME_SIZE);		
		
		if(this->video_index >= 0)
			info.audio_only=0;
		else
			info.audio_only=1;
		
		if(this->audio_index>=0)
			info.video_only=0;
		else
			info.video_only=1;
		
		if(!info.audio_only){
			frame = alloc_picture(vstream->codec.pix_fmt,
							vstream->codec.width,vstream->codec.height);
			frame_tmp = alloc_picture(vstream->codec.pix_fmt,
							vstream->codec.width,vstream->codec.height);
			output_tmp =alloc_picture(PIX_FMT_YUV420P, 
							vstream->codec.width,vstream->codec.height);
			output =alloc_picture(PIX_FMT_YUV420P, 
							vstream->codec.width,vstream->codec.height);
			output_resized =alloc_picture(PIX_FMT_YUV420P, 
							this->video_x, this->video_y);
							//this->output_width,this->output_height);
			output_buffered =alloc_picture(PIX_FMT_YUV420P,
							this->video_x, this->video_y);			
							//this->output_width,this->output_height);
		}

		if(!info.audio_only){
			/* video settings here */
			/* config file? commandline options? v2v presets? */
			

			theora_info_init (&info.ti);
			
			info.ti.width = this->video_x;
			info.ti.height = this->video_y;
			info.ti.frame_width = this->output_width;
			info.ti.frame_height = this->output_height;
			info.ti.offset_x = this->frame_x_offset;
			info.ti.offset_y = this->frame_y_offset;
			// FIXED: looks like ffmpeg uses num and denum for fps too
			// venc->frame_rate / venc->frame_rate_base;
			//info.ti.fps_numerator = 1000000 * (this->fps);	/* fps= numerator/denominator */
			//info.ti.fps_denominator = 1000000;
			info.ti.fps_numerator=venc->frame_rate;
			info.ti.fps_denominator = venc->frame_rate_base;
			/* this is pixel aspect ratio */
			info.ti.aspect_numerator=this->aspect_numerator;
			info.ti.aspect_denominator=this->aspect_denominator;
			// FIXME: is all input material with fps==25 OC_CS_ITU_REC_470BG?
			// guess not, commandline option to select colorspace would be the best.
			if(this->fps==25)
				info.ti.colorspace = OC_CS_ITU_REC_470BG;
			else if(abs(this->fps-30)<1)
				info.ti.colorspace = OC_CS_ITU_REC_470M;
			else
				info.ti.colorspace = OC_CS_UNSPECIFIED;
			//FIXME: allow target_bitrate as an alternative mode
			//only use quality mode for now.
			//info.ti.target_bitrate=1200; 
			info.ti.quality = this->video_quality;
			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_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;
			// range 0-2, 0 sharp, 2 less sharp,less bandwidth
			if(info.preset == V2V_PRESET_PREVIEW)
				info.ti.sharpness=2;
			
		}
		/* audio settings here */
		info.channels = this->channels;
		info.sample_rate = this->sample_rate;
		info.vorbis_quality = this->audio_quality;
		theoraframes_init ();
		printf("AV time base %i\n", AV_TIME_BASE);
		av_seek_frame( this->context, -1, (int64_t)AV_TIME_BASE*this->start_time, 1);
		/* main decoding loop */
		int no_frames = fps*(this->end_time - this->start_time);
		if(no_frames < 0){
			printf("Error -- end time < start time\n");
			exit(1);
		}
		do{
			if(no_frames > 0){
			//	printf("%i \n",frame_number);
				if(frame_number > no_frames){
					printf("\nTime to break\n");
					break;
				}
			}
			ret = av_read_frame(this->context,&pkt);
			if(ret<0){
				e_o_s=1;
			}
		
		
			ptr = pkt.data;
			len = pkt.size;
			if (e_o_s && !info.audio_only || (ret >= 0 && pkt.stream_index == this->video_index)){
				if(len == 0 && !first && !e_o_s){
					fprintf (stderr, "no frame available\n");
				}
				while(e_o_s || len > 0){
					
					if(len >0 &&
						(len1 = avcodec_decode_video(&vstream->codec,
										frame,&got_picture, ptr, len))>0) {
										
						//FIXME: move image resize/deinterlace/colorformat transformation
						//			into seperate function
						if(got_picture){

							frame_number++;
							//For audio only files command line option "-e" will not work
							//as we donot increment frame_number in audio section.

							//FIXME: better colorformat transformation to YUV420P
							/* might have to cast other progressive formats here */
							//if(venc->pix_fmt != PIX_FMT_YUV420P){
								img_convert((AVPicture *)output,PIX_FMT_YUV420P,
											(AVPicture *)frame,venc->pix_fmt,
											venc->width,venc->height);
								if(this->deinterlace){
									if(avpicture_deinterlace((AVPicture *)output_tmp,
											(AVPicture *)output,PIX_FMT_YUV420P,
											venc->width,venc->height)<0){
										output_tmp=output;
									}
								}
								else
									output_tmp=output;
							//}
							//else{
								/* there must be better way to do this, it seems to work like this though */
							/*
								if(frame->linesize[0] != vstream->codec.width){
									img_convert((AVPicture *)output_tmp,PIX_FMT_YUV420P,
												(AVPicture *)frame,venc->pix_fmt,venc->width,venc->height);
								}
								else{
									output_tmp=frame;
								}
							}
							*/
							// now output_tmp
							if(this->img_resample_ctx){
								img_resample(this->img_resample_ctx, 
											(AVPicture *)output_resized, (AVPicture *)output_tmp);
							}	
							else{
								output_resized=output_tmp;
							}
						}
						ptr += len1;
						len -= len1;
					}	
					first=0;
					//now output_resized
					if(theoraframes_add_video(output_resized->data[0],
					this->video_x,this->video_y,output_resized->linesize[0],e_o_s)){
						ret = -1;
						fprintf (stderr,"No theora frames available\n");
						break;
					}
					if(e_o_s){
						break;
					}
				}
				
			}
			if(e_o_s && !info.video_only 
					 || (ret >= 0 && pkt.stream_index == this->audio_index)){
				while(e_o_s || len > 0 ){
					int samples=0;
					int samples_out=0;
					int data_size;
					if(len > 0){
						len1 = avcodec_decode_audio(&astream->codec, audio_buf,&data_size, ptr, len);
						if (len1 < 0){
							/* if error, we skip the frame */
							break;
						}
						len -= len1;
						ptr += len1;
						if(data_size >0){
							samples =data_size / (aenc->channels * 2);
	
							samples_out = samples;
							if(this->audio_resample_ctx){
								samples_out = audio_resample(this->audio_resample_ctx, resampled, audio_buf, samples);
							}
							else
								resampled=audio_buf;
						}
					}
					if (theoraframes_add_audio(resampled, 
						samples_out *(this->channels),samples_out,e_o_s)){
						ret = -1;
						fprintf (stderr,"No audio frames available\n");
					}
					if(e_o_s && len <= 0){
						break;
					}
				}

			}
			/* flush out the file */
			theoraframes_flush (e_o_s);
			av_free_packet (&pkt);
		}
		while (ret >= 0);

		if(audio_buf){
			if(audio_buf!=resampled)
				av_free(resampled);
			av_free(audio_buf);
		}
		
		if (this->img_resample_ctx)
		    img_resample_close(this->img_resample_ctx);
		if (this->audio_resample_ctx)
		    audio_resample_close(this->audio_resample_ctx);

		theoraframes_close ();
	}
	else{
		fprintf (stderr, "No video or audio stream found\n");
	}
}

void ff2theora_close (ff2theora this){
	/* clear out state */
	av_free (this);
}

double aspect_check(const char *arg)
{
    int x = 0, y = 0;
    double ar = 0;
    const char *p;

    p = strchr(arg, ':');
    if (p) {
        x = strtol(arg, (char **)&arg, 10);
        if (arg == p)
            y = strtol(arg+1, (char **)&arg, 10);
        if (x > 0 && y > 0)
            ar = (double)x / (double)y;
    } else
        ar = strtod(arg, (char **)&arg);

    if (!ar) {
        fprintf(stderr, "Incorrect aspect ratio specification.\n");
        exit(1);
    }
	return ar;
}

int crop_check(ff2theora this, char *name, const char *arg)
{
	int crop_value = atoi(arg); 
    if (crop_value < 0) {
        fprintf(stderr, "Incorrect %s crop size\n",name);
        exit(1);
    }
    if ((crop_value % 2) != 0) {
        fprintf(stderr, "%s crop size must be a multiple of 2\n",name);
        exit(1);
    }
	/*
    if ((crop_value) >= this->height){
    	fprintf(stderr, "Vertical crop dimensions are outside the range of the original image.\nRemember to crop first and scale second.\n");
        exit(1);
    }
	*/
    return crop_value;
}



void print_presets_info() {
	fprintf (stderr, 
	//  "v2v presets - more info at http://wiki.v2v.cc/presets"
		"v2v presets\n"
		"preview\t\thalf size DV video. encoded with Ogg Theora/Ogg Vorbis\n"
		"\t\t\tVideo: Quality 5;\n"
		"\t\t\tAudio: Quality 1 - 44,1kHz - Stereo\n\n"
		"pro\t\tfull size DV video. encoded with Ogg Theora/Ogg Vorbis \n"
		"\t\t\tVideo: Quality 7;\n"
		"\t\t\tAudio: Quality 3 - 48kHz - Stereo\n"
		"\n");
}

void print_usage (){
	fprintf (stderr, 
		PACKAGE " " PACKAGE_VERSION "\n\n"
		" usage: " PACKAGE " [options] input\n\n"
		" Options:\n"
		"\t --output,-o\t\talternative output\n"
		"\t --format,-f\t\tspecify input format\n"
		"\t --width, -x\t\tscale to given size\n"
		"\t --height,-y\t\tscale to given size\n"
		"\t --endtime,-e\t\tend encoding at this time (in sec)\n"
		"\t --starttime,-s\t\tstart encoding at this time (in sec)\n"
		"\t --aspect\t\tdefine frame aspect ratio: i.e. 4:3 or 16:9\n"
		"\t --crop[top|bottom|left|right]\tcrop input before resizing\n"
		"\t --deinterlace,-d \t\t[off|on] disable deinterlace, \n"		
		"\t\t\t\t\tenabled by default right now\n"
		"\t --videoquality,-v\t[0 to 10]  encoding quality for video\n"
		"\t --audioquality,-a\t[-1 to 10] encoding quality for audio\n"
		"\t --samplerate,-H\t\tset output samplerate in Hz\n"
		"\t --nosound\t\tdisable the sound from input\n"
		"\n"
		"\t --v2v-preset,-p\tencode file with v2v preset, \n"
		"\t\t\t\t right now there is preview and pro,\n"
		"\t\t\t\t '"PACKAGE" -p info' for more informations\n"
#ifndef _WIN32
		"\t --nice\t\t\tset niceness to n\n"
#endif
		"\t --debug\t\toutputt some more information during encoding\n"
		"\t --help,-h\t\tthis message\n"
		"\n Examples:\n"
	
		"\tffmpeg2theora videoclip.avi (will write output to videoclip.avi.ogg)\n\n"
		"\tcat something.dv | ffmpeg2theora -f dv -o output.ogg -\n\n"
		"\tLive encoding from a DV camcorder (needs a fast machine)\n"
		"\tdvgrab - | ffmpeg2theora -f dv -x 352 -y 288 -o output.ogg -\n"
		"\n\tLive encoding and streaming to icecast server:\n"
		"\t dvgrab --format raw - | \\\n"
		"\t  ffmpeg2theora -f dv -x 160 -y 128 -o /dev/stdout - | \\\n"
		"\t  oggfwd iccast2server 8000 password /theora.ogg\n"
		"\n");
	exit (0);
}

int main (int argc, char **argv){
	int  n;
	int  outputfile_set=0;
	char outputfile_name[255];
	char inputfile_name[255];
	
	static int croptop_flag=0;
	static int cropbottom_flag=0;
	static int cropright_flag=0;
	static int cropleft_flag=0;	
	static int nosound_flag=0;	
	static int aspect_flag=0;
	
	AVInputFormat *input_fmt=NULL;
	ff2theora convert = ff2theora_init ();
	av_register_all ();
	
	int c,long_option_index;
	const char *optstring = "o:f:x:y:v:a:s:e:d:H:c:p:N:D:h::";
	struct option options [] = {
	  {"output",required_argument,NULL,'o'},
	  {"format",required_argument,NULL,'f'},
	  {"width",required_argument,NULL,'x'},
	  {"height",required_argument,NULL,'y'},
	  {"videoquality",required_argument,NULL,'v'},
	  {"audioquality",required_argument,NULL,'a'},
	  {"deinterlace",required_argument,NULL,'d'},
	  {"samplerate",required_argument,NULL,'H'},
	  {"channels",required_argument,NULL,'c'},
	  {"nosound",0,&nosound_flag,1},
	  {"aspect",required_argument,&aspect_flag,1},
	  {"v2v-preset",required_argument,NULL,'p'},
	  {"nice",required_argument,NULL,'N'},
	  {"croptop",required_argument,&croptop_flag,1},
	  {"cropbottom",required_argument,&cropbottom_flag,1},
	  {"cropright",required_argument,&cropright_flag,1},
	  {"cropleft",required_argument,&cropleft_flag,1},
	  {"starttime",required_argument,NULL,'s'},
	  {"endtime",required_argument,NULL,'e'},
	  
	  {"debug",0,NULL,'D'},
	  {"help",0,NULL,'h'},
	  {NULL,0,NULL,0}
	};
	if (argc == 1){
		print_usage ();
	}
	// set some variables;
	info.debug=0;
	
	while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
		switch(c)
	    {
			case 0:
				if (nosound_flag){
					convert->disable_audio=1;
					nosound_flag=0;
				}
				/* crop */
				if (croptop_flag){
					convert->frame_topBand=crop_check(convert,"top",optarg);
					croptop_flag=0;
				}
				if (cropbottom_flag){
					convert->frame_bottomBand=crop_check(convert,"bottom",optarg);
					cropbottom_flag=0;
				}
				if (cropright_flag){
					convert->frame_rightBand=crop_check(convert,"right",optarg);
					cropleft_flag=0;
				}
				if (cropleft_flag){
					convert->frame_leftBand=crop_check(convert,"left",optarg);
					cropleft_flag=0;
				}
				if (aspect_flag){
					convert->frame_aspect=aspect_check(optarg);
					aspect_flag=0;
				}
				break;
			case 'o':
				sprintf(outputfile_name,optarg);
				outputfile_set=1;
				break;
			case 'f':
				input_fmt=av_find_input_format(optarg);
				break;
			case 'x':
				convert->output_width=atoi(optarg);
				break;
			case 'y':
				convert->output_height=atoi(optarg);
				break;
			case 'v':
				convert->video_quality = rint(atof(optarg)*6.3);
				if(convert->video_quality <0 || convert->video_quality >63){
				        fprintf(stderr,"video quality out of range (choose 0 through 10)\n");
				        exit(1);
				}
				break;
			case 'a':
				convert->audio_quality=atof(optarg)*.099;
      			if(convert->audio_quality<-.1 || convert->audio_quality>1){
        			fprintf(stderr,"audio quality out of range (choose -1 through 10)\n");
        			exit(1);
				}
				break;
			case 's':
				convert->start_time = atoi(optarg);
				break;	
			case 'e':
				convert->end_time = atoi(optarg);
				break;	
			case 'd':
				if(!strcmp(optarg,"off"))
					convert->deinterlace=0;
				else
					convert->deinterlace=1;
				break;
			case 'H':
				convert->sample_rate=atoi(optarg);
				break;
			/* does not work right now */
			case 'c':
				convert->channels=atoi(optarg);
				break;
			case 'p':
				//v2v presets
				if(!strcmp(optarg, "info")){
					print_presets_info();
					exit(1);
				}
				else if(!strcmp(optarg, "pro")){
					//need a way to set resize here. and not later
					info.preset=V2V_PRESET_PRO;
					convert->video_quality = rint(7*6.3);
					convert->audio_quality=3*.099;
					convert->channels=2;
					convert->sample_rate=48000;
				}
				else if(!strcmp(optarg,"preview")){
					//need a way to set resize here. and not later
					info.preset=V2V_PRESET_PREVIEW;
					convert->video_quality = rint(5*6.3);
					convert->audio_quality=1*.099;
					convert->channels=2;
					convert->sample_rate=44100;
				}
				else{
					fprintf(stderr,"\nunknown preset.\n\n");
					print_presets_info();
					exit(1);
				}
				break;
			case 'N':
				n = atoi(optarg);
				if (n) {
#ifndef _WIN32
					if (nice(n)<0) {
						fprintf(stderr,"error setting %d for niceness", n);
					}
#endif
				}
				break;
			case 'D':
				//enable debug informations
				info.debug=1;
				break;
			case 'h':
				print_usage ();
				exit(1);
		}  
	}	
	//use PREVIEW as default setting
	if(argc==2){
		//need a way to set resize here. and not later
		info.preset=V2V_PRESET_PREVIEW;
		convert->video_quality = rint(5*6.3);
		convert->audio_quality=1*.099;
		convert->channels=2;
		convert->sample_rate=44100;
	}
	
	while(optind<argc){
		/* assume that anything following the options must be a filename */
		if(!strcmp(argv[optind],"-")){
			sprintf(inputfile_name,"pipe:");
		}
		else{
			sprintf(inputfile_name,"%s",argv[optind]);
			if(outputfile_set!=1){
				sprintf(outputfile_name,"%s.ogg",argv[optind]);
				outputfile_set=1;
			}	
		}
		optind++;
	}
	
	//FIXME: is using_stdin still neded? is it needed as global variable?
	using_stdin |= !strcmp(inputfile_name, "pipe:" ) ||
                   !strcmp( inputfile_name, "/dev/stdin" );

	if(outputfile_set!=1){	
		fprintf(stderr,"you have to specifie an output file with -o output.ogg.\n");	
		exit(1);
	}

	/* could go, but so far no player supports offset_x/y */
	if(convert->output_width % 16 ||  convert->output_height % 16){
		fprintf(stderr,"output size must be a multiple of 16 for now.\n");
		exit(1);
	}
	if(convert->output_width % 4 ||  convert->output_height % 4){
		fprintf(stderr,"output width and hight size must be a multiple of 2.\n");
		exit(1);
	}

	if (av_open_input_file(&convert->context, inputfile_name, input_fmt, 0, NULL) >= 0){
			if (av_find_stream_info (convert->context) >= 0){
				info.outfile = fopen(outputfile_name,"wb");
				dump_format (convert->context, 0,inputfile_name, 0);
				if(convert->disable_audio){
					fprintf(stderr,"  [audio disabled].\n");
				}
				ff2theora_output (convert);
				convert->audio_index =convert->video_index = -1;
			}
			else{
				fprintf (stderr,"Unable to decode input\n");
			}
			av_close_input_file (convert->context);
		}
		else{
			fprintf (stderr, "Unable to open file %s\n", inputfile_name);
		}
	ff2theora_close (convert);
	fprintf(stderr,"\n");
	return(0);
}


More information about the Theora mailing list