summaryrefslogtreecommitdiff
path: root/win32/experimental/transcoder/transcoder_example.c
diff options
context:
space:
mode:
Diffstat (limited to 'win32/experimental/transcoder/transcoder_example.c')
-rw-r--r--win32/experimental/transcoder/transcoder_example.c926
1 files changed, 926 insertions, 0 deletions
diff --git a/win32/experimental/transcoder/transcoder_example.c b/win32/experimental/transcoder/transcoder_example.c
new file mode 100644
index 0000000..28c2b52
--- /dev/null
+++ b/win32/experimental/transcoder/transcoder_example.c
@@ -0,0 +1,926 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2004 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: example encoder application; makes an Ogg Theora/Vorbis
+ file from YUV4MPEG2 and WAV input
+ last mod: $Id: transcoder_example.c,v 1.4 2004/03/20 00:14:04 tterribe Exp $
+
+ ********************************************************************/
+
+#define _GNU_SOURCE
+#define _REENTRANT
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <time.h>
+#include <math.h>
+#include "theora/theora.h"
+#include "vorbis/codec.h"
+#include "vorbis/vorbisenc.h"
+
+#ifdef _WIN32
+/*supply missing headers and functions to Win32. going to hell, I know*/
+#include <io.h>
+#include <fcntl.h>
+
+static double rint(double x)
+{
+ if (x < 0.0)
+ return (double)(int)(x - 0.5);
+ else
+ return (double)(int)(x + 0.5);
+}
+#endif
+
+/*Copied from vorbis/sharedbook.c*/
+static int _ilog(unsigned int v){
+ int ret=0;
+ while(v){
+ ret++;
+ v>>=1;
+ }
+ return(ret);
+}
+
+
+const char *optstring = "o:a:A:v:V:";
+struct option options [] = {
+ {"output",required_argument,NULL,'o'},
+ {"audio-rate-target",required_argument,NULL,'A'},
+ {"video-rate-target",required_argument,NULL,'V'},
+ {"audio-quality",required_argument,NULL,'a'},
+ {"video-quality",required_argument,NULL,'v'},
+ {NULL,0,NULL,0}
+};
+
+typedef struct TC_INSTANCE {
+ ogg_uint32_t LastKeyFrame ;
+ ogg_int64_t KeyFrameCount;
+ int ThisIsFirstFrame;
+ int ThisIsKeyFrame;
+ ogg_uint32_t CurrentFrame;
+ ogg_int64_t granulepos;
+ int keyframe_granule_shift;
+ char * in_bytes;
+ long in_bytecount;
+ ogg_uint32_t fps_denominator;
+ ogg_uint32_t fps_numerator;
+ oggpack_buffer opb_in;
+ oggpack_buffer opb_out;
+} TC_INSTANCE;
+
+/* You'll go to Hell for using globals. */
+
+FILE *audio=NULL;
+FILE *video=NULL;
+
+int audio_ch=0;
+int audio_hz=0;
+
+float audio_q=.1;
+int audio_r=-1;
+
+int video_x=0;
+int video_y=0;
+int frame_x=0;
+int frame_y=0;
+int frame_x_offset=0;
+int frame_y_offset=0;
+int video_hzn=0;
+int video_hzd=0;
+int video_an=0;
+int video_ad=0;
+
+int video_r=-1;
+int video_q=16;
+
+ char *vp3frame[2];
+ int framebytecount[2];
+ int frameiskey[2];
+
+ ogg_page audiopage;
+ ogg_page videopage;
+
+static void usage(void){
+ fprintf(stderr,
+ "Usage: encoder_example [options] [audio_file] video_file\n\n"
+ "Options: \n\n"
+ " -o --output <filename.ogv> file name for encoded output;\n"
+ " If this option is not given, the\n"
+ " compressed data is sent to stdout.\n\n"
+ " -A --audio-rate-target <n> bitrate target for Vorbis audio;\n"
+ " use -a and not -A if at all possible,\n"
+ " as -a gives higher quality for a given\n"
+ " bitrate.\n\n"
+ " -V --video-rate-target <n> bitrate target for Theora video\n\n"
+ " -a --audio-quality <n> Vorbis quality selector from -1 to 10\n"
+ " (-1 yields smallest files but lowest\n"
+ " fidelity; 10 yields highest fidelity\n"
+ " but large files. '2' is a reasonable\n"
+ " default).\n\n"
+ " -v --video-quality <n> Theora quality selector fro 0 to 10\n"
+ " (0 yields smallest files but lowest\n"
+ " video quality. 10 yields highest\n"
+ " fidelity but large files).\n\n"
+ "encoder_example accepts only uncompressed RIFF WAV format audio and\n"
+ "YUV4MPEG2 uncompressed video.\n\n");
+ exit(1);
+}
+
+static void id_file(char *f){
+ FILE *test;
+ unsigned char buffer[80];
+ int ret;
+
+ /* open it, look for magic */
+
+ if(!strcmp(f,"-")){
+ /* stdin */
+ test=stdin;
+ }else{
+ test=fopen(f,"rb");
+ if(!test){
+ fprintf(stderr,"Unable to open file %s.\n",f);
+ exit(1);
+ }
+ }
+
+ ret=fread(buffer,1,4,test);
+ if(ret<4){
+ fprintf(stderr,"EOF determining file type of file %s.\n",f);
+ exit(1);
+ }
+
+ if(!memcmp(buffer,"RIFF",4)){
+ /* possible WAV file */
+
+ if(audio){
+ /* umm, we already have one */
+ fprintf(stderr,"Multiple RIFF WAVE files specified on command line.\n");
+ exit(1);
+ }
+
+ /* Parse the rest of the header */
+
+ ret=fread(buffer,1,4,test);
+ ret=fread(buffer,1,4,test);
+ if(ret<4)goto riff_err;
+ if(!memcmp(buffer,"WAVE",4)){
+
+ while(!feof(test)){
+ ret=fread(buffer,1,4,test);
+ if(ret<4)goto riff_err;
+ if(!memcmp("fmt",buffer,3)){
+
+ /* OK, this is our audio specs chunk. Slurp it up. */
+
+ ret=fread(buffer,1,20,test);
+ if(ret<20)goto riff_err;
+
+ if(memcmp(buffer+4,"\001\000",2)){
+ fprintf(stderr,"The WAV file %s is in a compressed format; "
+ "can't read it.\n",f);
+ exit(1);
+ }
+
+ audio=test;
+ audio_ch=buffer[6]+(buffer[7]<<8);
+ audio_hz=buffer[8]+(buffer[9]<<8)+
+ (buffer[10]<<16)+(buffer[11]<<24);
+
+ if(buffer[18]+(buffer[19]<<8)!=16){
+ fprintf(stderr,"Can only read 16 bit WAV files for now.\n");
+ exit(1);
+ }
+
+ /* Now, align things to the beginning of the data */
+ /* Look for 'dataxxxx' */
+ while(!feof(test)){
+ ret=fread(buffer,1,4,test);
+ if(ret<4)goto riff_err;
+ if(!memcmp("data",buffer,4)){
+ /* We're there. Ignore the declared size for now. */
+ ret=fread(buffer,1,4,test);
+ if(ret<4)goto riff_err;
+
+ fprintf(stderr,"File %s is 16 bit %d channel %d Hz RIFF WAV audio.\n",
+ f,audio_ch,audio_hz);
+
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ fprintf(stderr,"Couldn't find WAVE data in RIFF file %s.\n",f);
+ exit(1);
+
+ }
+ if(!memcmp(buffer,"AVI2",4)){
+ /* possible AVI2VP31 format file */
+ /* read until newline, or 80 cols, whichever happens first */
+ int i;
+ for(i=0;i<79;i++){
+ ret=fread(buffer+i,1,1,test);
+ if(ret<1)goto yuv_err;
+ if(buffer[i]=='\n')break;
+ }
+ if(i==79){
+ fprintf(stderr,"Error parsing %s header; not a VP31 raw frames file?\n",f);
+ }
+ buffer[i]='\0';
+
+ if(!memcmp(buffer,"VP31",4)){
+ char interlace;
+
+ if(video){
+ /* umm, we already have one */
+ fprintf(stderr,"Multiple video files specified on command line.\n");
+ exit(1);
+ }
+
+ if(buffer[4]!='R'){
+ fprintf(stderr,"Incorrect file ; VP31 raw frames required.\n");
+ }
+
+ ret=sscanf(buffer,"VP31R W%d H%d F%d:%d I%c A%d:%d",
+ &frame_x,&frame_y,&video_hzn,&video_hzd,&interlace,
+ &video_an,&video_ad);
+ if(ret<7){
+ fprintf(stderr,"Error parsing AVI2VP31R header in file %s.\n",f);
+ exit(1);
+ }
+
+ if(interlace!='p'){
+ fprintf(stderr,"Input video is interlaced; Theora handles only progressive scan\n");
+ exit(1);
+ }
+
+ video=test;
+
+ fprintf(stderr,"File %s is %dx%d %.02f fps VP31 video.\n",
+ f,frame_x,frame_y,(double)video_hzn/video_hzd);
+
+ return;
+ }
+ }
+ fprintf(stderr,"Input file %s is neither a WAV nor VP31 file.\n",f);
+ exit(1);
+
+ riff_err:
+ fprintf(stderr,"EOF parsing RIFF file %s.\n",f);
+ exit(1);
+ yuv_err:
+ fprintf(stderr,"EOF parsing VP31 file %s.\n",f);
+ exit(1);
+
+}
+
+int spinner=0;
+char *spinascii="|/-\\";
+void spinnit(void){
+ spinner++;
+ if(spinner==4)spinner=0;
+ fprintf(stderr,"\r%c",spinascii[spinner]);
+}
+
+int fetch_and_process_audio(FILE *audio,ogg_page *audiopage,
+ ogg_stream_state *vo,
+ vorbis_dsp_state *vd,
+ vorbis_block *vb,
+ int audioflag){
+ ogg_packet op;
+ int i,j;
+
+ while(audio && !audioflag){
+ /* process any audio already buffered */
+ spinnit();
+ if(ogg_stream_pageout(vo,audiopage)>0) return 1;
+ if(ogg_stream_eos(vo))return 0;
+
+ {
+ /* read and process more audio */
+ signed char readbuffer[4096];
+ int toread=4096/2/audio_ch;
+ int bytesread=fread(readbuffer,1,toread*2*audio_ch,audio);
+ int sampread=bytesread/2/audio_ch;
+ float **vorbis_buffer;
+ int count=0;
+
+ if(bytesread<=0){
+ /* end of file. this can be done implicitly, but it's
+ easier to see here in non-clever fashion. Tell the
+ library we're at end of stream so that it can handle the
+ last frame and mark end of stream in the output properly */
+ vorbis_analysis_wrote(vd,0);
+ }else{
+ vorbis_buffer=vorbis_analysis_buffer(vd,sampread);
+ /* uninterleave samples */
+ for(i=0;i<sampread;i++){
+ for(j=0;j<audio_ch;j++){
+ vorbis_buffer[j][i]=((readbuffer[count+1]<<8)|
+ (0x00ff&(int)readbuffer[count]))/32768.f;
+ count+=2;
+ }
+ }
+
+ vorbis_analysis_wrote(vd,sampread);
+
+ }
+
+ while(vorbis_analysis_blockout(vd,vb)==1){
+
+ /* analysis, assume we want to use bitrate management */
+ vorbis_analysis(vb,NULL);
+ vorbis_bitrate_addblock(vb);
+
+ /* weld packets into the bitstream */
+ while(vorbis_bitrate_flushpacket(vd,&op))
+ ogg_stream_packetin(vo,&op);
+
+ }
+ }
+ }
+
+ return audioflag;
+}
+
+int theora_transcode_packetout( TC_INSTANCE *ttc, int last_p, ogg_packet *op){
+
+ long bytes=ttc->in_bytecount;
+
+ if(!bytes)return(0);
+
+ op->packet=ttc->in_bytes;
+ op->bytes=bytes;
+ op->b_o_s=0;
+ op->e_o_s=last_p;
+
+ op->packetno=ttc->CurrentFrame;
+ op->granulepos=ttc->granulepos;
+
+ return 1;
+}
+
+void TranscodeKeyFrame(TC_INSTANCE *ttc){
+ /* Keep track of the total number of Key Frames Coded */
+ ttc->KeyFrameCount += 1;
+ ttc->LastKeyFrame = 1;
+}
+
+void TranscodeFrame(TC_INSTANCE *ttc){
+ ttc->LastKeyFrame++;
+}
+
+void TranscodeFirstFrame(TC_INSTANCE *ttc){
+ /* Keep track of the total number of Key Frames Coded. */
+ ttc->KeyFrameCount = 1;
+ ttc->LastKeyFrame = 1;
+}
+
+int theora_transcode_bufferin( TC_INSTANCE *ttc, int isKeyFrame, char * bytes, int bytecount){
+
+ /*transcode: record keyframe flag*/
+ ttc->ThisIsKeyFrame = isKeyFrame;
+
+ /* Special case for first frame */
+ if ( ttc->ThisIsFirstFrame ){
+ ttc->ThisIsFirstFrame = 0;
+ ttc->ThisIsKeyFrame = 0;
+ } else if ( ttc->ThisIsKeyFrame ) {
+ TranscodeKeyFrame(ttc);
+ ttc->ThisIsKeyFrame = 0;
+ } else {
+ /* Compress the frame. */
+ TranscodeFrame( ttc );
+ }
+ /*need to pack info here*/
+ {
+
+ int frame_type;
+ long total_bits;
+ long total_words;
+ int frac_bits;
+
+ oggpackB_readinit(&ttc->opb_in,bytes,bytecount);
+ oggpackB_reset(&ttc->opb_out);
+
+ /*Mark as video frame.*/
+ oggpackB_write(&ttc->opb_out,0,1);
+ /*Copy frame type.*/
+ frame_type=oggpackB_read1(&ttc->opb_in);
+ oggpackB_write(&ttc->opb_out,frame_type,1);
+ /*Skip an unused bit in the VP32 header.*/
+ oggpackB_adv1(&ttc->opb_in);
+ /*Copy Q multiplier.*/
+ oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,6),6);
+ /*VP3 has no per-block Q multipliers*/
+ oggpackB_write(&ttc->opb_out,0,1);
+ /*If the frame is a base/key/golden frame, copy a few extra bits.*/
+ if(frame_type==0){
+ /*These 13 bits are not included in a Theora frame header.
+ They were 0's and VP3 version info in VP32.*/
+ oggpackB_adv(&ttc->opb_in,13);
+ /*Copy the key frame type and the spare configuration bits.*/
+ oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,3),3);
+ }
+
+ /*Copy the rest of the bits over.*/
+ total_bits=bytecount*8-oggpack_bits(&ttc->opb_in);
+ frac_bits=(int)(total_bits&31);
+ if(frac_bits){
+ oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,frac_bits),
+ frac_bits);
+ }
+ total_words=total_bits>>5;
+ while(total_words-->0){
+ oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,32),32);
+ }
+
+ ttc->in_bytecount = oggpackB_bytes(&ttc->opb_out);
+ ttc->in_bytes = oggpackB_get_buffer(&ttc->opb_out);
+ }
+
+ /* Update stats variables. */
+ ttc->CurrentFrame++;
+
+ ttc->granulepos=
+ ((ttc->CurrentFrame-ttc->LastKeyFrame-1)<<ttc->keyframe_granule_shift)+
+ ttc->LastKeyFrame-1;
+
+ return 0;
+}
+
+//static void _tp_writebuffer(oggpack_buffer *opb, const char *buf, const long len)
+
+int theora_transcoder_init(theora_info * ti, TC_INSTANCE * ttc){
+ memset(ttc, 0, sizeof(*ttc));
+ ttc->granulepos = -1;
+ ttc->keyframe_granule_shift=_ilog(ti->keyframe_frequency_force-1);
+ ttc->LastKeyFrame = 0;
+ ttc->KeyFrameCount = 0;
+ ttc->ThisIsFirstFrame = 1;
+ ttc->ThisIsKeyFrame = 0;
+ ttc->CurrentFrame = 1;
+ ttc->in_bytes = 0;
+ ttc->in_bytecount = 0;
+ ttc->fps_denominator = ti->fps_denominator;
+ ttc->fps_numerator = ti->fps_numerator;
+ oggpackB_writeinit(&ttc->opb_out);
+ return 0;
+}
+
+int fetch_and_process_video(FILE *video,ogg_page *videopage,
+ ogg_stream_state *to,
+ TC_INSTANCE *ttc,
+ int videoflag){
+ /* You'll go to Hell for using static variables */
+ static int state=-1;
+ ogg_packet op;
+ int i;
+ int keyframeflag, framelength;
+
+
+ if(state==-1){
+ /* initialize the double frame buffer */
+ state=0;
+ }
+
+ /* is there a video page flushed? If not, work until there is. */
+ while(!videoflag){
+ spinnit();
+
+ if(ogg_stream_pageout(to,videopage)>0) return 1;
+ if(ogg_stream_eos(to)) return 0;
+
+ {
+ /* read and process more video */
+ /* video strategy reads one frame ahead so we know when we're
+ at end of stream and can mark last video frame as such
+ (vorbis audio has to flush one frame past last video frame
+ due to overlap and thus doesn't need this extra work */
+
+ /* have two frame buffers full (if possible) before
+ proceeding. after first pass and until eos, one will
+ always be full when we get here */
+
+ for(i=state;i<2;i++){
+ char c,frame[6];
+ int ret=fread(frame,1,6,video);
+
+ /* match and skip the frame header */
+ if(ret<6)break;
+ if(memcmp(frame,"FRAME",5)){
+ fprintf(stderr,"Loss of framing in VP31 input data\n");
+ exit(1);
+ }
+ if(frame[5]!='\n'){
+ int j;
+ for(j=0;j<79;j++)
+ if(fread(&c,1,1,video)&&c=='\n')break;
+ if(j==79){
+ fprintf(stderr,"Error parsing VP31 frame header\n");
+ exit(1);
+ }
+ }
+
+ /*read the length*/
+ ret=fread(&framelength, sizeof(int), 1, video);
+
+ /*read the keyframeflag*/
+ ret=fread(&keyframeflag, sizeof(int), 1, video);
+
+ vp3frame[i] = malloc(framelength);
+ framebytecount[i] = framelength;
+ frameiskey[i] = keyframeflag;
+
+ /* read the frame */
+ ret=fread((char *) vp3frame[i], sizeof(char), framelength, video);
+ if(ret!=framelength) break;
+
+ state++;
+ }
+
+ if(state<1){
+ /* can't get here unless VP31 stream has no video */
+ fprintf(stderr,"Video input contains no frames.\n");
+ exit(1);
+ }
+
+ /* Theora is a one-frame-in,one-frame-out system; submit a frame
+ for compression and pull out the packet */
+
+ //theora_encode_YUVin(td,&yuv);
+ theora_transcode_bufferin( ttc, frameiskey[0], vp3frame[0], framebytecount[0]);
+
+ /* if there's only one frame, it's the last in the stream */
+ if(state<2)
+ theora_transcode_packetout(ttc,1,&op);
+ else
+ theora_transcode_packetout(ttc,0,&op);
+
+ ogg_stream_packetin(to,&op);
+
+ {
+ signed char *temp=vp3frame[0];
+ vp3frame[0]=vp3frame[1];
+ vp3frame[1] = temp;
+ free(temp);
+
+ framebytecount[0]= framebytecount[1];
+ frameiskey[0] = frameiskey[1];
+ state--;
+ }
+ }
+ }
+ return videoflag;
+}
+
+/* returns, in seconds, absolute time of current packet in given
+ logical stream */
+double transcode_granule_time(TC_INSTANCE *ttc,ogg_int64_t granulepos){
+ if(granulepos>=0){
+ ogg_int64_t iframe=granulepos>>ttc->keyframe_granule_shift;
+ ogg_int64_t pframe=granulepos-(iframe<<ttc->keyframe_granule_shift);
+
+ return (iframe+pframe)*
+ ((double)ttc->fps_denominator/ttc->fps_numerator);
+
+ }
+ return(-1);
+}
+
+int main(int argc,char *argv[]){
+ int c,long_option_index,ret;
+
+ ogg_stream_state to; /* take physical pages, weld into a logical
+ stream of packets */
+ ogg_stream_state vo; /* take physical pages, weld into a logical
+ stream of packets */
+ ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
+ ogg_packet op; /* one raw packet of data for decode */
+
+ theora_state td;
+ theora_info ti;
+ theora_comment tc;
+
+ vorbis_info vi; /* struct that stores all the static vorbis bitstream
+ settings */
+ vorbis_comment vc; /* struct that stores all the user comments */
+
+ vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+ vorbis_block vb; /* local working space for packet->PCM decode */
+
+ int audioflag=0;
+ int videoflag=0;
+ int akbps=0;
+ int vkbps=0;
+
+ ogg_int64_t audio_bytesout=0;
+ ogg_int64_t video_bytesout=0;
+ double timebase;
+
+ FILE* outfile = stdout;
+
+ TC_INSTANCE ttc;
+
+#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
+ /* if we were reading/writing a file, it would also need to in
+ binary mode, eg, fopen("file.wav","wb"); */
+ /* Beware the evil ifdef. We avoid these where we can, but this one we
+ cannot. Don't add any more, you'll probably go to hell if you do. */
+ _setmode( _fileno( stdin ), _O_BINARY );
+ _setmode( _fileno( stdout ), _O_BINARY );
+#endif
+
+ while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+ switch(c){
+ case 'o':
+ outfile=fopen(optarg,"wb");
+ if(outfile==NULL){
+ fprintf(stderr,"Unable to open output file '%s'\n", optarg);
+ exit(1);
+ }
+ break;;
+
+ case 'a':
+ audio_q=atof(optarg)*.099;
+ if(audio_q<-.1 || audio_q>1){
+ fprintf(stderr,"Illegal audio quality (choose -1 through 10)\n");
+ exit(1);
+ }
+ audio_r=-1;
+ break;
+
+ case 'v':
+ video_q=rint(atof(optarg)*6.3);
+ if(video_q<0 || video_q>63){
+ fprintf(stderr,"Illegal video quality (choose 0 through 10)\n");
+ exit(1);
+ }
+ video_r=0;
+ break;
+
+ case 'A':
+ audio_r=atof(optarg)*1000;
+ if(audio_q<0){
+ fprintf(stderr,"Illegal audio quality (choose > 0 please)\n");
+ exit(1);
+ }
+ audio_q=-99;
+ break;
+
+ case 'V':
+ video_r=rint(atof(optarg)*1000);
+ if(video_r<45000 || video_r>2000000){
+ fprintf(stderr,"Illegal video bitrate (choose 45kbps through 2000kbps)\n");
+ exit(1);
+ }
+ video_q=0;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ while(optind<argc){
+ /* assume that anything following the options must be a filename */
+ id_file(argv[optind]);
+ optind++;
+ }
+
+ /* yayness. Set up Ogg output stream */
+ srand(time(NULL));
+ ogg_stream_init(&vo,rand());
+ ogg_stream_init(&to,rand()); /* oops, add one ot the above */
+
+ /* Set up Theora encoder */
+ if(!video){
+ fprintf(stderr,"No video files submitted for compression?\n");
+ exit(1);
+ }
+ /* Theora has a divisible-by-sixteen restriction for the encoded video size */
+ /* scale the frame size up to the nearest /16 and calculate offsets */
+ video_x=((frame_x + 15) >>4)<<4;
+ video_y=((frame_y + 15) >>4)<<4;
+ frame_x_offset=(video_x-frame_x)/2;
+ frame_y_offset=(video_y-frame_y)/2;
+
+ theora_info_init(&ti);
+ ti.width=video_x;
+ ti.height=video_y;
+ ti.frame_width=frame_x;
+ ti.frame_height=frame_y;
+ ti.offset_x=frame_x_offset;
+ ti.offset_y=frame_y_offset;
+ ti.fps_numerator=video_hzn;
+ ti.fps_denominator=video_hzd;
+ ti.aspect_numerator=video_an;
+ ti.aspect_denominator=video_ad;
+ ti.colorspace=OC_CS_UNSPECIFIED;
+ ti.target_bitrate=video_r;
+ ti.quality=video_q;
+
+ ti.dropframes_p=0;
+ ti.quick_p=1;
+ ti.keyframe_auto_p=1;
+ ti.keyframe_frequency=32768;
+ ti.keyframe_frequency_force=32768;
+ ti.keyframe_data_target_bitrate=video_r*1.5;
+ ti.keyframe_auto_threshold=80;
+ ti.keyframe_mindistance=8;
+ ti.noise_sensitivity=1;
+
+ theora_encode_init(&td,&ti);
+ theora_transcoder_init(&ti, &ttc);
+ theora_info_clear(&ti);
+
+ /* initialize Vorbis too, assuming we have audio to compress. */
+ if(audio){
+ vorbis_info_init(&vi);
+ if(audio_q>-99)
+ ret = vorbis_encode_init_vbr(&vi,audio_ch,audio_hz,audio_q);
+ else
+ ret = vorbis_encode_init(&vi,audio_ch,audio_hz,-1,audio_r,-1);
+ if(ret){
+ fprintf(stderr,"The Vorbis encoder could not set up a mode according to\n"
+ "the requested quality or bitrate.\n\n");
+ exit(1);
+ }
+
+ vorbis_comment_init(&vc);
+ vorbis_analysis_init(&vd,&vi);
+ vorbis_block_init(&vd,&vb);
+ }
+
+ /* write the bitstream header packets with proper page interleave */
+
+ /* first packet will get its own page automatically */
+ theora_encode_header(&td,&op);
+ ogg_stream_packetin(&to,&op);
+ if(ogg_stream_pageout(&to,&og)!=1){
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ fwrite(og.header,1,og.header_len,outfile);
+ fwrite(og.body,1,og.body_len,outfile);
+
+ /* create the remaining theora headers */
+ theora_comment_init(&tc);
+ theora_encode_comment(&tc,&op);
+ ogg_stream_packetin(&to,&op);
+ theora_encode_tables(&td,&op);
+ ogg_stream_packetin(&to,&op);
+
+ if(audio){
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+
+ vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
+ ogg_stream_packetin(&vo,&header); /* automatically placed in its own
+ page */
+ if(ogg_stream_pageout(&vo,&og)!=1){
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ fwrite(og.header,1,og.header_len,outfile);
+ fwrite(og.body,1,og.body_len,outfile);
+
+ /* remaining vorbis header packets */
+ ogg_stream_packetin(&vo,&header_comm);
+ ogg_stream_packetin(&vo,&header_code);
+ }
+
+ /* Flush the rest of our headers. This ensures
+ the actual data in each stream will start
+ on a new page, as per spec. */
+ while(1){
+ int result = ogg_stream_flush(&to,&og);
+ if(result<0){
+ /* can't get here */
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ if(result==0)break;
+ fwrite(og.header,1,og.header_len,outfile);
+ fwrite(og.body,1,og.body_len,outfile);
+ }
+ if(audio){
+ while(1){
+ int result=ogg_stream_flush(&vo,&og);
+ if(result<0){
+ /* can't get here */
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ if(result==0)break;
+ fwrite(og.header,1,og.header_len,outfile);
+ fwrite(og.body,1,og.body_len,outfile);
+ }
+ }
+
+ /* setup complete. Raw processing loop */
+ fprintf(stderr,"Compressing....\n");
+ while(1){
+
+
+ /* is there an audio page flushed? If not, fetch one if possible */
+ audioflag=fetch_and_process_audio(audio,&audiopage,&vo,&vd,&vb,audioflag);
+
+ /* is there a video page flushed? If not, fetch one if possible */
+ videoflag=fetch_and_process_video(video,&videopage,&to,&ttc,videoflag);
+
+ /* no pages of either? Must be end of stream. */
+ if(!audioflag && !videoflag)break;
+
+ /* which is earlier; the end of the audio page or the end of the
+ video page? Flush the earlier to stream */
+ {
+ int audio_or_video=-1;
+ double audiotime=
+ audioflag?vorbis_granule_time(&vd,ogg_page_granulepos(&audiopage)):-1;
+ double videotime=
+ videoflag?transcode_granule_time(&ttc,ogg_page_granulepos(&videopage)):-1;
+
+ if(!audioflag){
+ audio_or_video=1;
+ } else if(!videoflag) {
+ audio_or_video=0;
+ } else {
+ if(audiotime<videotime)
+ audio_or_video=0;
+ else
+ audio_or_video=1;
+ }
+
+ if(audio_or_video==1){
+ /* flush a video page */
+ video_bytesout+=fwrite(videopage.header,1,videopage.header_len,outfile);
+ video_bytesout+=fwrite(videopage.body,1,videopage.body_len,outfile);
+ videoflag=0;
+ timebase=videotime;
+
+ }else{
+ /* flush an audio page */
+ audio_bytesout+=fwrite(audiopage.header,1,audiopage.header_len,outfile);
+ audio_bytesout+=fwrite(audiopage.body,1,audiopage.body_len,outfile);
+ audioflag=0;
+ timebase=audiotime;
+ }
+ {
+ int hundredths=timebase*100-(long)timebase*100;
+ int seconds=(long)timebase%60;
+ int minutes=((long)timebase/60)%60;
+ int hours=(long)timebase/3600;
+
+ if(audio_or_video)
+ vkbps=rint(video_bytesout*8./timebase*.001);
+ else
+ akbps=rint(audio_bytesout*8./timebase*.001);
+
+ fprintf(stderr,
+ "\n %d:%02d:%02d.%02d audio: %dkbps video: %dkbps ",
+ hours,minutes,seconds,hundredths,akbps,vkbps);
+ }
+ }
+
+ }
+
+ /* clear out state */
+
+ if(audio){
+ ogg_stream_clear(&vo);
+ vorbis_block_clear(&vb);
+ vorbis_dsp_clear(&vd);
+ vorbis_comment_clear(&vc);
+ vorbis_info_clear(&vi);
+ }
+ if(video){
+ ogg_stream_clear(&to);
+ theora_clear(&td);
+ }
+
+ if(outfile && outfile!=stdout)fclose(outfile);
+
+ fprintf(stderr,"\r \ndone.\n\n");
+
+ return(0);
+
+}