[xiph-commits] r16465 - branches/theora-monty-post-1-1/examples

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Tue Aug 11 20:35:33 PDT 2009


Author: xiphmont
Date: 2009-08-11 20:35:33 -0700 (Tue, 11 Aug 2009)
New Revision: 16465

Modified:
   branches/theora-monty-post-1-1/examples/Makefile.am
   branches/theora-monty-post-1-1/examples/encoder_example.c
Log:
Add y4o support to encoder_example

Clean up encoder example decode flow slightly (mostly in the actual 
encode loop)



Modified: branches/theora-monty-post-1-1/examples/Makefile.am
===================================================================
--- branches/theora-monty-post-1-1/examples/Makefile.am	2009-08-12 03:10:31 UTC (rev 16464)
+++ branches/theora-monty-post-1-1/examples/Makefile.am	2009-08-12 03:35:33 UTC (rev 16465)
@@ -24,7 +24,7 @@
 player_example_CFLAGS = $(SDL_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS)
 player_example_LDADD = $(LDADDDEC) $(SDL_LIBS) $(VORBIS_LIBS) $(OSS_LIBS)
 
-encoder_example_SOURCES = encoder_example.c
+encoder_example_SOURCES = encoder_example.c y4o.c y4o.h
 EXTRA_encoder_example_SOURCES = getopt.c getopt1.c getopt.h
 encoder_example_CFLAGS = $(OGG_CFLAGS) $(VORBIS_CFLAGS)
 encoder_example_LDADD = $(GETOPT_OBJS) $(LDADDENC) $(VORBIS_LIBS) $(VORBISENC_LIBS)

Modified: branches/theora-monty-post-1-1/examples/encoder_example.c
===================================================================
--- branches/theora-monty-post-1-1/examples/encoder_example.c	2009-08-12 03:10:31 UTC (rev 16464)
+++ branches/theora-monty-post-1-1/examples/encoder_example.c	2009-08-12 03:35:33 UTC (rev 16465)
@@ -46,7 +46,15 @@
 #include "theora/theoraenc.h"
 #include "vorbis/codec.h"
 #include "vorbis/vorbisenc.h"
+#include "y4o.h"
 
+/* Don't allow the audio/video buffers to get deeper than N seconds
+   when buffering audio and video from the same interleaved input
+   stream (eg, y4o).  This prevents eating arbitrary amounts of memory
+   futilely trying to buffer a stream that has wandered way out of
+   sync */
+#define MAX_BUFFER_SKEW 15.0
+
 #ifdef _WIN32
 /*supply missing headers and functions to Win32. going to hell, I know*/
 #include <fcntl.h>
@@ -89,9 +97,14 @@
 
 FILE *audio=NULL;
 FILE *video=NULL;
+y4o_in_t *y4o_audio=NULL;
+y4o_in_t *y4o_video=NULL;
+int y4o_audio_stream=-1;
+int y4o_video_stream=-1;
 
 int audio_ch=0;
 int audio_hz=0;
+int audio_b=0;
 
 float audio_q=.1f;
 int audio_r=-1;
@@ -112,21 +125,21 @@
 int src_c_dec_v=2;
 int dst_c_dec_h=2;
 int dst_c_dec_v=2;
-char chroma_type[16];
+y4o_chromafmt chroma_fmt;
 
 /*The size of each converted frame buffer.*/
-size_t y4m_dst_buf_sz;
+size_t y4x_dst_buf_sz;
 /*The amount to read directly into the converted frame buffer.*/
-size_t y4m_dst_buf_read_sz;
+size_t y4x_dst_buf_read_sz;
 /*The size of the auxilliary buffer.*/
-size_t y4m_aux_buf_sz;
+size_t y4x_aux_buf_sz;
 /*The amount to read into the auxilliary buffer.*/
-size_t y4m_aux_buf_read_sz;
+size_t y4x_aux_buf_read_sz;
 
 /*The function used to perform chroma conversion.*/
-typedef void (*y4m_convert_func)(unsigned char *_dst,unsigned char *_aux);
+typedef void (*y4x_convert_func)(unsigned char *_dst,unsigned char *_aux);
 
-y4m_convert_func y4m_convert=NULL;
+y4x_convert_func y4x_convert=NULL;
 
 int video_r=-1;
 int video_q=-1;
@@ -211,11 +224,13 @@
           "   -b --begin-time <h:m:s.d>      Begin encoding at offset into input\n"
           "   -e --end-time <h:m:s.d>        End encoding at offset into input\n"
           "encoder_example accepts only uncompressed RIFF WAV format audio and\n"
-          "YUV4MPEG2 uncompressed video.\n\n");
+          "YUV4MPEG2 uncompressed video. A YUV4OGG input may be used as an audio\n"
+          "and/or video source.\n\n");
   exit(1);
 }
 
 static int y4m_parse_tags(char *_tags){
+  char  tmp_chroma_type[16];
   int   got_w;
   int   got_h;
   int   got_fps;
@@ -260,8 +275,8 @@
       }break;
       case 'C':{
         if(q-p>16)return -1;
-        memcpy(chroma_type,p+1,q-p-1);
-        chroma_type[q-p-1]='\0';
+        memcpy(tmp_chroma_type,p+1,q-p-1);
+        tmp_chroma_type[q-p-1]='\0';
         got_chroma=1;
       }break;
       /*Ignore unknown tags.*/
@@ -270,12 +285,35 @@
   if(!got_w||!got_h||!got_fps||!got_interlace||!got_par)return -1;
   /*Chroma-type is not specified in older files, e.g., those generated by
      mplayer.*/
-  if(!got_chroma)strcpy(chroma_type,"420");
+  if(!got_chroma)strcpy(tmp_chroma_type,"420");
   /*Update fps and aspect ratio globals if not specified in the command line.*/
   if(video_fps_n==-1)video_fps_n=tmp_video_fps_n;
   if(video_fps_d==-1)video_fps_d=tmp_video_fps_d;
   if(video_par_n==-1)video_par_n=tmp_video_par_n;
   if(video_par_d==-1)video_par_d=tmp_video_par_d;
