diff options
Diffstat (limited to 'win32/experimental/transcoder/transcoder_example.c')
-rw-r--r-- | win32/experimental/transcoder/transcoder_example.c | 926 |
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); + +} |