+
+  /* reuse Y4O chroma format enum for Y4M */
+  if(strcmp(tmp_chroma_type,"420")==0||strcmp(tmp_chroma_type,"420jpeg")==0){
+    chroma_fmt = Y4O_C420jpeg;
+  }else if(strcmp(tmp_chroma_type,"420mpeg2")==0){
+    chroma_fmt = Y4O_C420mpeg2;
+  }else if(strcmp(tmp_chroma_type,"420paldv")==0){
+    chroma_fmt = Y4O_C420paldv;
+  }else if(strcmp(tmp_chroma_type,"422")==0){
+    chroma_fmt = Y4O_C422smpte;
+  }else if(strcmp(tmp_chroma_type,"411")==0){
+    chroma_fmt = Y4O_C411ntscdv;
+  }else if(strcmp(tmp_chroma_type,"444")==0){
+    chroma_fmt = Y4O_C444;
+  }else if(strcmp(tmp_chroma_type,"444alpha")==0){
+    chroma_fmt = Y4O_C444alpha;
+  }else if(strcmp(tmp_chroma_type,"mono")==0){
+    chroma_fmt = Y4O_Cmono;
+  }else{
+    fprintf(stderr,"Unknown chroma sampling type: %s\n",tmp_chroma_type);
+    exit(1);
+  }
+
   return 0;
 }
 
@@ -342,7 +380,7 @@
   The 4:2:2 modes look exactly the same, except there are twice as many chroma
    lines, and they are vertically co-sited with the luma samples in both the
    mpeg2 and jpeg cases (thus requiring no vertical resampling).*/
-static void y4m_convert_42xmpeg2_42xjpeg(unsigned char *_dst,
+static void y4x_convert_42xmpeg2_42xjpeg(unsigned char *_dst,
  unsigned char *_aux){
   int c_w;
   int c_h;
@@ -421,7 +459,7 @@
    the chroma plane's resolution) to the right.
   Then we use another filter to move the C_r location down one quarter pixel,
    and the C_b location up one quarter pixel.*/
-static void y4m_convert_42xpaldv_42xjpeg(unsigned char *_dst,
+static void y4x_convert_42xpaldv_42xjpeg(unsigned char *_dst,
  unsigned char *_aux){
   unsigned char *tmp;
   int            c_w;
@@ -564,7 +602,7 @@
   We use a filter to resample at site locations one eighth pixel (at the source
    chroma plane's horizontal resolution) and five eighths of a pixel to the
    right.*/
-static void y4m_convert_411_422jpeg(unsigned char *_dst,
+static void y4x_convert_411_422jpeg(unsigned char *_dst,
  unsigned char *_aux){
   int c_w;
   int dst_c_w;
@@ -610,7 +648,7 @@
 
 /*The image is padded with empty chroma components at 4:2:0.
   This costs about 17 bits a frame to code.*/
-static void y4m_convert_mono_420jpeg(unsigned char *_dst,
+static void y4x_convert_mono_420jpeg(unsigned char *_dst,
  unsigned char *_aux){
   int c_sz;
   _dst+=pic_w*pic_h;
@@ -621,7 +659,7 @@
 #if 0
 /*Right now just 444 to 420.
   Not too hard to generalize.*/
-static void y4m_convert_4xxjpeg_42xjpeg(unsigned char *_dst,
+static void y4x_convert_4xxjpeg_42xjpeg(unsigned char *_dst,
  unsigned char *_aux){
   unsigned char *tmp;
   int            c_w;
@@ -692,10 +730,109 @@
 
 
 /*No conversion function needed.*/
-static void y4m_convert_null(unsigned char *_dst,
- unsigned char *_aux){
+static void y4x_convert_null(unsigned char *_dst, unsigned char *_aux){
 }
 
+static void setup_video(char *f, FILE *test){
+
+  if(interlace!='p'){
+    fprintf(stderr,"Input video is interlaced; Theora handles only progressive scan\n\n");
+    exit(1);
+  }
+
+  switch(chroma_fmt){
+  case Y4O_C420jpeg:
+    src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+    y4x_dst_buf_read_sz=pic_w*pic_h+2*((pic_w+1)/2)*((pic_h+1)/2);
+    y4x_aux_buf_sz=y4x_aux_buf_read_sz=0;
+    y4x_convert=y4x_convert_null;
+    break;
+  case Y4O_C420unknown:
+    fprintf(stderr,"WARNING: Unknown 4:2:0 chroma subsampling!\n"
+            "         Assuming mpeg2 chroma pixel positioning.\n\n");
+    /* fall through */
+  case Y4O_C420mpeg2:
+    src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+    y4x_dst_buf_read_sz=pic_w*pic_h;
+    /*Chroma filter required: read into the aux buf first.*/
+    y4x_aux_buf_sz=y4x_aux_buf_read_sz=2*((pic_w+1)/2)*((pic_h+1)/2);
+    y4x_convert=y4x_convert_42xmpeg2_42xjpeg;
+    break;
+  case Y4O_C420paldv:
+    src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+    y4x_dst_buf_read_sz=pic_w*pic_h;
+    /*Chroma filter required: read into the aux buf first.
+      We need to make two filter passes, so we need some extra space in the
+      aux buffer.*/
+    y4x_aux_buf_sz=3*((pic_w+1)/2)*((pic_h+1)/2);
+    y4x_aux_buf_read_sz=2*((pic_w+1)/2)*((pic_h+1)/2);
+    y4x_convert=y4x_convert_42xpaldv_42xjpeg;
+    break;
+  case Y4O_C422smpte:
+    src_c_dec_h=dst_c_dec_h=2;
+    src_c_dec_v=dst_c_dec_v=1;
+    y4x_dst_buf_read_sz=pic_w*pic_h;
+    /*Chroma filter required: read into the aux buf first.*/
+    y4x_aux_buf_sz=y4x_aux_buf_read_sz=2*((pic_w+1)/2)*pic_h;
+    y4x_convert=y4x_convert_42xmpeg2_42xjpeg;
+    break;
+  case Y4O_C422jpeg:
+    src_c_dec_h=dst_c_dec_h=2;
+    src_c_dec_v=dst_c_dec_v=1;
+    y4x_dst_buf_read_sz=pic_w*pic_h;
+    y4x_aux_buf_sz=y4x_aux_buf_read_sz=0;
+    y4x_convert=y4x_convert_null;
+    break;
+  case Y4O_C411ntscdv:
+    src_c_dec_h=4;
+    /*We don't want to introduce any additional sub-sampling, so we
+      promote 4:1:1 material to 4:2:2, as the closest format Theora can
+      handle.*/
+    dst_c_dec_h=2;
+    src_c_dec_v=dst_c_dec_v=1;
+    y4x_dst_buf_read_sz=pic_w*pic_h;
+    /*Chroma filter required: read into the aux buf first.*/
+    y4x_aux_buf_sz=y4x_aux_buf_read_sz=2*((pic_w+3)/4)*pic_h;
+    y4x_convert=y4x_convert_411_422jpeg;
+    break;
+  case Y4O_C444:
+    src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=1;
+    y4x_dst_buf_read_sz=pic_w*pic_h*3;
+    y4x_aux_buf_sz=y4x_aux_buf_read_sz=0;
+    y4x_convert=y4x_convert_null;
+    break;
+  case Y4O_C444alpha:
+    src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=1;
+    y4x_dst_buf_read_sz=pic_w*pic_h*3;
+    /*Read the extra alpha plane into the aux buf.
+          It will be discarded.*/
+    y4x_aux_buf_sz=y4x_aux_buf_read_sz=pic_w*pic_h;
+    y4x_convert=y4x_convert_null;
+    break;
+  case Y4O_Cmono:
+    src_c_dec_h=src_c_dec_v=0;
+    dst_c_dec_h=dst_c_dec_v=2;
+    y4x_dst_buf_read_sz=pic_w*pic_h;
+    y4x_aux_buf_sz=y4x_aux_buf_read_sz=0;
+    y4x_convert=y4x_convert_mono_420jpeg;
+    break;
+  default:
+    /* can't get here */
+    exit(1);
+  }
+
+  /*The size of the final frame buffers is always computed from the
+    destination chroma decimation type.*/
+  y4x_dst_buf_sz=pic_w*pic_h+2*((pic_w+dst_c_dec_h-1)/dst_c_dec_h)*
+    ((pic_h+dst_c_dec_v-1)/dst_c_dec_v);
+
+  video=test;
+
+  fprintf(stderr,"File %s contains %dx%d %.02f fps %s video.\n",
+          f,pic_w,pic_h,(double)video_fps_n/video_fps_d,
+          y4o_chromaformat_long[chroma_fmt]);
+}
+
 static void id_file(char *f){
   FILE *test;
   unsigned char buffer[80];
@@ -756,9 +893,13 @@
           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");
+          audio_b=buffer[18]+(buffer[19]<<8);
+          switch(audio_b){
+          case 16: case 24:
+            audio_b/=8;
+            break;
+          default:
+            fprintf(stderr,"Can only read 16 and 24 bit WAV files for now.\n");
             exit(1);
           }
 
@@ -772,8 +913,8 @@
               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);
+              fprintf(stderr,"File %s contains %d bit %d channel %d Hz RIFF WAV audio.\n",
+                      f,audio_b*8,audio_ch,audio_hz);
 
               return;
             }
@@ -787,124 +928,99 @@
 
   }
   if(!memcmp(buffer,"YUV4",4)){
-    /* possible YUV2MPEG2 format file */
+    /* possible YUV2MPEG2 or YUV4OGG format file */
     /* read until newline, or 80 cols, whichever happens first */
     int i;
-    for(i=0;i<79;i++){
+    for(i=4;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 YUV2MPEG2 file?\n",f);
+      fprintf(stderr,"Error parsing %s header; not a YUV2MPEG2 or YUV4OGG file?\n",f);
     }
+
     buffer[i]='\0';
 
-    if(!memcmp(buffer,"MPEG",4)){
+    if(!memcmp(buffer+4,"OGG ",4)){
+      y4o_in_t *yf=y4o_init(test, (char *)buffer);
 
+      if(!yf)
+        goto y4o_err;
+
+      for(i=0;i<yf->num_streams;i++){
+        y4o_stream_t *s=yf->streams[i];
+        if(s->type==Y4O_STREAM_VIDEO){
+          if(video){
+            /* umm, we already have one */
+            fprintf(stderr,"Multiple video sources specified on command line.\n");
+            exit(1);
+          }
+
+          pic_w = s->m.video.w;
+          pic_h = s->m.video.h;
+          video_fps_n = s->m.video.fps_n;
+          video_fps_d = s->m.video.fps_d;
+          interlace = (s->m.video.i == Y4O_I_PROGRESSIVE ? 'p' : 'i');
+          video_par_n = s->m.video.pa_n;
+          video_par_d = s->m.video.pa_d;
+          chroma_fmt = s->m.video.format;
+
+          setup_video(f,test);
+          y4o_video = yf;
+          y4o_video_stream = i;
+
+        }else if(s->type==Y4O_STREAM_AUDIO){
+          if(audio){
+            /* umm, we already have one */
+            fprintf(stderr,"Multiple audio sources specified on command line.\n");
+            exit(1);
+          }
+
+          audio_ch = s->m.audio.ch;
+          audio_hz = s->m.audio.rate;
+          audio_b  = 3;
+
+          fprintf(stderr,"File %s contains 24 bit %d channel %d Hz RIFF WAV audio.\n",
+                  f,audio_ch,audio_hz);
+
+          audio=test;
+          y4o_audio = yf;
+          y4o_audio_stream = i;
+        }
+      }
+
+      if(!yf->synced && y4o_video && y4o_audio){
+        fprintf(stderr,"\nWARNING: YUV4OGG input stream is unsynced! Audio and video\n"
+                "         timing may be out of sync and input buffering may\n"
+                "         require larger than normal amounts of memory.\n");
+      }
+
+      return;
+    }else if(!memcmp(buffer+4,"MPEG",4)){
+
       if(video){
         /* umm, we already have one */
         fprintf(stderr,"Multiple video files specified on command line.\n");
         exit(1);
       }
 
-      if(buffer[4]!='2'){
+      if(buffer[8]!='2'){
         fprintf(stderr,"Incorrect YUV input file version; YUV4MPEG2 required.\n");
       }
 
-      ret=y4m_parse_tags((char *)buffer+5);
+      ret=y4m_parse_tags((char *)buffer+9);
       if(ret<0){
         fprintf(stderr,"Error parsing YUV4MPEG2 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);
-      }
+      setup_video(f,test);
 
-      if(strcmp(chroma_type,"420")==0||strcmp(chroma_type,"420jpeg")==0){
-        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
-        y4m_dst_buf_read_sz=pic_w*pic_h+2*((pic_w+1)/2)*((pic_h+1)/2);
-        y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
-        y4m_convert=y4m_convert_null;
-      }
-      else if(strcmp(chroma_type,"420mpeg2")==0){
-        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
-        y4m_dst_buf_read_sz=pic_w*pic_h;
-        /*Chroma filter required: read into the aux buf first.*/
-        y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+1)/2)*((pic_h+1)/2);
-        y4m_convert=y4m_convert_42xmpeg2_42xjpeg;
-      }
-      else if(strcmp(chroma_type,"420paldv")==0){
-        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
-        y4m_dst_buf_read_sz=pic_w*pic_h;
-        /*Chroma filter required: read into the aux buf first.
-          We need to make two filter passes, so we need some extra space in the
-           aux buffer.*/
-        y4m_aux_buf_sz=3*((pic_w+1)/2)*((pic_h+1)/2);
-        y4m_aux_buf_read_sz=2*((pic_w+1)/2)*((pic_h+1)/2);
-        y4m_convert=y4m_convert_42xpaldv_42xjpeg;
-      }
-      else if(strcmp(chroma_type,"422")==0){
-        src_c_dec_h=dst_c_dec_h=2;
-        src_c_dec_v=dst_c_dec_v=1;
-        y4m_dst_buf_read_sz=pic_w*pic_h;
-        /*Chroma filter required: read into the aux buf first.*/
-        y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+1)/2)*pic_h;
-        y4m_convert=y4m_convert_42xmpeg2_42xjpeg;
-      }
-      else if(strcmp(chroma_type,"411")==0){
-        src_c_dec_h=4;
-        /*We don't want to introduce any additional sub-sampling, so we
-           promote 4:1:1 material to 4:2:2, as the closest format Theora can
-           handle.*/
-        dst_c_dec_h=2;
-        src_c_dec_v=dst_c_dec_v=1;
-        y4m_dst_buf_read_sz=pic_w*pic_h;
-        /*Chroma filter required: read into the aux buf first.*/
-        y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+3)/4)*pic_h;
-        y4m_convert=y4m_convert_411_422jpeg;
-      }
-      else if(strcmp(chroma_type,"444")==0){
-        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=1;
-        y4m_dst_buf_read_sz=pic_w*pic_h*3;
-        y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
-        y4m_convert=y4m_convert_null;
-      }
-      else if(strcmp(chroma_type,"444alpha")==0){
-        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=1;
-        y4m_dst_buf_read_sz=pic_w*pic_h*3;
-        /*Read the extra alpha plane into the aux buf.
-          It will be discarded.*/
-        y4m_aux_buf_sz=y4m_aux_buf_read_sz=pic_w*pic_h;
-        y4m_convert=y4m_convert_null;
-      }
-      else if(strcmp(chroma_type,"mono")==0){
-        src_c_dec_h=src_c_dec_v=0;
-        dst_c_dec_h=dst_c_dec_v=2;
-        y4m_dst_buf_read_sz=pic_w*pic_h;
-        y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
-        y4m_convert=y4m_convert_mono_420jpeg;
-      }
-      else{
-        fprintf(stderr,"Unknown chroma sampling type: %s\n",chroma_type);
-        exit(1);
-      }
-      /*The size of the final frame buffers is always computed from the
-         destination chroma decimation type.*/
-      y4m_dst_buf_sz=pic_w*pic_h+2*((pic_w+dst_c_dec_h-1)/dst_c_dec_h)*
-       ((pic_h+dst_c_dec_v-1)/dst_c_dec_v);
-
-      video=test;
-
-      fprintf(stderr,"File %s is %dx%d %.02f fps %s video.\n",
-              f,pic_w,pic_h,(double)video_fps_n/video_fps_d,chroma_type);
-
       return;
     }
   }
-  fprintf(stderr,"Input file %s is neither a WAV nor YUV4MPEG2 file.\n",f);
+  fprintf(stderr,"Input file %s is not a WAV, YUV4MPEG2, or YUV4OGG file.\n",f);
   exit(1);
 
  riff_err:
@@ -913,6 +1029,8 @@
  yuv_err:
   fprintf(stderr,"EOF parsing YUV4MPEG2 file %s.\n",f);
   exit(1);
+ y4o_err:
+  exit(1);
 
 }
 
@@ -924,88 +1042,100 @@
   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){
-  static ogg_int64_t samples_sofar=0;
+static double last_video_time=0;
+static double last_audio_time=0;
+static ogg_int64_t samples_sofar=0;
+
+/* Audio input buffering is handled by libvorbis, so unlike video we
+   don't need to implement both input and output buffering here.  We
+   have a block of audio, we can immediately push into libvorbis */
+void push_raw_audio_block(unsigned char *readbuffer, int bytesread,
+                          ogg_stream_state *vo,
+                          vorbis_dsp_state *vd,
+                          vorbis_block *vb){
+
+  ogg_int64_t beginsample = audio_hz*begin_sec + audio_hz*begin_usec*.000001;
+  ogg_int64_t endsample = audio_hz*end_sec + audio_hz*end_usec*.000001;
   ogg_packet op;
   int i,j;
-  ogg_int64_t beginsample = audio_hz*begin_sec + audio_hz*begin_usec*.000001;
-  ogg_int64_t endsample = audio_hz*end_sec + audio_hz*end_usec*.000001;
 
-  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;
+  unsigned char *readptr=readbuffer;
+  int sampread=bytesread/audio_b/audio_ch;
+  float **vorbis_buffer;
+  int count=0;
 
-    {
-      /* read and process more audio */
-      signed char readbuffer[4096];
-      signed char *readptr=readbuffer;
-      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 ||
-         (samples_sofar>=endsample && endsample>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);
+  if(bytesread<=0 || (samples_sofar>=endsample && endsample>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{
+    if(samples_sofar < beginsample){
+      if(samples_sofar+sampread > beginsample){
+        readptr += (beginsample-samples_sofar)*audio_b*audio_ch;
+        sampread += samples_sofar-beginsample;
+        samples_sofar = sampread+beginsample;
       }else{
-        if(samples_sofar < beginsample){
-          if(samples_sofar+sampread > beginsample){
-            readptr += (beginsample-samples_sofar)*2*audio_ch;
-            sampread += samples_sofar-beginsample;
-            samples_sofar = sampread+beginsample;
-          }else{
-            samples_sofar += sampread;
-            sampread = 0;
-          }
-        }else{
-          samples_sofar += sampread;
-        }
+        samples_sofar += sampread;
+        sampread = 0;
+      }
+    }else{
+      samples_sofar += sampread;
+    }
 
-        if(samples_sofar > endsample && endsample > 0)
-          sampread-= (samples_sofar - endsample);
+    if(samples_sofar > endsample && endsample > 0)
+      sampread-= (samples_sofar - endsample);
 
-        if(sampread>0){
+    if(sampread>0){
 
-          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]=((readptr[count+1]<<8)|
-                                   (0x00ff&(int)readptr[count]))/32768.f;
-              count+=2;
-            }
+      vorbis_buffer=vorbis_analysis_buffer(vd,sampread);
+      /* uninterleave samples */
+      switch(audio_b){
+      case 2:
+        for(i=0;i<sampread;i++){
+          for(j=0;j<audio_ch;j++){
+            vorbis_buffer[j][i]=(((signed char)readptr[count+1]<<8)|
+                                 (readptr[count]))/32768.f;
+            count+=2;
           }
-
-          vorbis_analysis_wrote(vd,sampread);
         }
+        break;
+      case 3:
+        for(i=0;i<sampread;i++){
+          for(j=0;j<audio_ch;j++){
+            vorbis_buffer[j][i]=(((signed char)readptr[count+2]<<16)|
+                                 (readptr[count+1]<<8)|
+                                 (readptr[count]))/8388608.f;
+            count+=3;
+          }
+        }
+        break;
       }
 
-      while(vorbis_analysis_blockout(vd,vb)==1){
+      vorbis_analysis_wrote(vd,sampread);
+    }
+  }
 
-        /* analysis, assume we want to use bitrate management */
-        vorbis_analysis(vb,NULL);
-        vorbis_bitrate_addblock(vb);
+  /* while we're at it, pull all data through the encoder and into
+     output stream buffering.  The compressed audio in the output
+     buffer takes up less space than the uncompressed audio in the
+     input buffer */
+  while(vorbis_analysis_blockout(vd,vb)==1){
 
-        /* weld packets into the bitstream */
-        while(vorbis_bitrate_flushpacket(vd,&op))
-          ogg_stream_packetin(vo,&op);
+    /* 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)){
+      if(vorbis_granule_time(vd,op.granulepos)>last_audio_time+MAX_BUFFER_SKEW){
+        fprintf(stderr,"ERROR: Audio / Video buffer skew has exceeded maximum limit.\n");
+        exit(1);
       }
+      ogg_stream_packetin(vo,&op);
     }
   }
-
-  return audioflag;
 }
 
 static int                 frame_state=-1;
@@ -1013,87 +1143,22 @@
 static unsigned char      *yuvframe[3];
 static th_ycbcr_buffer     ycbcr;
 
-int fetch_and_process_video_packet(FILE *video,FILE *twopass_file,int passno,
- th_enc_ctx *td,ogg_packet *op){
-  int                        ret;
-  int                        pic_sz;
-  int                        frame_c_w;
-  int                        frame_c_h;
-  int                        c_w;
-  int                        c_h;
-  int                        c_sz;
-  ogg_int64_t                beginframe;
-  ogg_int64_t                endframe;
-  spinnit();
-  beginframe=(video_fps_n*begin_sec+video_fps_n*begin_usec*.000001)/video_fps_d;
-  endframe=(video_fps_n*end_sec+video_fps_n*end_usec*.000001)/video_fps_d;
-  if(frame_state==-1){
-    /* initialize the double frame buffer */
-    yuvframe[0]=(unsigned char *)malloc(y4m_dst_buf_sz);
-    yuvframe[1]=(unsigned char *)malloc(y4m_dst_buf_sz);
-    yuvframe[2]=(unsigned char *)malloc(y4m_aux_buf_sz);
-    frame_state=0;
-  }
-  pic_sz=pic_w*pic_h;
-  frame_c_w=frame_w/dst_c_dec_h;
-  frame_c_h=frame_h/dst_c_dec_v;
-  c_w=(pic_w+dst_c_dec_h-1)/dst_c_dec_h;
-  c_h=(pic_h+dst_c_dec_v-1)/dst_c_dec_v;
-  c_sz=c_w*c_h;
-  /* 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 */
+void process_video_block(FILE *twopass_file,int passno,
+                         th_enc_ctx *td, ogg_stream_state *to){
+  int pic_sz=pic_w*pic_h;
+  int frame_c_w=frame_w/dst_c_dec_h;
+  int frame_c_h=frame_h/dst_c_dec_v;
+  int c_w=(pic_w+dst_c_dec_h-1)/dst_c_dec_h;
+  int c_h=(pic_h+dst_c_dec_v-1)/dst_c_dec_v;
+  int c_sz=c_w*c_h;
+  ogg_packet op;
+  int ret;
 
-  /* 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(;frame_state<2 && (frames<endframe || endframe<0);){
-    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 YUV 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 YUV frame header\n");
-        exit(1);
-      }
-    }
-    /*Read the frame data that needs no conversion.*/
-    if(fread(yuvframe[frame_state],1,y4m_dst_buf_read_sz,video)!=
-     y4m_dst_buf_read_sz){
-      fprintf(stderr,"Error reading YUV frame data.\n");
-      exit(1);
-    }
-    /*Read the frame data that does need conversion.*/
-    if(fread(yuvframe[2],1,y4m_aux_buf_read_sz,video)!=y4m_aux_buf_read_sz){
-      fprintf(stderr,"Error reading YUV frame data.\n");
-      exit(1);
-    }
-    /*Now convert the just read frame.*/
-    (*y4m_convert)(yuvframe[frame_state],yuvframe[2]);
-    frames++;
-    if(frames>=beginframe)
-    frame_state++;
-  }
-  /* check to see if there are dupes to flush */
-  if(th_encode_packetout(td,frame_state<1,op)>0)return 1;
-  if(frame_state<1){
-    /* can't get here unless YUV4MPEG 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 */
-  /* in two-pass mode's second pass, we need to submit first-pass data */
+  /* in two-pass mode's second pass, we need to submit first-pass
+     data; this can be pulled on-demand as it's separate local data */
+
   if(passno==2){
     for(;;){
       static unsigned char buffer[80];
@@ -1142,12 +1207,15 @@
   ycbcr[2].stride=c_w;
   ycbcr[2].data=ycbcr[1].data+c_sz;
   th_encode_ycbcr_in(td,ycbcr);
+
+  /* flip frame buffers */
   {
     unsigned char *temp=yuvframe[0];
     yuvframe[0]=yuvframe[1];
     yuvframe[1]=temp;
     frame_state--;
   }
+
   /* in two-pass mode's first pass we need to extract and save the pass data */
   if(passno==1){
     unsigned char *buffer;
@@ -1162,8 +1230,9 @@
     }
     fflush(twopass_file);
   }
-  /* if there was only one frame, it's the last in the stream */
-  ret = th_encode_packetout(td,frame_state<1,op);
+  /* if there was only one frame in the buffer, it was the last in the stream */
+  ret = th_encode_packetout(td,frame_state<1,&op);
+
   if(passno==1 && frame_state<1){
     /* need to read the final (summary) packet */
     unsigned char *buffer;
@@ -1182,26 +1251,274 @@
     }
     fflush(twopass_file);
   }
-  return ret;
+
+  if(ret){
+    if(passno==1){
+      /* first pass does not push pages, so we need to track time seperately */
+      last_video_time = th_granule_time(td,op.granulepos);
+    }else{
+      if(th_granule_time(td,op.granulepos)>last_video_time+MAX_BUFFER_SKEW){
+        fprintf(stderr,"ERROR: Audio / Video buffer skew has exceeded maximum limit.\n");
+        exit(1);
+      }
+      ogg_stream_packetin(to,&op);
+    }
+  }
+
+  /* fetch dupe frames if any */
+  while(th_encode_packetout(td,frame_state<1,&op)>0){
+    if(passno!=1){
+      ogg_stream_packetin(to,&op);
+    }else{
+      /* first pass does not push pages, so we need to track time seperately */
+      last_video_time=th_granule_time(td,op.granulepos);
+    }
+    /* no need to re-check buffer depth guard here */
+  }
 }
 
 
-int fetch_and_process_video(FILE *video,ogg_page *videopage,
- ogg_stream_state *to,th_enc_ctx *td,FILE *twopass_file,int passno,
- int videoflag){
-  ogg_packet op;
-  int ret;
-  /* is there a video page flushed?  If not, work until there is. */
-  while(!videoflag){
-    if(ogg_stream_pageout(to,videopage)>0) return 1;
-    if(ogg_stream_eos(to)) return 0;
-    ret=fetch_and_process_video_packet(video,twopass_file,passno,td,&op);
-    if(ret<=0)return 0;
-    ogg_stream_packetin(to,&op);
+/* This is a triggered push, not an on-demand pull.  Rather than
+   requesting a block of compressed video, what we actually have is a
+   frame of uncompressed video that has to be pushed into buffering
+   right now.  This may require pulling data out of the encoder/input
+   buffer into the output buffer to make space for a frame. */
+void push_raw_video_block(FILE *twopass_file,int passno,
+                          th_enc_ctx *td, ogg_stream_state *to,
+                          y4o_frame_t *p){
+
+  ogg_int64_t beginframe=(video_fps_n*begin_sec+video_fps_n*begin_usec*.000001)/video_fps_d;
+  ogg_int64_t endframe=(video_fps_n*end_sec+video_fps_n*end_usec*.000001)/video_fps_d;
+
+  if(frame_state==-1){
+    /* initialize the double frame buffer */
+    yuvframe[0]=(unsigned char *)malloc(y4x_dst_buf_sz);
+    yuvframe[1]=(unsigned char *)malloc(y4x_dst_buf_sz);
+    yuvframe[2]=(unsigned char *)malloc(y4x_aux_buf_sz);
+    frame_state=0;
   }
-  return videoflag;
+
+  /* Do we actually need to read this frame into buffering?  If it's
+     outside our read range, just pull and discard. */
+  if(frames<beginframe || (frames>=endframe && endframe>=0)){
+    if(y4o_video){
+      /* in the y4o case, the frame header has already been read */
+      y4o_read_frame_data(y4o_audio,p);
+      y4o_free_frame(p);
+    } /* else y4m is not interleaved; do nothing */
+    return;
+  }
+
+  /* if the frame input buffer is full, we need to pull a frame
+     through the encoder to make space before reading a new one into
+     buffering */
+  while(frame_state>=2)
+    process_video_block(twopass_file,passno,td,to);
+
+  /* now we have space to push the video input */
+  /* header read/check */
+  if(y4o_video){
+    /* verify the payload is what we're expecting */
+    if(p->len != y4x_dst_buf_read_sz+y4x_aux_buf_read_sz){
+      fprintf(stderr,"Error in YUV frame size:  Expected %d, got %d.\n",
+              (int)(y4x_dst_buf_read_sz+y4x_aux_buf_read_sz),(int)(p->len));
+      exit(1);
+    }
+  }else{
+    /* y4o header already read, y4m needs to read it still */
+    char c,frame[6];
+    int ret=fread(frame,1,6,video);
+    /* match and skip the frame header */
+    if(ret<6)return;
+    if(memcmp(frame,"FRAME",5)){
+      fprintf(stderr,"Loss of framing in YUV 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 YUV frame header\n");
+        exit(1);
+      }
+    }
+  }
+
+  /*Read the frame data that needs no conversion.*/
+  if(fread(yuvframe[frame_state],1,y4x_dst_buf_read_sz,video)!=
+     y4x_dst_buf_read_sz){
+    fprintf(stderr,"Error reading YUV frame data.\n");
+    exit(1);
+  }
+  /*Read the frame data that does need conversion.*/
+  if(fread(yuvframe[2],1,y4x_aux_buf_read_sz,video)!=y4x_aux_buf_read_sz){
+    fprintf(stderr,"Error reading YUV frame data.\n");
+    exit(1);
+  }
+  /*Now convert the just read frame.*/
+  (*y4x_convert)(yuvframe[frame_state],yuvframe[2]);
+  frames++;
+  frame_state++;
 }
 
+static unsigned char audioheader[27+255];
+static unsigned char audiobody[255*255];
+
+int fetch_audio(ogg_stream_state *to,
+                ogg_stream_state *vo,
+                th_enc_ctx *td,
+                vorbis_dsp_state *vd,
+                vorbis_block *vb,
+                FILE *twopass_file,
+                int passno,
+                ogg_page *audiopage){
+  ogg_page og;
+  while(audio){
+    /* process any audio already buffered */
+    spinnit();
+    if(ogg_stream_pageout(vo,&og)>0){
+      double t=vorbis_granule_time(vd,ogg_page_granulepos(&og));
+
+      /* Ogg will reclaim the memory associated with the page the next
+         time the stream is accessed.  Unfortunately, because we're
+         using push buffering (to avoid having to implement an async
+         input buffer to handle interleaved Y4O input streams),
+         working ahead to prime the video pipe may cause another
+         stream push in the audio stream before this page is used.
+         Thus we copy the data into local storage.  There are
+         obviously more efficient ways to handle this, but the extra
+         effort would be misplaced here. */
+
+      memcpy(audiopage,&og,sizeof(og));
+      audiopage->header=audioheader;
+      audiopage->body=audiobody;
+      memcpy(audiopage->header,og.header,og.header_len);
+      memcpy(audiopage->body,og.body,og.body_len);
+
+      if(t!=-1)last_audio_time=t;
+      return 1;
+    }
+    if(ogg_stream_eos(vo))return 0;
+
+    /* read and process more audio; because some inputs may have audio
+       and video in the same stream, this may result in needing to
+       push video into buffering as well. */
+
+    if(y4o_audio){
+      /* y4o streams are structured and may have other data types mixed in */
+      y4o_frame_t *p=y4o_read_frame_header(y4o_audio);
+      if(!p){
+        push_raw_audio_block(NULL, 0, vo, vd, vb);
+      }else{
+        if(p->streamno == y4o_audio_stream){
+          y4o_read_frame_data(y4o_audio,p);
+          push_raw_audio_block((unsigned char *)p->data, p->len, vo, vd, vb);
+        }else if(y4o_audio == y4o_video && p->streamno == y4o_video_stream){
+          push_raw_video_block(twopass_file,passno,td,to,p);
+        }else{
+          /* unknown frame type, discard */
+          y4o_read_frame_data(y4o_audio,p);
+          y4o_free_frame(p);
+        }
+      }
+    }else{
+      /* the only other audio source is wav, so it's a raw blob of data */
+      unsigned char readbuffer[4096];
+      int toread=4096/audio_b/audio_ch;
+      int bytesread=fread(readbuffer,1,toread*audio_b*audio_ch,audio);
+
+      push_raw_audio_block(readbuffer, bytesread, vo, vd, vb);
+    }
+  }
+
+  return 0;
+}
+
+static unsigned char videoheader[27+255];
+static unsigned char videobody[255*255];
+
+int fetch_video(ogg_stream_state *to,
+                ogg_stream_state *vo,
+                th_enc_ctx *td,
+                vorbis_dsp_state *vd,
+                vorbis_block *vb,
+                FILE *twopass_file,
+                int passno,
+                ogg_page *videopage){
+
+  ogg_int64_t endframe=(video_fps_n*end_sec+video_fps_n*end_usec*.000001)/video_fps_d;
+  ogg_page og;
+
+  while(video){
+    /* process any audio already buffered */
+    spinnit();
+    if(passno!=1){
+      if(ogg_stream_pageout(to,&og)>0){
+        double t=th_granule_time(td,ogg_page_granulepos(&og));
+
+      /* Ogg will reclaim the memory associated with the page the next
+         time the stream is accessed.  Unfortunately, because we're
+         using push buffering (to avoid having to implement an async
+         input buffer to handle interleaved Y4O input streams),
+         working ahead to prime the video pipe may cause another
+         stream push in the audio stream before this page is used.
+         Thus we copy the data into local storage.  There are
+         obviously more efficient ways to handle this, but the extra
+         effort would be misplaced here. */
+
+        memcpy(videopage,&og,sizeof(og));
+        videopage->header=videoheader;
+        videopage->body=videobody;
+        memcpy(videopage->header,og.header,og.header_len);
+        memcpy(videopage->body,og.body,og.body_len);
+
+        if(t!=-1)last_video_time=t;
+        return 1;
+      }
+      if(ogg_stream_eos(to))return 0;
+    }
+    /* Are we draining the stream at encode end? */
+    if((frames>=endframe && endframe>=0) || feof(video)){
+      if(frame_state>0){ /* this guards startup as well as two-pass
+                            first-pass packet drain */
+        process_video_block(twopass_file,passno,td,to);
+        if(passno==1) return 1;
+        continue;
+      }else{
+        return 0;
+      }
+    }
+
+    /* read and process more video; because some inputs may have audio
+       and video in the same stream, this may result in needing to
+       push audio as well. */
+
+    if(y4o_video){
+      /* y4o streams are structured and may have other data types mixed in */
+      y4o_frame_t *p=y4o_read_frame_header(y4o_video);
+      if(p){
+        if(p->streamno == y4o_video_stream){
+          push_raw_video_block(twopass_file,passno,td,to,p);
+          if(passno==1) return 1;
+        }else if(y4o_audio == y4o_video && p->streamno == y4o_audio_stream && passno!=1){
+          y4o_read_frame_data(y4o_audio,p);
+          push_raw_audio_block((unsigned char *)p->data, p->len, vo, vd, vb);
+        }else{
+          /* unknown frame type, discard */
+          y4o_read_frame_data(y4o_audio,p);
+          y4o_free_frame(p);
+        }
+      }
+    }else{
+      /* the only other video source is y4m */
+      push_raw_video_block(twopass_file,passno,td,to,NULL);
+      if(passno==1) return 1;
+    }
+  }
+  return 0;
+}
+
 static int ilog(unsigned _v){
   int ret;
   for(ret=0;_v;ret++)_v>>=1;
@@ -1230,15 +1547,12 @@
   vorbis_block     vb; /* local working space for packet->PCM decode */
 
   int speed=-1;
-  int audioflag=0;
-  int videoflag=0;
   int akbps=0;
   int vkbps=0;
   int soft_target=0;
 
   ogg_int64_t audio_bytesout=0;
   ogg_int64_t video_bytesout=0;
-  double timebase;
 
   FILE *outfile = stdout;
 
@@ -1256,6 +1570,8 @@
   _setmode( _fileno( stdout ), _O_BINARY );
 #endif
 
+  fprintf(stderr,"\n");
+
   while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
     switch(c){
     case 'o':
@@ -1453,6 +1769,7 @@
     id_file(argv[optind]);
     optind++;
   }
+  fprintf(stderr,"\n");
 
   if(twopass==3){
     /* verify that the input is seekable! */
@@ -1625,7 +1942,7 @@
       ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,
        &buf_delay,sizeof(buf_delay));
       if(ret<0){
-        fprintf(stderr,"Warning: could not set desired buffer delay.\n");
+        fprintf(stderr,"WARNING: could not set desired buffer delay.\n");
       }
     }
     /*Speed should also be set after the current encoder mode is established,
@@ -1727,80 +2044,80 @@
       }
     }
     /* setup complete.  Raw processing loop */
-      switch(passno){
-      case 0: case 2:
-        fprintf(stderr,"\rCompressing....                                          \n");
-        break;
-      case 1:
-        fprintf(stderr,"\rScanning first pass....                                  \n");
-        break;
-      }
-    for(;;){
-      int audio_or_video=-1;
-      if(passno==1){
-        ogg_packet op;
-        int ret=fetch_and_process_video_packet(video,twopass_file,passno,td,&op);
-        if(ret<0)break;
-        if(op.e_o_s)break; /* end of stream */
-        timebase=th_granule_time(td,op.granulepos);
-        audio_or_video=1;
-      }else{
-        double audiotime;
-        double videotime;
-        ogg_page audiopage;
-        ogg_page videopage;
-        /* 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,td,twopass_file,passno,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 */
-        audiotime=
-        audioflag?vorbis_granule_time(&vd,ogg_page_granulepos(&audiopage)):-1;
-        videotime=
-        videoflag?th_granule_time(td,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;
+    switch(passno){
+    case 0: case 2:
+      fprintf(stderr,"\rCompressing....                                          \n");
+      break;
+    case 1:
+      fprintf(stderr,"\rScanning first pass....                                  \n");
+      break;
+    }
+
+    {
+      int have_audio_page=0;
+      int have_video_page=0;
+      ogg_page audiopage;
+      ogg_page videopage;
+      double audiotime;
+      double videotime;
+      double timebase=-1;
+
+      for(;;){
+        if(passno==1){
+          if(fetch_video(&to,&vo,td,&vd,&vb,twopass_file,1,NULL)<=0) break;
+          timebase = last_video_time;
         }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;
+
+          if(!have_video_page)
+            have_video_page = fetch_video(&to,&vo,td,&vd,&vb,twopass_file,passno,&videopage);
+          if(!have_audio_page)
+            have_audio_page = fetch_audio(&to,&vo,td,&vd,&vb,twopass_file,passno,&audiopage);
+
+          /* no pages of either?  Must be end of stream. */
+          if(!have_audio_page && !have_video_page)break;
+
+          /* if we have both audio and video to flush, which is
+             earlier; the end of the audio page or the end of the
+             video page? Flush the earlier to stream. */
+          audiotime=have_audio_page?vorbis_granule_time(&vd,ogg_page_granulepos(&audiopage)):-1;
+          videotime=have_video_page?th_granule_time(td,ogg_page_granulepos(&videopage)):-1;
+
+          if(have_video_page && (!have_audio_page || videotime<audiotime)){
+            /* flush a video page */
+            video_bytesout+=fwrite(videopage.header,1,videopage.header_len,outfile);
+            video_bytesout+=fwrite(videopage.body,1,videopage.body_len,outfile);
+            have_video_page=0;
+            if(videotime>0)vkbps=(int)rint(video_bytesout*8./videotime*.001);
+            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);
+            have_audio_page=0;
+            if(audiotime>0)akbps=(int)rint(audio_bytesout*8./audiotime*.001);
+            timebase=audiotime;
+          }
         }
+
+        if(timebase>0){
+          int hundredths=(int)(timebase*100-(long)timebase*100);
+          int seconds=(long)timebase%60;
+          int minutes=((long)timebase/60)%60;
+          int hours=(long)timebase/3600;
+          fprintf(stderr,
+                  "\r      %d:%02d:%02d.%02d audio: %dkbps video: %dkbps                 ",
+                  hours,minutes,seconds,hundredths,akbps,vkbps);
+        }
       }
-      if(timebase > 0){
-        int hundredths=(int)(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=(int)rint(video_bytesout*8./timebase*.001);
-        else akbps=(int)rint(audio_bytesout*8./timebase*.001);
-        fprintf(stderr,
-                "\r      %d:%02d:%02d.%02d audio: %dkbps video: %dkbps                 ",
-                hours,minutes,seconds,hundredths,akbps,vkbps);
-      }
     }
     if(video)th_encode_free(td);
   }
 
   /* clear out state */
+  if(y4o_video && y4o_video!=y4o_audio)
+    y4o_free(y4o_video);
+  if(y4o_audio)
+    y4o_free(y4o_audio);
   if(audio){
     ogg_stream_clear(&vo);
     vorbis_block_clear(&vb);
@@ -1812,7 +2129,7 @@
   if(video){
     ogg_stream_clear(&to);
     th_comment_clear(&tc);
-    if(video!=stdin)fclose(video);
+    if(video!=stdin && audio!=video)fclose(video);
   }
 
   if(outfile && outfile!=stdout)fclose(outfile);



More information about the commits mailing list