[xiph-commits] r16340 - in branches/theora-thusnelda: examples include/theora lib/dec lib/enc

tterribe at svn.xiph.org tterribe at svn.xiph.org
Sun Jul 26 20:33:27 PDT 2009


Author: tterribe
Date: 2009-07-26 20:33:27 -0700 (Sun, 26 Jul 2009)
New Revision: 16340

Modified:
   branches/theora-thusnelda/examples/encoder_example.c
   branches/theora-thusnelda/include/theora/theoraenc.h
   branches/theora-thusnelda/lib/dec/decode.c
   branches/theora-thusnelda/lib/enc/analyze.c
   branches/theora-thusnelda/lib/enc/encint.h
   branches/theora-thusnelda/lib/enc/encode.c
   branches/theora-thusnelda/lib/enc/rate.c
Log:
Add support for two-pass rate control.
Heavily modified version of a patch from xiphmont, this should support drop
 frames, VFR content, and finite buffer delays properly.


Modified: branches/theora-thusnelda/examples/encoder_example.c
===================================================================
--- branches/theora-thusnelda/examples/encoder_example.c	2009-07-26 17:46:00 UTC (rev 16339)
+++ branches/theora-thusnelda/examples/encoder_example.c	2009-07-27 03:33:27 UTC (rev 16340)
@@ -61,7 +61,7 @@
 }
 #endif
 
-const char *optstring = "b:e:o:a:A:v:V:s:S:f:F:ck:\1";
+const char *optstring = "b:e:o:a:A:v:V:s:S:f:F:ck:d:\1\2\3\4";
 struct option options [] = {
   {"begin-time",required_argument,NULL,'b'},
   {"end-time",required_argument,NULL,'e'},
@@ -77,6 +77,10 @@
   {"vp3-compatible",no_argument,NULL,'c'},
   {"soft-target",no_argument,NULL,'\1'},
   {"keyframe-freq",required_argument,NULL,'k'},
+  {"buf-delay",required_argument,NULL,'d'},
+  {"two-pass",no_argument,NULL,'\2'},
+  {"first-pass",required_argument,NULL,'\3'},
+  {"second-pass",required_argument,NULL,'\4'},
   {NULL,0,NULL,0}
 };
 
@@ -126,6 +130,7 @@
 int video_r=-1;
 int video_q=-1;
 ogg_uint32_t keyframe_frequency=64;
+int buf_delay=-1;
 
 long begin_sec=-1;
 long begin_usec=0;
@@ -150,6 +155,19 @@
           "                                  higher/smoother overall. Soft target also\n"
           "                                  allows an optional -v setting to specify\n"
           "                                  a minimum allowed quality.\n\n"
+          "     --two-pass                   Compress input using two-pass rate control\n"
+          "                                  This option requires that the input to the\n"
+          "                                  to the encoder is seekable and performs\n"
+          "                                  both passes automatically.\n\n"
+          "     --first-pass <filename>      Perform first-pass of a two-pass rate\n"
+          "                                  controlled encoding, saving pass data to\n"
+          "                                  <filename> for a later second pass\n\n"
+          "     --second-pass <filename>     Perform second-pass of a two-pass rate\n"
+          "                                  controlled encoding, reading first-pass\n"
+          "                                  data from <filename>.  The first pass\n"
+          "                                  data must comre from a first encoding pass\n"
+          "                                  using identical input video to work\n"
+          "                                  properly.\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"
@@ -170,6 +188,14 @@
           "                                  The frame rate nominator divided by this\n"
           "                                  determinates the frame rate in units per tick\n"
           "   -k --keyframe-freq <n>         Keyframe frequency\n"
+          "   -d --buf-delay <n>             Buffer delay (in frames). Longer delays\n"
+          "                                  allow smoother rate adaptation and provide\n"
+          "                                  better overall quality, but require more\n"
+          "                                  client side buffering and add latency. The\n"
+          "                                  default value is the keyframe interval for\n"
+          "                                  one-pass encoding (or somewhat larger if\n"
+          "                                  --soft-target is used) and infinite for\n"
+          "                                  two-pass encoding.\n"
           "   -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"
@@ -970,145 +996,198 @@
   return audioflag;
 }
 
-int fetch_and_process_video(FILE *video,ogg_page *videopage,
-                            ogg_stream_state *to,
-                            th_enc_ctx *td,
-                            int videoflag){
-  /* You'll go to Hell for using static variables */
-  static ogg_int64_t         frames=0;
-  static int                 state=-1;
-  static unsigned char      *yuvframe[3];
-  static th_ycbcr_buffer     ycbcr;
-  ogg_packet                 op;
+static int                 frame_state=-1;
+static ogg_int64_t         frames=0;
+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 = (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;
-
+  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 */
 
-  if(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);
-
-    state=0;
+  /* 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++;
   }
-
-  /* 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(;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[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[state],yuvframe[2]);
-
-        frames++;
-        if(frames>=beginframe)
-          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 */
+  if(passno==2){
+    for(;;){
+      static unsigned char buffer[80];
+      static int buf_pos;
+      int bytes;
+      /*Ask the encoder how many bytes it would like.*/
+      bytes=th_encode_ctl(td,TH_ENCCTL_2PASS_IN,NULL,0);
+      if(bytes<0){
+        fprintf(stderr,"Error submitting pass data in second pass.\n");
+        exit(1);
       }
-
-      if(state<1){
-        /* can't get here unless YUV4MPEG stream has no video */
-        fprintf(stderr,"Video input contains no frames.\n");
+      /*If it's got enough, stop.*/
+      if(bytes==0)break;
+      /*Read in some more bytes, if necessary.*/
+      if(bytes>80-buf_pos)bytes=80-buf_pos;
+      if(bytes>0&&fread(buffer+buf_pos,1,bytes,twopass_file)<bytes){
+        fprintf(stderr,"Could not read frame data from two-pass data file!\n");
         exit(1);
       }
-
-      /* Theora is a one-frame-in,one-frame-out system; submit a frame
-         for compression and pull out the packet */
-
-      /*We submit the buffer to the library as if it were padded, but we do not
-         actually allocate space for the padding.
-        This is okay, because the library will never read data from the padded
-         region.
-        This is only currently true of the experimental encoder; do NOT do this
-         with the reference encoder.*/
-      ycbcr[0].width=frame_w;
-      ycbcr[0].height=frame_h;
-      ycbcr[0].stride=pic_w;
-      ycbcr[0].data=yuvframe[0]-pic_x-pic_y*pic_w;
-      ycbcr[1].width=frame_c_w;
-      ycbcr[1].height=frame_c_h;
-      ycbcr[1].stride=c_w;
-      ycbcr[1].data=yuvframe[0]+pic_sz-(pic_x/dst_c_dec_h)-
-       (pic_y/dst_c_dec_v)*c_w;
-      ycbcr[2].width=frame_c_w;
-      ycbcr[2].height=frame_c_h;
-      ycbcr[2].stride=c_w;
-      ycbcr[2].data=ycbcr[1].data+c_sz;
-
-      th_encode_ycbcr_in(td,ycbcr);
-
-      /* if there's only one frame, it's the last in the stream */
-      while(th_encode_packetout(td,state<2,&op)){
-        ogg_stream_packetin(to,&op);
+      /*And pass them off.*/
+      ret=th_encode_ctl(td,TH_ENCCTL_2PASS_IN,buffer,bytes);
+      if(ret<0){
+        fprintf(stderr,"Error submitting pass data in second pass.\n");
+        exit(1);
       }
+      /*If the encoder consumed the whole buffer, reset it.*/
+      if(ret>=bytes)buf_pos=0;
+      /*Otherwise remember how much it used.*/
+      else buf_pos+=ret;
+    }
+  }
+  /*We submit the buffer to the library as if it were padded, but we do not
+     actually allocate space for the padding.
+    This is okay, because the library will never read data from the padded
+     region.
+    This is only currently true of the experimental encoder; do NOT do this
+     with the reference encoder.*/
+  ycbcr[0].width=frame_w;
+  ycbcr[0].height=frame_h;
+  ycbcr[0].stride=pic_w;
+  ycbcr[0].data=yuvframe[0]-pic_x-pic_y*pic_w;
+  ycbcr[1].width=frame_c_w;
+  ycbcr[1].height=frame_c_h;
+  ycbcr[1].stride=c_w;
+  ycbcr[1].data=yuvframe[0]+pic_sz-(pic_x/dst_c_dec_h)-(pic_y/dst_c_dec_v)*c_w;
+  ycbcr[2].width=frame_c_w;
+  ycbcr[2].height=frame_c_h;
+  ycbcr[2].stride=c_w;
+  ycbcr[2].data=ycbcr[1].data+c_sz;
+  th_encode_ycbcr_in(td,ycbcr);
+  {
+    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;
+    int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+    if(bytes<0){
+      fprintf(stderr,"Could not read two-pass data from encoder.\n");
+      exit(1);
+    }
+    if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+      fprintf(stderr,"Unable to write to two-pass data file.\n");
+      exit(1);
+    }
+    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(passno==1 && frame_state<1){
+    /* need to read the final (summary) packet */
+    unsigned char *buffer;
+    int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+    if(bytes<0){
+      fprintf(stderr,"Could not read two-pass summary data from encoder.\n");
+      exit(1);
+    }
+    if(fseek(twopass_file,0,SEEK_SET)<0){
+      fprintf(stderr,"Unable to seek in two-pass data file.\n");
+      exit(1);
+    }
+    if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+      fprintf(stderr,"Unable to write to two-pass data file.\n");
+      exit(1);
+    }
+    fflush(twopass_file);
+  }
+  return ret;
+}
 
-      {
-        unsigned char *temp=yuvframe[0];
-        yuvframe[0]=yuvframe[1];
-        yuvframe[1]=temp;
-        state--;
-      }
 
-    }
+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);
   }
   return videoflag;
 }
@@ -1152,6 +1231,11 @@
 
   FILE *outfile = stdout;
 
+  FILE *twopass_file = NULL;
+  fpos_t video_rewind_pos;
+  int twopass=0;
+  int passno;
+
 #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"); */
@@ -1237,6 +1321,14 @@
       }
       break;
 
+    case 'd':
+      buf_delay=atoi(optarg);
+      if(buf_delay<=0){
+        fprintf(stderr,"Illegal buffer delay\n");
+        exit(1);
+      }
+      break;
+
     case 'b':
       {
         char *pos=strchr(optarg,':');
@@ -1287,6 +1379,30 @@
         }
       }
       break;
+    case '\2':
+      twopass=3; /* perform both passes */
+      twopass_file=tmpfile();
+      if(!twopass_file){
+        fprintf(stderr,"Unable to open temporary file for twopass data\n");
+        exit(1);
+      }
+      break;
+    case '\3':
+      twopass=1; /* perform first pass */
+      twopass_file=fopen(optarg,"wb");
+      if(!twopass_file){
+        fprintf(stderr,"Unable to open \'%s\' for twopass data\n",optarg);
+        exit(1);
+      }
+      break;
+    case '\4':
+      twopass=2; /* perform second pass */
+      twopass_file=fopen(optarg,"rb");
+      if(!twopass_file){
+        fprintf(stderr,"Unable to open twopass data file \'%s\'",optarg);
+        exit(1);
+      }
+      break;
 
     default:
       usage();
@@ -1313,99 +1429,36 @@
     optind++;
   }
 
-  /* yayness.  Set up Ogg output stream */
-  srand(time(NULL));
-  if(audio)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 frame size */
-  /* scale the picture size up to the nearest /16 and calculate offsets */
-  frame_w=pic_w+15&~0xF;
-  frame_h=pic_h+15&~0xF;
-  /*Force the offsets to be even so that chroma samples line up like we
-     expect.*/
-  pic_x=frame_w-pic_w>>1&~1;
-  pic_y=frame_h-pic_h>>1&~1;
-
-  th_info_init(&ti);
-  ti.frame_width=frame_w;
-  ti.frame_height=frame_h;
-  ti.pic_width=pic_w;
-  ti.pic_height=pic_h;
-  ti.pic_x=pic_x;
-  ti.pic_y=pic_y;
-  ti.fps_numerator=video_fps_n;
-  ti.fps_denominator=video_fps_d;
-  ti.aspect_numerator=video_par_n;
-  ti.aspect_denominator=video_par_d;
-  ti.colorspace=TH_CS_UNSPECIFIED;
-  /*Account for the Ogg page overhead.
-    This is 1 byte per 255 for lacing values, plus 26 bytes per 4096 bytes for
-     the page header, plus approximately 1/2 byte per packet (not accounted for
-     here).*/
-  ti.target_bitrate=(int)(64870*(ogg_int64_t)video_r>>16);
-  ti.quality=video_q;
-  ti.keyframe_granule_shift=ilog(keyframe_frequency-1);
-
-  if(dst_c_dec_h==2){
-    if(dst_c_dec_v==2)ti.pixel_fmt=TH_PF_420;
-    else ti.pixel_fmt=TH_PF_422;
-  }
-  else ti.pixel_fmt=TH_PF_444;
-
-  td=th_encode_alloc(&ti);
-  th_info_clear(&ti);
-
-  /* setting just the granule shift only allows power-of-two keyframe
-     spacing.  Set the actual requested spacing. */
-  ret=th_encode_ctl(td,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,&keyframe_frequency,
-                    sizeof(keyframe_frequency-1));
-  if(ret<0){
-    fprintf(stderr,"Could not set keyframe interval to %d.\n",(int)keyframe_frequency);
-  }
-
-  if(vp3_compatible){
-    ret=th_encode_ctl(td,TH_ENCCTL_SET_VP3_COMPATIBLE,&vp3_compatible,
-     sizeof(vp3_compatible));
-    if(ret<0||!vp3_compatible){
-      fprintf(stderr,"Could not enable strict VP3 compatibility.\n");
-      if(ret>=0){
-        fprintf(stderr,"Ensure your source format is supported by VP3.\n");
-        fprintf(stderr,
-         "(4:2:0 pixel format, width and height multiples of 16).\n");
+  if(twopass==3){
+    /* verify that the input is seekable! */
+    if(video){
+      if(fseek(video,0,SEEK_CUR)){
+        fprintf(stderr,"--two-pass (automatic two-pass) requires the video input\n"
+                "to be seekable.  For non-seekable input, encoder_example\n"
+                "must be run twice, first with the --first-pass option, then\n"
+                "with the --second-pass option.\n\n");
+        exit(1);
       }
+      if(fgetpos(video,&video_rewind_pos)<0){
+        fprintf(stderr,"Unable to determine start position of video data.\n");
+        exit(1);
+      }
     }
   }
 
-  if(soft_target){
-    /* reverse the rate control flags to favor a 'long time' strategy */
-    int arg = TH_RATECTL_CAP_UNDERFLOW;
-    ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_FLAGS,&arg,sizeof(arg));
-    if(ret<0)
-      fprintf(stderr,"Could not set encoder flags for --soft-target\n");
+  /* Set up Ogg output stream */
+  srand(time(NULL));
+  if(audio)ogg_stream_init(&vo,rand());
+  ogg_stream_init(&to,rand()); /* oops, add one ot the above */
 
-    if((keyframe_frequency*7>>1) > 5*video_fps_n/video_fps_d)
-      arg=keyframe_frequency*7>>1;
-    else
-      arg=30*video_fps_n/video_fps_d;
-    ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,&arg,sizeof(arg));
-    if(ret<0)
-      fprintf(stderr,"Could not set rate control buffer for --soft-target\n");
-  }
-
-  /* initialize Vorbis too, assuming we have audio to compress. */
-  if(audio){
+  /* initialize Vorbis assuming we have audio to compress. */
+  if(audio && twopass!=1){
     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,
-       (int)(64870*(ogg_int64_t)audio_r>>16),-1);
+                               (int)(64870*(ogg_int64_t)audio_r>>16),-1);
     if(ret){
       fprintf(stderr,"The Vorbis encoder could not set up a mode according to\n"
               "the requested quality or bitrate.\n\n");
@@ -1417,153 +1470,273 @@
     vorbis_block_init(&vd,&vb);
   }
 
-  /* write the bitstream header packets with proper page interleave */
-
-  th_comment_init(&tc);
-
-  /* first packet will get its own page automatically */
-  if(th_encode_flushheader(td,&tc,&op)<=0){
-    fprintf(stderr,"Internal Theora library error.\n");
-    exit(1);
-  }
-  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 */
-  for(;;){
-    ret=th_encode_flushheader(td,&tc,&op);
+  for(passno=(twopass==3?1:twopass);passno<=(twopass==3?2:twopass);passno++){
+    /* 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 frame size */
+    /* scale the picture size up to the nearest /16 and calculate offsets */
+    frame_w=pic_w+15&~0xF;
+    frame_h=pic_h+15&~0xF;
+    /*Force the offsets to be even so that chroma samples line up like we
+       expect.*/
+    pic_x=frame_w-pic_w>>1&~1;
+    pic_y=frame_h-pic_h>>1&~1;
+    th_info_init(&ti);
+    ti.frame_width=frame_w;
+    ti.frame_height=frame_h;
+    ti.pic_width=pic_w;
+    ti.pic_height=pic_h;
+    ti.pic_x=pic_x;
+    ti.pic_y=pic_y;
+    ti.fps_numerator=video_fps_n;
+    ti.fps_denominator=video_fps_d;
+    ti.aspect_numerator=video_par_n;
+    ti.aspect_denominator=video_par_d;
+    ti.colorspace=TH_CS_UNSPECIFIED;
+    /*Account for the Ogg page overhead.
+      This is 1 byte per 255 for lacing values, plus 26 bytes per 4096 bytes for
+       the page header, plus approximately 1/2 byte per packet (not accounted for
+       here).*/
+    ti.target_bitrate=(int)(64870*(ogg_int64_t)video_r>>16);
+    ti.quality=video_q;
+    ti.keyframe_granule_shift=ilog(keyframe_frequency-1);
+    if(dst_c_dec_h==2){
+      if(dst_c_dec_v==2)ti.pixel_fmt=TH_PF_420;
+      else ti.pixel_fmt=TH_PF_422;
+    }
+    else ti.pixel_fmt=TH_PF_444;
+    td=th_encode_alloc(&ti);
+    th_info_clear(&ti);
+    /* setting just the granule shift only allows power-of-two keyframe
+       spacing.  Set the actual requested spacing. */
+    ret=th_encode_ctl(td,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,&keyframe_frequency,
+                      sizeof(keyframe_frequency-1));
     if(ret<0){
+      fprintf(stderr,"Could not set keyframe interval to %d.\n",(int)keyframe_frequency);
+    }
+    if(vp3_compatible){
+      ret=th_encode_ctl(td,TH_ENCCTL_SET_VP3_COMPATIBLE,&vp3_compatible,
+       sizeof(vp3_compatible));
+      if(ret<0||!vp3_compatible){
+        fprintf(stderr,"Could not enable strict VP3 compatibility.\n");
+        if(ret>=0){
+          fprintf(stderr,"Ensure your source format is supported by VP3.\n");
+          fprintf(stderr,
+           "(4:2:0 pixel format, width and height multiples of 16).\n");
+        }
+      }
+    }
+    if(soft_target){
+      /* reverse the rate control flags to favor a 'long time' strategy */
+      int arg = TH_RATECTL_CAP_UNDERFLOW;
+      ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_FLAGS,&arg,sizeof(arg));
+      if(ret<0)
+        fprintf(stderr,"Could not set encoder flags for --soft-target\n");
+      /* Default buffer control is overridden on two-pass */
+      if(!twopass&&buf_delay<0){
+        if((keyframe_frequency*7>>1) > 5*video_fps_n/video_fps_d)
+          arg=keyframe_frequency*7>>1;
+        else
+          arg=30*video_fps_n/video_fps_d;
+        ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,&arg,sizeof(arg));
+        if(ret<0)
+          fprintf(stderr,"Could not set rate control buffer for --soft-target\n");
+      }
+    }
+    /* set up two-pass if needed */
+    if(passno==1){
+      unsigned char *buffer;
+      int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+      if(bytes<0){
+        fprintf(stderr,"Could not set up the first pass of two-pass mode.\n");
+        fprintf(stderr,"Did you remember to specify an estimated bitrate?\n");
+        exit(1);
+      }
+      /*Perform a seek test to ensure we can overwrite this placeholder data at
+         the end; this is better than letting the user sit through a whole encode
+         only to find out their pass 1 file is useless at the end.*/
+      if(fseek(twopass_file,0,SEEK_SET)<0){
+        fprintf(stderr,"Unable to seek in two-pass data file.\n");
+        exit(1);
+      }
+      if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+        fprintf(stderr,"Unable to write to two-pass data file.\n");
+        exit(1);
+      }
+      fflush(twopass_file);
+    }
+    if(passno==2){
+      /* enable second pass here, actual data feeding comes later */
+      if(th_encode_ctl(td,TH_ENCCTL_2PASS_IN,NULL,0)<0){
+        fprintf(stderr,"Could not set up the second pass of two-pass mode.\n");
+        exit(1);
+      }
+      if(twopass==3){
+        /* 'automatic' second pass */
+        if(fsetpos(video,&video_rewind_pos)<0){
+          fprintf(stderr,"Could not rewind video input file for second pass!\n");
+          exit(1);
+        }
+        frame_state=0;
+        frames=0;
+      }
+    }
+    if(passno!=1&&buf_delay>=0){
+      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");
+      }
+    }
+    /* write the bitstream header packets with proper page interleave */
+    th_comment_init(&tc);
+    /* first packet will get its own page automatically */
+    if(th_encode_flushheader(td,&tc,&op)<=0){
       fprintf(stderr,"Internal Theora library error.\n");
       exit(1);
     }
-    else if(!ret)break;
-    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. */
-  for(;;){
-    int result = ogg_stream_flush(&to,&og);
-      if(result<0){
-        /* can't get here */
+    if(passno!=1){
+      ogg_stream_packetin(&to,&op);
+      if(ogg_stream_pageout(&to,&og)!=1){
         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){
+      fwrite(og.header,1,og.header_len,outfile);
+      fwrite(og.body,1,og.body_len,outfile);
+    }
+    /* create the remaining theora headers */
     for(;;){
-      int result=ogg_stream_flush(&vo,&og);
-      if(result<0){
-        /* can't get here */
+      ret=th_encode_flushheader(td,&tc,&op);
+      if(ret<0){
+        fprintf(stderr,"Internal Theora library error.\n");
+        exit(1);
+      }
+      else if(!ret)break;
+      if(passno!=1)ogg_stream_packetin(&to,&op);
+    }
+    if(audio && passno!=1){
+      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);
       }
-      if(result==0)break;
       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);
     }
-  }
-
-  /* setup complete.  Raw processing loop */
-  fprintf(stderr,"Compressing....\n");
-  for(;;){
-    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,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 */
-    {
+    /* Flush the rest of our headers. This ensures
+       the actual data in each stream will start
+       on a new page, as per spec. */
+    if(passno!=1){
+      for(;;){
+        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 && passno!=1){
+      for(;;){
+        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 */
+      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;
-      double audiotime=
+      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;
-      double videotime=
+        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)
+        if(!audioflag){
+          audio_or_video=1;
+        } else if(!videoflag) {
           audio_or_video=0;
-        else
-          audio_or_video=1;
+        } 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;
+        }
       }
-
-      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;
-      }
-      if(timebase > 0)
-      {
+      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);
-
+        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(audio){
     ogg_stream_clear(&vo);
     vorbis_block_clear(&vb);
@@ -1574,12 +1747,12 @@
   }
   if(video){
     ogg_stream_clear(&to);
-    th_encode_free(td);
     th_comment_clear(&tc);
     if(video!=stdin)fclose(video);
   }
 
   if(outfile && outfile!=stdout)fclose(outfile);
+  if(twopass_file)fclose(twopass_file);
 
   fprintf(stderr,"\r   \ndone.\n\n");
 

Modified: branches/theora-thusnelda/include/theora/theoraenc.h
===================================================================
--- branches/theora-thusnelda/include/theora/theoraenc.h	2009-07-26 17:46:00 UTC (rev 16339)
+++ branches/theora-thusnelda/include/theora/theoraenc.h	2009-07-27 03:33:27 UTC (rev 16340)
@@ -200,7 +200,7 @@
  *  requested.
  * The actual value set will be returned.
  *
- * \param[in] _buf  <tt>int</tt>: Requested size of the reservoir measured in
+ * \param[in]  _buf <tt>int</tt>: Requested size of the reservoir measured in
  *                   frames.
  * \param[out] _buf <tt>int</tt>: The actual size of the reservoir set.
  * \retval TH_EFAULT \a _enc_ctx or \a _buf is <tt>NULL</tt>.
@@ -211,6 +211,80 @@
  * \retval TH_IMPL   Not supported by this implementation in the current
  *                    encoding mode.*/
 #define TH_ENCCTL_SET_RATE_BUFFER (22)
+/**Enable pass 1 of two-pass encoding mode and retrieve the first pass metrics.
+ * Pass 1 mode must be enabled before the first frame is encoded, and a target
+ *  bitrate must have already been specified to the encoder.
+ * Although this does not have to be the exact rate that will be used in the
+ *  second pass, closer values may produce better results.
+ * The first call returns the size of the two-pass header data, along with some
+ *  placeholder content, and sets the encoder into pass 1 mode implicitly.
+ * This call sets the encoder to pass 1 mode implicitly.
+ * Then, a subsequent call must be made after each call to
+ *  th_encode_ycbcr_in() to retrieve the metrics for that frame.
+ * An additional, final call must be made to retrieve the summary data,
+ *  containing such information as the total number of frames, etc.
+ * This must be stored in place of the placeholder data that was returned
+ *  in the first call, before the frame metrics data.
+ * All of this data must be presented back to the encoder during pass 2 using
+ *  #TH_ENCCTL_2PASS_IN.
+ *
+ * \param[out] <tt>char *</tt>_buf: Returns a pointer to internal storage
+ *              containing the two pass metrics data.
+ *             This storage is only valid until the next call, or until the
+ *              encoder context is freed, and must be copied by the
+ *              application.
+ * \retval >=0       The number of bytes of metric data available in the
+ *                    returned buffer.
+ * \retval TH_EFAULT \a _enc_ctx or \a _buf is <tt>NULL</tt>.
+ * \retval TH_EINVAL \a _buf_sz is not <tt>sizeof(char *)</tt>, no target
+ *                    bitrate has been set, or the first call was made after
+ *                    the first frame was submitted for encoding.
+ * \retval TH_IMPL   Not supported by this implementation.*/
+#define TH_ENCCTL_2PASS_OUT (24)
+/**Submits two-pass encoding metric data collected the first encoding pass to
+ *  the second pass.
+ * The first call must be made before the first frame is encoded, and sets the
+ *  encoder to pass 2 mode implicitly.
+ * The encoder may require reading data from some or all of the frames in
+ *  advance, depending on, e.g., the reservoir size used in the second pass.
+ * You must call this function repeatedly before each frame to provide data
+ *  until either a) it fails to consume all of the data presented or b) all of
+ *  the pass 1 data has been consumed.
+ * In the first case, you must save the remaining data to be presented after
+ *  the next frame.
+ * You can call this function with a NULL argument to get an upper bound on
+ *  the number of bytes that will be required before the next frame.
+ *
+ * When pass 2 is first enabled, the default bit reservoir is set to the entire
+ *  file; this gives maximum flexibility but can lead to very high peak rates.
+ * You can subsequently set it to another value with #TH_ENCCTL_SET_RATE_BUFFER
+ *  (e.g., to set it to the keyframe interval for non-live streaming), however,
+ *  you may then need to provide more data before the next frame.
+ *
+ * \param[in] _buf <tt>char[]</tt>: A buffer containing the data returned by
+ *                  #TH_ENCCTL_2PASS_OUT in pass 1.
+ *                 You may pass <tt>NULL</tt> for \a _buf to return an upper
+ *                  bound on the number of additional bytes needed before the
+ *                  next frame.
+ *                 The summary data returned at the end of pass 1 must be at
+ *                  the head of the buffer on the first call with a
+ *                  non-<tt>NULL</tt> \a _buf, and the placeholder data
+ *                  returned at the start of pass 1 should be omitted.
+ *                 After each call you should advance this buffer by the number
+ *                  of bytes consumed.
+ * \retval >0            The number of bytes of metric data required/consumed.
+ * \retval 0             No more data is required before the next frame.
+ * \retval TH_EFAULT     \a _enc_ctx is <tt>NULL</tt>.
+ * \retval TH_EINVAL     The first call was made after the first frame was
+ *                        submitted for encoding.
+ * \retval TH_ENOTFORMAT The data did not appear to be pass 1 from a compatible
+ *                        implementation of this library.
+ * \retval TH_EBADHEADER The data was invalid; this may be returned when
+ *                        attempting to read an aborted pass 1 file that still
+ *                        has the placeholder data in place of the summary
+ *                        data.
+ * \retval TH_IMPL       Not supported by this implementation.*/
+#define TH_ENCCTL_2PASS_IN (26)
 /*@}*/
 
 
@@ -240,6 +314,7 @@
 /*@}*/
 
 
+
 /**The quantization parameters used by VP3.*/
 extern const th_quant_info TH_VP31_QUANT_INFO;
 

Modified: branches/theora-thusnelda/lib/dec/decode.c
===================================================================
--- branches/theora-thusnelda/lib/dec/decode.c	2009-07-26 17:46:00 UTC (rev 16339)
+++ branches/theora-thusnelda/lib/dec/decode.c	2009-07-27 03:33:27 UTC (rev 16340)
@@ -1066,7 +1066,6 @@
       eobs=oc_dec_ac_coeff_unpack(_dec,zzi,huff_idxs,ntoks_left,eobs);
     }
   }
-
   /*TODO: eobs should be exactly zero, or 4096 or greater.
     The second case occurs when an EOB run of size zero is encountered, which
      gets treated as an infinite EOB run (where infinity is PTRDIFF_MAX).
@@ -1555,10 +1554,7 @@
       else{
         int ebflag;
         token=dct_tokens[ti[zzi]++];
-        /*This is equivalent to
-            ebflag=OC_DCT_TOKEN_EXTRA_BITS[token]!=0;
-           but should be simpler on platforms that can use 16-bit immediates.*/
-        ebflag=-0x1E08>>token&1;
+        ebflag=OC_DCT_TOKEN_EXTRA_BITS[token]!=0;
         eb=extra_bits[ebi[zzi]]&-ebflag;
         ebi[zzi]+=ebflag;
         if(token<OC_NDCT_EOB_TOKEN_MAX){

Modified: branches/theora-thusnelda/lib/enc/analyze.c
===================================================================
--- branches/theora-thusnelda/lib/enc/analyze.c	2009-07-26 17:46:00 UTC (rev 16339)
+++ branches/theora-thusnelda/lib/enc/analyze.c	2009-07-27 03:33:27 UTC (rev 16340)
@@ -1213,7 +1213,7 @@
 }
 
 /*Analysis stage for an INTRA frame.*/
-int oc_enc_analyze_intra(oc_enc_ctx *_enc,int _recode){
+void oc_enc_analyze_intra(oc_enc_ctx *_enc,int _recode){
   oc_enc_pipeline_state   pipe;
   const unsigned char    *map_idxs;
   int                     nmap_idxs;
@@ -1291,7 +1291,6 @@
   refi=_enc->state.ref_frame_idx[OC_FRAME_SELF];
   for(pli=0;pli<3;pli++)oc_state_borders_fill_caps(&_enc->state,refi,pli);
   _enc->state.ntotal_coded_fragis=_enc->state.nfrags;
-  return 0;
 }
 
 
@@ -1838,7 +1837,7 @@
   oc_mode_set_cost(_modec,_enc->lambda);
 }
 
-int oc_enc_analyze(oc_enc_ctx *_enc,int _frame_type,int _recode){
+int oc_enc_analyze_inter(oc_enc_ctx *_enc,int _allow_keyframe,int _recode){
   oc_set_chroma_mvs_func  set_chroma_mvs;
   oc_enc_pipeline_state   pipe;
   oc_qii_state            intra_luma_qs;
@@ -1869,13 +1868,12 @@
   unsigned                sbi_end;
   int                     refi;
   int                     pli;
-  if(_frame_type==OC_INTRA_FRAME)return oc_enc_analyze_intra(_enc,_recode);
   set_chroma_mvs=OC_SET_CHROMA_MVS_TABLE[_enc->state.info.pixel_fmt];
-  _enc->state.frame_type=_frame_type;
+  _enc->state.frame_type=OC_INTER_FRAME;
   oc_mode_scheme_chooser_reset(&_enc->chooser);
   oc_enc_tokenize_start(_enc);
   oc_enc_pipeline_init(_enc,&pipe);
-  oc_qii_state_init(&intra_luma_qs);
+  if(_allow_keyframe)oc_qii_state_init(&intra_luma_qs);
   _enc->mv_bits[0]=_enc->mv_bits[1]=0;
   interbits=intrabits=0;
   last_mv[0]=last_mv[1]=prior_mv[0]=prior_mv[1]=0;
@@ -1943,12 +1941,14 @@
         }
         oc_mb_intra_satd(_enc,mbi,intra_satd);
         /*Estimate the cost of coding this MB in a keyframe.*/
-        oc_cost_intra(_enc,modes+OC_MODE_INTRA,mbi,
-         pipe.fr+0,&intra_luma_qs,intra_satd,OC_NOSKIP);
-        intrabits+=modes[OC_MODE_INTRA].rate;
-        for(bi=0;bi<4;bi++){
-          oc_qii_state_advance(&intra_luma_qs,&intra_luma_qs,
-           modes[OC_MODE_INTRA].qii[bi]);
+        if(_allow_keyframe){
+          oc_cost_intra(_enc,modes+OC_MODE_INTRA,mbi,
+           pipe.fr+0,&intra_luma_qs,intra_satd,OC_NOSKIP);
+          intrabits+=modes[OC_MODE_INTRA].rate;
+          for(bi=0;bi<4;bi++){
+            oc_qii_state_advance(&intra_luma_qs,&intra_luma_qs,
+             modes[OC_MODE_INTRA].qii[bi]);
+          }
         }
         /*Estimate the cost in a delta frame for various modes.*/
         oc_skip_cost(_enc,&pipe,mbi,skip_ssd);
@@ -2161,7 +2161,7 @@
   for(pli=0;pli<3;pli++)oc_state_borders_fill_caps(&_enc->state,refi,pli);
   /*Finish adding flagging overhead costs to inter bit counts to determine if
      we should have coded a key frame instead.*/
-  if(_enc->state.frame_type!=OC_INTRA_FRAME){
+  if(_allow_keyframe){
     if(interbits>intrabits)return 1;
     /*Technically the chroma plane counts are over-estimations, because they
        don't account for continuing runs from the luma planes, but the

Modified: branches/theora-thusnelda/lib/enc/encint.h
===================================================================
--- branches/theora-thusnelda/lib/enc/encint.h	2009-07-26 17:46:00 UTC (rev 16339)
+++ branches/theora-thusnelda/lib/enc/encint.h	2009-07-27 03:33:27 UTC (rev 16340)
@@ -35,6 +35,8 @@
 typedef struct oc_mb_enc_info         oc_mb_enc_info;
 typedef struct oc_mode_scheme_chooser oc_mode_scheme_chooser;
 typedef struct oc_iir_filter          oc_iir_filter;
+typedef struct oc_log_linear_fit      oc_log_linear_fit;
+typedef struct oc_frame_metrics       oc_frame_metrics;
 typedef struct oc_rc_state            oc_rc_state;
 typedef struct th_enc_ctx             oc_enc_ctx;
 typedef struct oc_token_checkpoint    oc_token_checkpoint;
@@ -171,53 +173,119 @@
   ogg_int32_t y[2];
 };
 
+
+
+/*A linear fit for the log-domain scale factors used in 2-pass.*/
+struct oc_log_linear_fit{
+  ogg_int64_t  x;
+  ogg_int64_t  y;
+  ogg_int64_t  x2;
+  ogg_int64_t  xy;
+  ogg_uint32_t n;
+};
+
+
+
+/*The 2-pass metrics associated with a single frame.*/
+struct oc_frame_metrics{
+  ogg_int32_t   scale;
+  unsigned      dup_count:31;
+  unsigned      frame_type:1;
+};
+
+
+
 /*Rate control state information.*/
 struct oc_rc_state{
   /*The target average bits per frame.*/
-  ogg_int64_t     bits_per_frame;
+  ogg_int64_t        bits_per_frame;
   /*The current buffer fullness (bits available to be used).*/
-  ogg_int64_t     fullness;
+  ogg_int64_t        fullness;
   /*The target buffer fullness.
     This is where we'd like to be by the last keyframe the appears in the next
      buf_delay frames.*/
-  ogg_int64_t     target;
+  ogg_int64_t        target;
   /*The maximum buffer fullness (total size of the buffer).*/
-  ogg_int64_t     max;
+  ogg_int64_t        max;
   /*The log of the number of pixels in a frame in Q57 format.*/
-  ogg_int64_t     log_npixels;
+  ogg_int64_t        log_npixels;
   /*The exponent used in the rate model in Q8 format.*/
-  unsigned        exp[2];
+  unsigned           exp[2];
   /*The number of frames to distribute the buffer usage over.*/
-  int             buf_delay;
+  int                buf_delay;
   /*The total drop count from the previous frame.
     This includes duplicates explicitly requested via the
      TH_ENCCTL_SET_DUP_COUNT API as well as frames we chose to drop ourselves.*/
-  ogg_uint32_t    prev_drop_count;
+  ogg_uint32_t       prev_drop_count;
   /*The log of an estimated scale factor used to obtain the real framerate, for
      VFR sources or, e.g., 12 fps content doubled to 24 fps, etc.*/
-  ogg_int64_t     log_drop_scale;
+  ogg_int64_t        log_drop_scale;
   /*The log of estimated scale factor for the rate model in Q57 format.*/
-  ogg_int64_t     log_scale[2];
+  ogg_int64_t        log_scale[2];
   /*The log of the target quantizer level in Q57 format.*/
-  ogg_int64_t     log_qtarget;
+  ogg_int64_t        log_qtarget;
   /*Will we drop frames to meet bitrate target?*/
-  unsigned char   drop_frames;
+  unsigned char      drop_frames;
   /*Do we respect the maximum buffer fullness?*/
-  unsigned char   cap_overflow;
+  unsigned char      cap_overflow;
   /*Can the reservoir go negative?*/
-  unsigned char   cap_underflow;
+  unsigned char      cap_underflow;
   /*Second-order lowpass filters to track scale and VFR.*/
-  oc_iir_filter   scalefilter[2];
-  oc_iir_filter   vfrfilter;
+  oc_iir_filter      scalefilter[2];
+  oc_iir_filter      vfrfilter;
+  /*Two-pass mode state.
+    0 => 1-pass encoding.
+    1 => 1st pass of 2-pass encoding.
+    2 => 2nd pass of 2-pass encoding.*/
+  int                twopass;
+  /*Buffer for current frame metrics.*/
+  unsigned char      twopass_buffer[48];
+  /*The number of bytes in the frame metrics buffer.
+    When 2-pass encoding is enabled, this is set to 0 after each frame is
+     submitted, and must be non-zero before the next frame will be accepted.*/
+  int                twopass_buffer_bytes;
+  int                twopass_buffer_fill;
+  /*Whether or not to force the next frame to be a keyframe.*/
+  unsigned char      twopass_force_kf;
+  /*The metrics for the previous frame.*/
+  oc_frame_metrics   prev_metrics;
+  /*The metrics for the current frame.*/
+  oc_frame_metrics   cur_metrics;
+  /*The buffered metrics for future frames.*/
+  oc_frame_metrics  *frame_metrics;
+  int                nframe_metrics;
+  int                cframe_metrics;
+  /*The index of the current frame in the circular metric buffer.*/
+  int                frame_metrics_head;
+  /*The frame count of each type (keyframes, delta frames, and dup frames);
+     32 bits limits us to 2.268 years at 60 fps.*/
+  ogg_uint32_t       frames_total[3];
+  /*The number of frames of each type yet to be processed.*/
+  ogg_uint32_t       frames_left[3];
+  /*The sum of the scale values for each frame type.*/
+  ogg_int64_t        scale_sum[2];
+  /*The start of the window over which the current scale sums are taken.*/
+  int                scale_window0;
+  /*The end of the window over which the current scale sums are taken.*/
+  int                scale_window_end;
+  /*The frame count of each type in the current 2-pass window; this does not
+     include dup frames.*/
+  int                nframes[3];
+  /*Bias correction fits for the 1st-pass scale factors.*/
+  oc_log_linear_fit  corr[2];
 };
 
 
+void oc_rc_state_init(oc_rc_state *_rc,oc_enc_ctx *_enc);
+void oc_rc_state_clear(oc_rc_state *_rc);
+
+void oc_enc_rc_resize(oc_enc_ctx *_enc);
+int oc_enc_select_qi(oc_enc_ctx *_enc,int _qti,int _clamp);
 void oc_enc_calc_lambda(oc_enc_ctx *_enc,int _frame_type);
-void oc_rc_state_init(oc_rc_state *_rc,const oc_enc_ctx *_enc);
-void oc_rc_state_reinit(oc_rc_state *_rc,const oc_enc_ctx *_enc);
 int oc_enc_update_rc_state(oc_enc_ctx *_enc,
  long _bits,int _qti,int _qi,int _trial,int _droppable);
-int oc_enc_select_qi(oc_enc_ctx *_enc,int _qti,int _clamp);
+int oc_enc_rc_2pass_out(oc_enc_ctx *_enc,unsigned char **_buf);
+int oc_enc_rc_2pass_in(oc_enc_ctx *_enc,unsigned char *_buf,size_t _bytes);
 
 
 
@@ -307,7 +375,8 @@
 };
 
 
-int oc_enc_analyze(oc_enc_ctx *_enc,int _frame_type,int _recode);
+void oc_enc_analyze_intra(oc_enc_ctx *_enc,int _recode);
+int oc_enc_analyze_inter(oc_enc_ctx *_enc,int _allow_keyframe,int _recode);
 #if defined(OC_COLLECT_METRICS)
 void oc_enc_mode_metrics_collect(oc_enc_ctx *_enc);
 void oc_enc_mode_metrics_dump(oc_enc_ctx *_enc);

Modified: branches/theora-thusnelda/lib/enc/encode.c
===================================================================
--- branches/theora-thusnelda/lib/enc/encode.c	2009-07-26 17:46:00 UTC (rev 16339)
+++ branches/theora-thusnelda/lib/enc/encode.c	2009-07-27 03:33:27 UTC (rev 16340)
@@ -1099,12 +1099,13 @@
   oc_enc_set_quant_params(_enc,NULL);
   _enc->state.qis[0]=_enc->state.info.quality;
   _enc->state.nqis=1;
-  if(_enc->state.info.target_bitrate>0)oc_rc_state_init(&_enc->rc,_enc);
+  oc_rc_state_init(&_enc->rc,_enc);
   return 0;
 }
 
 static void oc_enc_clear(th_enc_ctx *_enc){
   int pli;
+  oc_rc_state_clear(&_enc->rc);
 #if defined(OC_COLLECT_METRICS)
   oc_enc_mode_metrics_dump(_enc);
 #endif
@@ -1141,7 +1142,7 @@
     _enc->state.nqis=1;
   }
   oc_enc_calc_lambda(_enc,OC_INTRA_FRAME);
-  oc_enc_analyze(_enc,OC_INTRA_FRAME,_recode);
+  oc_enc_analyze_intra(_enc,_recode);
   oc_enc_frame_pack(_enc);
   /*On the first frame, the previous call was an initial dry-run to prime
      feed-forward statistics.*/
@@ -1160,7 +1161,7 @@
     _enc->state.nqis=1;
   }
   oc_enc_calc_lambda(_enc,OC_INTER_FRAME);
-  if(oc_enc_analyze(_enc,OC_INTER_FRAME,_recode)){
+  if(oc_enc_analyze_inter(_enc,_enc->rc.twopass!=2,_recode)){
     /*Mode analysis thinks this should have been a keyframe; start over.*/
     oc_enc_compress_keyframe(_enc,1);
   }
@@ -1333,10 +1334,27 @@
       if(_enc->state.info.target_bitrate<=0)return TH_EINVAL;
       set=*(int *)_buf;
       _enc->rc.buf_delay=set;
-      oc_rc_state_reset(&_enc->rc,_enc);
+      oc_enc_rc_resize(_enc);
       *(int *)_buf=_enc->rc.buf_delay;
       return 0;
     }break;
+    case TH_ENCCTL_2PASS_OUT:{
+      if(_enc==NULL||_buf==NULL)return TH_EFAULT;
+      if(_enc->state.info.target_bitrate<=0||
+       _enc->state.curframe_num>=0&&_enc->rc.twopass!=1||
+       _buf_sz!=sizeof(unsigned char *)){
+        return TH_EINVAL;
+      }
+      return oc_enc_rc_2pass_out(_enc,(unsigned char **)_buf);
+    }break;
+    case TH_ENCCTL_2PASS_IN:{
+      if(_enc==NULL)return TH_EFAULT;
+      if(_enc->state.info.target_bitrate<=0||
+       _enc->state.curframe_num>=0&&_enc->rc.twopass!=2){
+        return TH_EINVAL;
+      }
+      return oc_enc_rc_2pass_in(_enc,_buf,_buf_sz);
+    }break;
     default:return TH_EIMPL;
   }
 }
@@ -1426,7 +1444,6 @@
   }
 }
 
-#include<stdio.h>
 int th_encode_ycbcr_in(th_enc_ctx *_enc,th_ycbcr_buffer _img){
   th_ycbcr_buffer img;
   int             cframe_width;
@@ -1443,6 +1460,8 @@
   /*Step 1: validate parameters.*/
   if(_enc==NULL||_img==NULL)return TH_EFAULT;
   if(_enc->packet_state==OC_PACKET_DONE)return TH_EINVAL;
+  /*TODO: Fix this.*/
+  if(_enc->rc.twopass&&_enc->rc.twopass_buffer_bytes==0)return TH_EINVAL;
   if((ogg_uint32_t)_img[0].width!=_enc->state.info.frame_width||
    (ogg_uint32_t)_img[0].height!=_enc->state.info.frame_height){
     return TH_EINVAL;
@@ -1494,7 +1513,7 @@
   /*Step 4: Compress the frame.*/
   /*Start with a keyframe, and don't allow the generation of invalid files that
      overflow the keyframe_granule_shift.*/
-  if(_enc->state.curframe_num==0||
+  if(_enc->rc.twopass_force_kf||_enc->state.curframe_num==0||
    _enc->state.curframe_num-_enc->state.keyframe_num+_enc->dup_count>=
    _enc->keyframe_frequency_force){
     oc_enc_compress_keyframe(_enc,0);
@@ -1517,6 +1536,7 @@
   _enc->packet_state=OC_PACKET_READY;
   _enc->prev_dup_count=_enc->nqueued_dups=_enc->dup_count;
   _enc->dup_count=0;
+  _enc->rc.twopass_buffer_bytes=0;
 #if defined(OC_DUMP_IMAGES)
   oc_enc_set_granpos(_enc);
   oc_state_dump_frame(&_enc->state,OC_FRAME_IO,"src");
@@ -1529,8 +1549,15 @@
   if(_enc==NULL||_op==NULL)return TH_EFAULT;
   if(_enc->packet_state==OC_PACKET_READY){
     _enc->packet_state=OC_PACKET_EMPTY;
-    _op->packet=oggpackB_get_buffer(&_enc->opb);
-    _op->bytes=oggpackB_bytes(&_enc->opb);
+    /*For the first pass in 2-pass mode, don't emit any packet data.*/
+    if(_enc->rc.twopass==1){
+      _op->packet=NULL;
+      _op->bytes=0;
+    }
+    else{
+      _op->packet=oggpackB_get_buffer(&_enc->opb);
+      _op->bytes=oggpackB_bytes(&_enc->opb);
+    }
   }
   else if(_enc->packet_state==OC_PACKET_EMPTY){
     if(_enc->nqueued_dups>0){
@@ -1550,5 +1577,6 @@
   oc_enc_set_granpos(_enc);
   _op->packetno=th_granule_frame(_enc,_enc->state.granpos)+3;
   _op->granulepos=_enc->state.granpos;
+  if(_last_p)_enc->packet_state=OC_PACKET_DONE;
   return 1+_enc->nqueued_dups;
 }

Modified: branches/theora-thusnelda/lib/enc/rate.c
===================================================================
--- branches/theora-thusnelda/lib/enc/rate.c	2009-07-26 17:46:00 UTC (rev 16339)
+++ branches/theora-thusnelda/lib/enc/rate.c	2009-07-27 03:33:27 UTC (rev 16340)
@@ -154,8 +154,11 @@
   /*If rate control is active, use the lambda for the _target_ quantizer.
     This allows us to scale to rates slightly lower than we'd normally be able
      to reach, and give the rate control a semblance of "fractional qi"
-     precision.*/
-  if(_enc->state.info.target_bitrate>0)lq=_enc->rc.log_qtarget;
+     precision.
+    TODO: Add API for changing QI, and allow extra precision.*/
+  if(_enc->state.info.target_bitrate>0&&_enc->rc.twopass!=1){
+    lq=_enc->rc.log_qtarget;
+  }
   else lq=_enc->log_qavg[_qti][qi];
   /*The resulting lambda value is less than 0x500000.*/
   _enc->lambda=(int)oc_bexp64(2*lq-0x4780BD468D6B62BLL);
@@ -202,158 +205,168 @@
 }
 
 
-void oc_rc_state_reset(oc_rc_state *_rc,const oc_enc_ctx *_enc){
+
+static void oc_enc_rc_reset(oc_enc_ctx *_enc){
   ogg_int64_t npixels;
   ogg_int64_t ibpp;
   /*TODO: These parameters should be exposed in a th_encode_ctl() API.*/
-  _rc->bits_per_frame=(_enc->state.info.target_bitrate*
+  _enc->rc.bits_per_frame=(_enc->state.info.target_bitrate*
    (ogg_int64_t)_enc->state.info.fps_denominator)/
    _enc->state.info.fps_numerator;
   /*Insane framerates or frame sizes mean insane bitrates.
     Let's not get carried away.*/
-  if(_rc->bits_per_frame>0x400000000000LL){
-    _rc->bits_per_frame=(ogg_int64_t)0x400000000000LL;
+  if(_enc->rc.bits_per_frame>0x400000000000LL){
+    _enc->rc.bits_per_frame=(ogg_int64_t)0x400000000000LL;
   }
-  else if(_rc->bits_per_frame<32)_rc->bits_per_frame=32;
-  _rc->buf_delay=OC_MAXI(_rc->buf_delay,12);
-  _rc->max=_rc->bits_per_frame*_rc->buf_delay;
+  else if(_enc->rc.bits_per_frame<32)_enc->rc.bits_per_frame=32;
+  _enc->rc.buf_delay=OC_MAXI(_enc->rc.buf_delay,12);
+  _enc->rc.max=_enc->rc.bits_per_frame*_enc->rc.buf_delay;
   /*Start with a buffer fullness of 75%.
     We can require fully half the buffer for a keyframe, and so this initial
      level gives us maximum flexibility for over/under-shooting in subsequent
      frames.*/
-  _rc->target=_rc->fullness=(_rc->max+1>>1)+(_rc->max+2>>2);
+  _enc->rc.target=_enc->rc.fullness=(_enc->rc.max+1>>1)+(_enc->rc.max+2>>2);
   /*Pick exponents and initial scales for quantizer selection.*/
   npixels=_enc->state.info.frame_width*
    (ogg_int64_t)_enc->state.info.frame_height;
-  _rc->log_npixels=oc_blog64(npixels);
-  ibpp=npixels/_rc->bits_per_frame;
+  _enc->rc.log_npixels=oc_blog64(npixels);
+  ibpp=npixels/_enc->rc.bits_per_frame;
   if(ibpp<1){
-    _rc->exp[0]=59;
-    _rc->log_scale[0]=oc_blog64(1997)-OC_Q57(8);
+    _enc->rc.exp[0]=59;
+    _enc->rc.log_scale[0]=oc_blog64(1997)-OC_Q57(8);
   }
   else if(ibpp<2){
-    _rc->exp[0]=55;
-    _rc->log_scale[0]=oc_blog64(1604)-OC_Q57(8);
+    _enc->rc.exp[0]=55;
+    _enc->rc.log_scale[0]=oc_blog64(1604)-OC_Q57(8);
   }
   else{
-    _rc->exp[0]=48;
-    _rc->log_scale[0]=oc_blog64(834)-OC_Q57(8);
+    _enc->rc.exp[0]=48;
+    _enc->rc.log_scale[0]=oc_blog64(834)-OC_Q57(8);
   }
   if(ibpp<4){
-    _rc->exp[1]=100;
-    _rc->log_scale[1]=oc_blog64(2249)-OC_Q57(8);
+    _enc->rc.exp[1]=100;
+    _enc->rc.log_scale[1]=oc_blog64(2249)-OC_Q57(8);
   }
   else if(ibpp<8){
-    _rc->exp[1]=95;
-    _rc->log_scale[1]=oc_blog64(1751)-OC_Q57(8);
+    _enc->rc.exp[1]=95;
+    _enc->rc.log_scale[1]=oc_blog64(1751)-OC_Q57(8);
   }
   else{
-    _rc->exp[1]=73;
-    _rc->log_scale[1]=oc_blog64(1260)-OC_Q57(8);
+    _enc->rc.exp[1]=73;
+    _enc->rc.log_scale[1]=oc_blog64(1260)-OC_Q57(8);
   }
-  _rc->prev_drop_count=0;
-  _rc->log_drop_scale=OC_Q57(0);
+  _enc->rc.prev_drop_count=0;
+  _enc->rc.log_drop_scale=OC_Q57(0);
   /*Set up second order followers, initialized according to corresponding
      time constants.*/
-  oc_iir_filter_init(&_rc->scalefilter[0],2,oc_bexp_q24(_rc->log_scale[0]));
-  oc_iir_filter_init(&_rc->scalefilter[1],_rc->buf_delay>>1,
-   oc_bexp_q24(_rc->log_scale[1]));
-  oc_iir_filter_init(&_rc->vfrfilter,2,oc_bexp_q24(_rc->log_drop_scale));
+  oc_iir_filter_init(&_enc->rc.scalefilter[0],2,oc_bexp_q24(_enc->rc.log_scale[0]));
+  oc_iir_filter_init(&_enc->rc.scalefilter[1],_enc->rc.buf_delay>>1,
+   oc_bexp_q24(_enc->rc.log_scale[1]));
+  oc_iir_filter_init(&_enc->rc.vfrfilter,2,oc_bexp_q24(_enc->rc.log_drop_scale));
 }
 
+void oc_rc_state_init(oc_rc_state *_rc,oc_enc_ctx *_enc){
+  if(_enc->state.info.target_bitrate>0){
+    /*The buffer size is set equal to the keyframe interval, clamped to the
+       range [12,256] frames.
+      The 12 frame minimum gives us some chance to distribute bit estimation
+       errors.
+      The 256 frame maximum means we'll require 8-10 seconds of pre-buffering
+       at 24-30 fps, which is not unreasonable.*/
+    _rc->buf_delay=_enc->keyframe_frequency_force>256?
+     256:_enc->keyframe_frequency_force;
+    /*By default, enforce all buffer constraints.*/
+    _rc->drop_frames=1;
+    _rc->cap_overflow=1;
+    _rc->cap_underflow=0;
+    oc_enc_rc_reset(_enc);
+  }
+  _rc->twopass=0;
+  _rc->twopass_buffer_bytes=0;
+  _rc->frame_metrics=NULL;
+}
 
-void oc_rc_state_init(oc_rc_state *_rc,const oc_enc_ctx *_enc){
-  /*The buffer size is set equal to the keyframe interval, clamped to the range
-     [12,256] frames.
-    The 12 frame minimum gives us some chance to distribute bit estimation
-     errors.
-    The 256 frame maximum means we'll require 8-10 seconds of pre-buffering at
-     24-30 fps, which is not unreasonable.*/
-  _rc->buf_delay=_enc->keyframe_frequency_force>256?
-   256:_enc->keyframe_frequency_force;
-  /*By default, enforce all buffer constraints.*/
-  _rc->drop_frames=1;
-  _rc->cap_overflow=1;
-  _rc->cap_underflow=0;
-  oc_rc_state_reset(_rc,_enc);
+void oc_rc_state_clear(oc_rc_state *_rc){
+  _ogg_free(_rc->frame_metrics);
 }
 
-int oc_enc_update_rc_state(oc_enc_ctx *_enc,
- long _bits,int _qti,int _qi,int _trial,int _droppable){
-  ogg_int64_t buf_delta;
-  int         dropped;
-  dropped=0;
-  if(!_enc->rc.drop_frames)_droppable=0;
-  buf_delta=_enc->rc.bits_per_frame*(1+_enc->dup_count);
-  if(_bits<=0){
-    /*We didn't code any blocks in this frame.
-      Add it to the previous frame's dup count.*/
-    _enc->rc.prev_drop_count+=1+_enc->dup_count;
-    /*If this was the first frame of this type, lower the expected scale, but
-       don't set it to zero outright.*/
-    if(_trial)_enc->rc.log_scale[_qti]>>=1;
-    _bits=0;
-  }
+void oc_enc_rc_resize(oc_enc_ctx *_enc){
+  /*If encoding has not yet begun, reset the buffer state.*/
+  if(_enc->state.curframe_num<0)oc_enc_rc_reset(_enc);
   else{
-    ogg_int64_t log_scale;
-    ogg_int64_t log_bits;
-    ogg_int64_t log_qexp;
-    /*Compute the estimated scale factor for this frame type.*/
-    log_bits=oc_blog64(_bits);
-    log_qexp=_enc->log_qavg[_qti][_qi]-OC_Q57(2);
-    log_qexp=(log_qexp>>6)*(_enc->rc.exp[_qti]);
-    log_scale=OC_MINI(log_bits-_enc->rc.log_npixels+log_qexp,OC_Q57(16));
-    /*Use it to set that factor directly if this was a trial.*/
-    if(_trial)_enc->rc.log_scale[_qti]=log_scale;
-    else{
-      /*Otherwise update the low-pass scale filter for this frame type,
-         regardless of whether or not we dropped this frame.*/
-      _enc->rc.log_scale[_qti]=oc_blog64(oc_iir_filter_update(
-       _enc->rc.scalefilter+_qti,oc_bexp_q24(log_scale)))-OC_Q57(24);
-      /*If this frame busts our budget, it must be dropped.*/
-      if(_droppable&&_enc->rc.fullness+buf_delta<_bits){
-        _enc->rc.prev_drop_count+=1+_enc->dup_count;
-        _bits=0;
-        dropped=1;
-      }
-      else{
-        ogg_uint32_t drop_count;
-        /*Update a simple exponential moving average to estimate the "real"
-           frame rate taking drops and duplicates into account.
-          This is only done if the frame is coded, as it needs the final count
-           of dropped frames.*/
-        drop_count=_enc->rc.prev_drop_count+1;
-        if(drop_count>0x7F)drop_count=0x7FFFFFFF;
-        else drop_count<<=24;
-        _enc->rc.log_drop_scale=oc_blog64(oc_iir_filter_update(
-         &_enc->rc.vfrfilter,drop_count))-OC_Q57(24);
-        /*Initialize the drop count for this frame to the user-requested dup
-           count.
-          It will be increased if we drop more frames.*/
-        _enc->rc.prev_drop_count=_enc->dup_count;
-      }
+    /*Otherwise, update the bounds on the buffer, but not the current
+       fullness.*/
+    _enc->rc.bits_per_frame=(_enc->state.info.target_bitrate*
+     (ogg_int64_t)_enc->state.info.fps_denominator)/
+     _enc->state.info.fps_numerator;
+    /*Insane framerates or frame sizes mean insane bitrates.
+      Let's not get carried away.*/
+    if(_enc->rc.bits_per_frame>0x400000000000LL){
+      _enc->rc.bits_per_frame=(ogg_int64_t)0x400000000000LL;
     }
+    else if(_enc->rc.bits_per_frame<32)_enc->rc.bits_per_frame=32;
+    _enc->rc.buf_delay=OC_MAXI(_enc->rc.buf_delay,12);
+    _enc->rc.max=_enc->rc.bits_per_frame*_enc->rc.buf_delay;
+    _enc->rc.target=(_enc->rc.max+1>>1)+(_enc->rc.max+2>>2);
+    oc_iir_filter_init(&_enc->rc.scalefilter[1],_enc->rc.buf_delay>>1,
+     oc_bexp_q24(_enc->rc.log_scale[1]));
   }
-  if(!_trial){
-    /*And update the buffer fullness level.*/
-    _enc->rc.fullness+=buf_delta-_bits;
-    /*If we're too quick filling the buffer and overflow is capped,
-      that rate is lost forever.*/
-    if(_enc->rc.cap_overflow&&_enc->rc.fullness>_enc->rc.max){
-      _enc->rc.fullness=_enc->rc.max;
+  /*If we're in pass-2 mode, make sure the frame metrics array is big enough
+     to hold frame statistics for the full buffer.*/
+  if(_enc->rc.twopass==2){
+    int cfm;
+    int buf_delay;
+    int reset_window;
+    reset_window=_enc->rc.frame_metrics==NULL;
+    cfm=_enc->rc.cframe_metrics;
+    buf_delay=_enc->rc.buf_delay;
+    if(cfm<buf_delay){
+      oc_frame_metrics *fm;
+      int               nfm;
+      int               fmh;
+      fm=_enc->rc.frame_metrics=(oc_frame_metrics *)_ogg_realloc(
+       _enc->rc.frame_metrics,buf_delay*sizeof(*_enc->rc.frame_metrics));
+      _enc->rc.cframe_metrics=buf_delay;
+      /*Re-organize the circular buffer.*/
+      fmh=_enc->rc.frame_metrics_head;
+      nfm=_enc->rc.nframe_metrics;
+      if(fmh+nfm>cfm){
+        int shift;
+        shift=OC_MINI(fmh+nfm-cfm,buf_delay-cfm);
+        memcpy(fm+cfm,fm,OC_MINI(fmh+nfm-cfm,buf_delay-cfm)*sizeof(*fm));
+        if(fmh+nfm>buf_delay)memmove(fm,fm+shift,fmh+nfm-buf_delay);
+      }
     }
-    /*If we're too quick draining the buffer and underflow is capped,
-      don't try to make up that rate later.*/
-    if(_enc->rc.cap_underflow&&_enc->rc.fullness<0){
-      _enc->rc.fullness=0;
+    /*We were using whole-file buffering; now we're not.*/
+    if(reset_window){
+      _enc->rc.nframes[0]=_enc->rc.nframes[1]=_enc->rc.nframes[2]=0;
+      _enc->rc.scale_sum[0]=_enc->rc.scale_sum[1]=0;
+      _enc->rc.scale_window_end=_enc->rc.scale_window0=
+       _enc->state.curframe_num+_enc->prev_dup_count+1;
+      if(_enc->rc.twopass_buffer_bytes){
+        int qti;
+        /*We already read the metrics for the first frame in the window.*/
+        *(_enc->rc.frame_metrics)=*&_enc->rc.cur_metrics;
+        _enc->rc.nframe_metrics++;
+        qti=_enc->rc.cur_metrics.frame_type;
+        _enc->rc.nframes[qti]++;
+        _enc->rc.nframes[2]+=_enc->rc.cur_metrics.dup_count;
+        _enc->rc.scale_sum[qti]+=_enc->rc.cur_metrics.scale;
+        _enc->rc.scale_window_end+=_enc->rc.cur_metrics.dup_count+1;
+        if(_enc->rc.scale_window_end-_enc->rc.scale_window0<buf_delay){
+          /*We need more frame data.*/
+          _enc->rc.twopass_buffer_bytes=0;
+        }
+      }
     }
+    /*Otherwise, we could shrink the size of the current window, if necessary,
+       but leaving it like it is lets us adapt to the new buffer size more
+       gracefully.*/
   }
-  return dropped;
 }
 
 int oc_enc_select_qi(oc_enc_ctx *_enc,int _qti,int _clamp){
   ogg_int64_t  rate_total;
-  ogg_uint32_t next_key_frame;
   int          nframes[2];
   int          buf_delay;
   ogg_int64_t  log_qtarget;
@@ -363,37 +376,103 @@
   /*Figure out how to re-distribute bits so that we hit our fullness target
      before the last keyframe in our current buffer window (after the current
      frame), or the end of the buffer window, whichever comes first.*/
-  next_key_frame=_qti?_enc->keyframe_frequency_force
-   -(_enc->state.curframe_num-_enc->state.keyframe_num):0;
-  nframes[0]=(_enc->rc.buf_delay-OC_MINI(next_key_frame,_enc->rc.buf_delay)
-   +_enc->keyframe_frequency_force-1)/_enc->keyframe_frequency_force;
-  if(nframes[0]+_qti>1){
-    nframes[0]--;
-    buf_delay=next_key_frame+nframes[0]*_enc->keyframe_frequency_force;
+  switch(_enc->rc.twopass){
+    default:{
+      ogg_uint32_t next_key_frame;
+      /*Single pass mode: assume only forced keyframes and attempt to estimate
+         the drop count for VFR content.*/
+      next_key_frame=_qti?_enc->keyframe_frequency_force
+       -(_enc->state.curframe_num-_enc->state.keyframe_num):0;
+      nframes[0]=(_enc->rc.buf_delay-OC_MINI(next_key_frame,_enc->rc.buf_delay)
+       +_enc->keyframe_frequency_force-1)/_enc->keyframe_frequency_force;
+      if(nframes[0]+_qti>1){
+        nframes[0]--;
+        buf_delay=next_key_frame+nframes[0]*_enc->keyframe_frequency_force;
+      }
+      else buf_delay=_enc->rc.buf_delay;
+      nframes[1]=buf_delay-nframes[0];
+      /*Downgrade the delta frame rate to correspond to the recent drop count
+         history.*/
+      if(_enc->rc.prev_drop_count>0||_enc->rc.log_drop_scale>OC_Q57(0)){
+        ogg_int64_t dup_scale;
+        dup_scale=oc_bexp64((_enc->rc.log_drop_scale
+         +oc_blog64(_enc->rc.prev_drop_count+1)>>1)+OC_Q57(8));
+        if(dup_scale<nframes[1]<<8){
+          int dup_scalei;
+          dup_scalei=(int)dup_scale;
+          if(dup_scalei>0)nframes[1]=((nframes[1]<<8)+dup_scalei-1)/dup_scalei;
+        }
+        else nframes[1]=!!nframes[1];
+      }
+    }break;
+    case 1:{
+      /*Pass 1 mode: use a fixed qi value.*/
+      return _enc->state.qis[0];
+    }break;
+    case 2:{
+      int qti;
+      /*Pass 2 mode: we know exactly how much of each frame type there is in
+         the current buffer window, and have estimates for the scales.*/
+      for(qti=0;qti<2;qti++){
+        oc_log_linear_fit *fit;
+        ogg_int64_t        x;
+        /*TODO: If we're using a finite buffer, target the last keyframe in
+           the current window, as in one-pass mode.
+          This may not be strictly necessary in two-pass mode, as we have a
+           much better idea of the distribution of bits in the window.*/
+        nframes[qti]=_enc->rc.nframes[qti];
+        x=nframes[qti]>0?
+         oc_blog64(_enc->rc.scale_sum[qti])-oc_blog64(nframes[qti])-OC_Q57(24):
+         -_enc->rc.log_npixels;
+        fit=_enc->rc.corr+qti;
+        if(fit->n>0){
+          ogg_int64_t  var;
+          ogg_uint32_t n_2;
+          n_2=fit->n>>1;
+          var=fit->x2-(fit->x+2048>>12)*(((fit->x+2048>>12)+n_2)/fit->n);
+          if(var>fit->n){
+            ogg_int64_t cov;
+            ogg_int64_t beta;
+            ogg_int64_t alpha;
+            ogg_int64_t y;
+            cov=fit->xy-(fit->y+2048>>12)*(((fit->x+2048>>12)+n_2)/fit->n);
+            /*beta is Q33.*/
+            beta=((cov+n_2)/fit->n<<33)/((var+n_2)/fit->n);
+            /*alpha is Q57.*/
+            alpha=((fit->y+n_2)/fit->n<<33)-beta*((fit->x+n_2)/fit->n);
+            /*Predict the mean y from the mean x.
+              Mathematically, this is completely wrong (we want to take the
+               mean of the predictions), but in practice it works out well
+               enough.
+              The alternative would be to do thousands of 64-bit logs and
+               exponentials every frame.*/
+            y=(x+((ogg_int64_t)1<<32)>>33)*beta+alpha;
+            /*If we have enough points for a good estimation, use the corrected
+               predictor value directly.*/
+            if(fit->n>(128<<qti))x=y;
+            /*Otherwise interpolate between the two.*/
+            else x+=fit->n*(y-x>>7+qti);
+          }
+        }
+        _enc->rc.log_scale[qti]=x;
+      }
+      buf_delay=_enc->rc.scale_window_end-_enc->rc.scale_window0;
+      /*If we're approaching the end of the file, add some slack to keep us
+         from slamming into a rail.*/
+      if(buf_delay<_enc->keyframe_frequency_force){
+        nframes[1]+=_enc->keyframe_frequency_force-buf_delay;
+        buf_delay=_enc->keyframe_frequency_force;
+      }
+    }break;
   }
-  else buf_delay=_enc->rc.buf_delay;
-  nframes[1]=buf_delay-nframes[0];
   rate_total=_enc->rc.fullness-_enc->rc.target
    +buf_delay*_enc->rc.bits_per_frame;
-  /*Downgrade the delta frame rate to correspond to the recent drop count
-     history.*/
-  if(_enc->rc.prev_drop_count>0||_enc->rc.log_drop_scale>OC_Q57(0)){
-    ogg_int64_t dup_scale;
-    dup_scale=oc_bexp64((_enc->rc.log_drop_scale
-     +oc_blog64(_enc->rc.prev_drop_count+1)>>1)+OC_Q57(8));
-    if(dup_scale<nframes[1]<<8){
-      int dup_scalei;
-      dup_scalei=(int)dup_scale;
-      if(dup_scalei>0)nframes[1]=((nframes[1]<<8)+dup_scalei-1)/dup_scalei;
-    }
-    else nframes[1]=!!nframes[1];
-  }
   log_scale0=_enc->rc.log_scale[_qti]+_enc->rc.log_npixels;
   /*If there aren't enough bits to achieve our desired fullness level, use the
      minimum quality permitted.*/
   if(rate_total<=buf_delay)log_qtarget=OC_QUANT_MAX_LOG;
   else{
-    static const unsigned char KEY_RATIO[2]={32,17};
+    static const unsigned char KEY_RATIO[2]={32,21};
     ogg_int64_t   log_scale1;
     ogg_int64_t   prevr;
     ogg_int64_t   curr;
@@ -468,3 +547,412 @@
   _enc->rc.log_qtarget=log_qtarget;
   return qi;
 }
+
+int oc_enc_update_rc_state(oc_enc_ctx *_enc,
+ long _bits,int _qti,int _qi,int _trial,int _droppable){
+  ogg_int64_t buf_delta;
+  ogg_int64_t log_scale;
+  int         dropped;
+  dropped=0;
+  if(!_enc->rc.drop_frames)_droppable=0;
+  buf_delta=_enc->rc.bits_per_frame*(1+_enc->dup_count);
+  if(_bits<=0){
+    /*We didn't code any blocks in this frame.*/
+    _bits=0;
+    log_scale=OC_Q57(-64);
+  }
+  else{
+    ogg_int64_t log_bits;
+    ogg_int64_t log_qexp;
+    /*Compute the estimated scale factor for this frame type.*/
+    log_bits=oc_blog64(_bits);
+    log_qexp=_enc->log_qavg[_qti][_qi]-OC_Q57(2);
+    log_qexp=(log_qexp>>6)*(_enc->rc.exp[_qti]);
+    log_scale=OC_MINI(log_bits-_enc->rc.log_npixels+log_qexp,OC_Q57(16));
+  }
+  switch(_enc->rc.twopass){
+    default:{
+      /*Single pass mode:*/
+      if(_bits>0){
+        /*Use the estimated scale factor directly directly if this was a
+           trial.*/
+        if(_trial)_enc->rc.log_scale[_qti]=log_scale;
+        else{
+          /*Otherwise update the low-pass scale filter for this frame type,
+             regardless of whether or not we dropped this frame.*/
+          _enc->rc.log_scale[_qti]=oc_blog64(oc_iir_filter_update(
+           _enc->rc.scalefilter+_qti,oc_bexp_q24(log_scale)))-OC_Q57(24);
+          /*If this frame busts our budget, it must be dropped.*/
+          if(_droppable&&_enc->rc.fullness+buf_delta<_bits){
+            _enc->rc.prev_drop_count+=1+_enc->dup_count;
+            _bits=0;
+            dropped=1;
+          }
+          else{
+            ogg_uint32_t drop_count;
+            /*Update a low-pass filter to estimate the "real" frame rate taking
+               drops and duplicates into account.
+              This is only done if the frame is coded, as it needs the final
+               count of dropped frames.*/
+            drop_count=_enc->rc.prev_drop_count+1;
+            if(drop_count>0x7F)drop_count=0x7FFFFFFF;
+            else drop_count<<=24;
+            _enc->rc.log_drop_scale=oc_blog64(oc_iir_filter_update(
+             &_enc->rc.vfrfilter,drop_count))-OC_Q57(24);
+            /*Initialize the drop count for this frame to the user-requested dup
+               count.
+              It will be increased if we drop more frames.*/
+            _enc->rc.prev_drop_count=_enc->dup_count;
+          }
+        }
+      }
+      /*Increase the drop count.*/
+      else _enc->rc.prev_drop_count+=1+_enc->dup_count;
+    }break;
+    case 1:{
+      /*Pass-1 mode: save the metrics for this frame.*/
+      _enc->rc.cur_metrics.scale=oc_bexp_q24(log_scale);
+      _enc->rc.cur_metrics.dup_count=_enc->dup_count;
+      _enc->rc.cur_metrics.frame_type=_enc->state.frame_type;
+    }break;
+    case 2:{
+      ogg_int64_t        x;
+      ogg_int64_t        y;
+      oc_log_linear_fit *fit;
+      /*Pass 2 mode:*/
+      /*Accumulate statistics for estimation bias correction.
+        Everything is done in Q24 format.*/
+      x=oc_blog64(_enc->rc.cur_metrics.scale)-OC_Q57(24)>>33;
+      y=log_scale>>33;
+      fit=_enc->rc.corr+_qti;
+      fit->n++;
+      fit->x+=x;
+      fit->y+=y;
+      fit->x2+=(x+2048>>12)*(x+2048>>12);
+      fit->xy+=(x+2048>>12)*(y+2048>>12);
+      /*Move the current metrics back one frame.*/
+      *&_enc->rc.prev_metrics=*&_enc->rc.cur_metrics;
+      /*If this frame busts our budget, it must be dropped.*/
+      if(_droppable&&_enc->rc.fullness+buf_delta<_bits){
+        _bits=0;
+        dropped=1;
+      }
+    }break;
+  }
+  if(!_trial){
+    /*And update the buffer fullness level.*/
+    _enc->rc.fullness+=buf_delta-_bits;
+    /*If we're too quick filling the buffer and overflow is capped,
+      that rate is lost forever.*/
+    if(_enc->rc.cap_overflow&&_enc->rc.fullness>_enc->rc.max){
+      _enc->rc.fullness=_enc->rc.max;
+    }
+    /*If we're too quick draining the buffer and underflow is capped,
+      don't try to make up that rate later.*/
+    if(_enc->rc.cap_underflow&&_enc->rc.fullness<0){
+      _enc->rc.fullness=0;
+    }
+  }
+  return dropped;
+}
+
+#define OC_RC_2PASS_HDR_SZ    (38)
+#define OC_RC_2PASS_PACKET_SZ (8)
+
+static void oc_rc_buffer_val(oc_rc_state *_rc,ogg_int64_t _val,int _bytes){
+  while(_bytes-->0){
+    _rc->twopass_buffer[_rc->twopass_buffer_bytes++]=(unsigned char)(_val&0xFF);
+    _val>>=8;
+  }
+}
+
+int oc_enc_rc_2pass_out(oc_enc_ctx *_enc,unsigned char **_buf){
+  if(_enc->rc.twopass_buffer_bytes==0){
+    if(_enc->rc.twopass==0){
+      int qi;
+      /*Pick first-pass qi for scale calculations.*/
+      qi=oc_enc_select_qi(_enc,0,0);
+      _enc->state.nqis=1;
+      _enc->state.qis[0]=qi;
+      _enc->rc.twopass=1;
+      _enc->rc.frames_total[0]=_enc->rc.frames_total[1]=
+       _enc->rc.frames_total[2]=0;
+      _enc->rc.scale_sum[0]=_enc->rc.scale_sum[1]=0;
+      /*Fill in dummy summary values.*/
+      oc_rc_buffer_val(&_enc->rc,0x5032544F,4);
+      oc_rc_buffer_val(&_enc->rc,0,4);
+      oc_rc_buffer_val(&_enc->rc,0,OC_RC_2PASS_HDR_SZ-8);
+    }
+    else{
+      int qti;
+      qti=_enc->rc.cur_metrics.frame_type;
+      _enc->rc.scale_sum[qti]+=_enc->rc.cur_metrics.scale;
+      _enc->rc.frames_total[qti]++;
+      _enc->rc.frames_total[2]+=_enc->rc.cur_metrics.dup_count;
+      oc_rc_buffer_val(&_enc->rc,
+       _enc->rc.cur_metrics.dup_count|_enc->rc.cur_metrics.frame_type<<31,4);
+      oc_rc_buffer_val(&_enc->rc,_enc->rc.cur_metrics.scale,4);
+    }
+  }
+  else if(_enc->packet_state==OC_PACKET_DONE&&
+   _enc->rc.twopass_buffer_bytes!=OC_RC_2PASS_HDR_SZ){
+    _enc->rc.twopass_buffer_bytes=0;
+    oc_rc_buffer_val(&_enc->rc,0x5032544F,4);
+    oc_rc_buffer_val(&_enc->rc,0,4);
+    oc_rc_buffer_val(&_enc->rc,_enc->rc.frames_total[0],4);
+    oc_rc_buffer_val(&_enc->rc,_enc->rc.frames_total[1],4);
+    oc_rc_buffer_val(&_enc->rc,_enc->rc.frames_total[2],4);
+    oc_rc_buffer_val(&_enc->rc,_enc->rc.exp[0],1);
+    oc_rc_buffer_val(&_enc->rc,_enc->rc.exp[1],1);
+    oc_rc_buffer_val(&_enc->rc,_enc->rc.scale_sum[0],8);
+    oc_rc_buffer_val(&_enc->rc,_enc->rc.scale_sum[1],8);
+  }
+  else{
+    /*The data for this frame has already been retrieved.*/
+    *_buf=NULL;
+    return 0;
+  }
+  *_buf=_enc->rc.twopass_buffer;
+  return _enc->rc.twopass_buffer_bytes;
+}
+
+static size_t oc_rc_buffer_fill(oc_rc_state *_rc,
+ unsigned char *_buf,size_t _bytes,size_t _consumed,size_t _goal){
+  while(_rc->twopass_buffer_fill<_goal&&_consumed<_bytes){
+    _rc->twopass_buffer[_rc->twopass_buffer_fill++]=_buf[_consumed++];
+  }
+  return _consumed;
+}
+
+static ogg_int64_t oc_rc_unbuffer_val(oc_rc_state *_rc,int _bytes){
+  ogg_int64_t ret;
+  int         shift;
+  ret=0;
+  shift=0;
+  while(_bytes-->0){
+    ret|=((ogg_int64_t)_rc->twopass_buffer[_rc->twopass_buffer_bytes++])<<shift;
+    shift+=8;
+  }
+  return ret;
+}
+
+int oc_enc_rc_2pass_in(oc_enc_ctx *_enc,unsigned char *_buf,size_t _bytes){
+  size_t consumed;
+  consumed=0;
+  /*Enable pass 2 mode if this is the first call.*/
+  if(_enc->rc.twopass==0){
+    _enc->rc.twopass=2;
+    _enc->rc.twopass_buffer_fill=0;
+    _enc->rc.frames_total[0]=0;
+    _enc->rc.nframe_metrics=0;
+    _enc->rc.cframe_metrics=0;
+    _enc->rc.frame_metrics_head=0;
+    _enc->rc.scale_window0=0;
+    _enc->rc.scale_window_end=0;
+  }
+  /*If we haven't got a valid summary header yet, try to parse one.*/
+  if(_enc->rc.frames_total[0]==0){
+    if(!_buf){
+      int req_sz;
+      /*If we're using a whole-file buffer, we just need the first frame.*/
+      if(_enc->rc.frame_metrics==NULL)req_sz=OC_RC_2PASS_PACKET_SZ;
+      /*Otherwise, we may need many frames.*/
+      else req_sz=OC_RC_2PASS_PACKET_SZ*_enc->rc.buf_delay;
+      return OC_RC_2PASS_HDR_SZ+req_sz-_enc->rc.twopass_buffer_fill;
+    }
+    consumed=oc_rc_buffer_fill(&_enc->rc,
+     _buf,_bytes,consumed,OC_RC_2PASS_HDR_SZ);
+    if(_enc->rc.twopass_buffer_fill>=OC_RC_2PASS_HDR_SZ){
+      ogg_int64_t scale_sum[2];
+      int         exp[2];
+      int         buf_delay;
+      /*Read the summary header data.*/
+      /*Check the magic value and version number.*/
+      if(oc_rc_unbuffer_val(&_enc->rc,4)!=0x5032544F||
+       oc_rc_unbuffer_val(&_enc->rc,4)!=0){
+        _enc->rc.twopass_buffer_bytes=0;
+        return TH_ENOTFORMAT;
+      }
+      _enc->rc.frames_total[0]=(ogg_uint32_t)oc_rc_unbuffer_val(&_enc->rc,4);
+      _enc->rc.frames_total[1]=(ogg_uint32_t)oc_rc_unbuffer_val(&_enc->rc,4);
+      _enc->rc.frames_total[2]=(ogg_uint32_t)oc_rc_unbuffer_val(&_enc->rc,4);
+      exp[0]=(int)oc_rc_unbuffer_val(&_enc->rc,1);
+      exp[1]=(int)oc_rc_unbuffer_val(&_enc->rc,1);
+      scale_sum[0]=oc_rc_unbuffer_val(&_enc->rc,8);
+      scale_sum[1]=oc_rc_unbuffer_val(&_enc->rc,8);
+      /*Make sure the file claims to have at least one frame.
+        Otherwise we probably got the placeholder data from an aborted pass 1.
+        Also make sure the total frame count doesn't overflow an integer.*/
+      buf_delay=_enc->rc.frames_total[0]+_enc->rc.frames_total[1]
+       +_enc->rc.frames_total[2];
+      if(_enc->rc.frames_total[0]==0||buf_delay<0||
+       (ogg_uint32_t)buf_delay<_enc->rc.frames_total[0]||
+       (ogg_uint32_t)buf_delay<_enc->rc.frames_total[1]){
+        _enc->rc.frames_total[0]=0;
+        _enc->rc.twopass_buffer_bytes=0;
+        return TH_EBADHEADER;
+      }
+      /*Got a valid header; set up pass 2.*/
+      _enc->rc.frames_left[0]=_enc->rc.frames_total[0];
+      _enc->rc.frames_left[1]=_enc->rc.frames_total[1];
+      _enc->rc.frames_left[2]=_enc->rc.frames_total[2];
+      /*If the user hasn't specified a buffer size, use the whole file.*/
+      if(_enc->rc.frame_metrics==NULL){
+        _enc->rc.buf_delay=buf_delay;
+        _enc->rc.nframes[0]=_enc->rc.frames_total[0];
+        _enc->rc.nframes[1]=_enc->rc.frames_total[1];
+        _enc->rc.nframes[2]=_enc->rc.frames_total[2];
+        _enc->rc.scale_sum[0]=scale_sum[0];
+        _enc->rc.scale_sum[1]=scale_sum[1];
+        _enc->rc.scale_window_end=buf_delay;
+        oc_enc_rc_reset(_enc);
+      }
+      /*TODO: If exp[] does not match the current values, we should adjust the
+         initial correction fit to compensate.*/
+      memset(_enc->rc.corr,0,sizeof(_enc->rc.corr));
+      /*Clear the header data from the buffer to make room for packet data.*/
+      _enc->rc.twopass_buffer_fill=0;
+      _enc->rc.twopass_buffer_bytes=0;
+    }
+  }
+  if(_enc->rc.frames_total[0]!=0){
+    ogg_int64_t curframe_num;
+    int         nframes_total;
+    curframe_num=_enc->state.curframe_num;
+    if(curframe_num>=0){
+      /*We just encoded a frame; make sure things matched.*/
+      if(_enc->rc.prev_metrics.dup_count!=_enc->prev_dup_count){
+        _enc->rc.twopass_buffer_bytes=0;
+        return TH_EINVAL;
+      }
+    }
+    curframe_num+=_enc->prev_dup_count+1;
+    nframes_total=_enc->rc.frames_total[0]+_enc->rc.frames_total[1]
+     +_enc->rc.frames_total[2];
+    if(curframe_num>=nframes_total){
+      /*We don't want any more data after the last frame.*/
+      _enc->rc.twopass_buffer_bytes=0;
+      return 0;
+    }
+    if(_enc->rc.frame_metrics==NULL){
+      /*We're using a whole-file buffer:*/
+      if(_enc->rc.twopass_buffer_bytes==0){
+        if(!_buf)return OC_RC_2PASS_PACKET_SZ-_enc->rc.twopass_buffer_fill;
+        consumed=oc_rc_buffer_fill(&_enc->rc,
+         _buf,_bytes,consumed,OC_RC_2PASS_PACKET_SZ);
+        if(_enc->rc.twopass_buffer_fill>=OC_RC_2PASS_PACKET_SZ){
+          ogg_uint32_t dup_count;
+          ogg_int32_t  scale;
+          int          qti;
+          int          arg;
+          if(curframe_num>0){
+            /*Back out the last frame's statistics from the sliding window.*/
+            qti=_enc->rc.prev_metrics.frame_type;
+            _enc->rc.frames_left[qti]--;
+            _enc->rc.frames_left[2]-=_enc->rc.prev_metrics.dup_count;
+            _enc->rc.nframes[qti]--;
+            _enc->rc.nframes[2]-=_enc->rc.prev_metrics.dup_count;
+            _enc->rc.scale_sum[qti]-=_enc->rc.prev_metrics.scale;
+            _enc->rc.scale_window0=(int)curframe_num;
+          }
+          /*Read the metrics for the next frame.*/
+          dup_count=oc_rc_unbuffer_val(&_enc->rc,4);
+          scale=oc_rc_unbuffer_val(&_enc->rc,4);
+          _enc->rc.cur_metrics.scale=scale;
+          qti=(dup_count&0x80000000)>>31;
+          _enc->rc.cur_metrics.dup_count=dup_count&0x7FFFFFFF;
+          _enc->rc.cur_metrics.frame_type=qti;
+          _enc->rc.twopass_force_kf=qti==OC_INTRA_FRAME;
+          /*"Helpfully" set the dup count back to what it was in pass 1.*/
+          arg=_enc->rc.cur_metrics.dup_count;
+          th_encode_ctl(_enc,TH_ENCCTL_SET_DUP_COUNT,&arg,sizeof(arg));
+          /*Clear the buffer for the next frame.*/
+          _enc->rc.twopass_buffer_fill=0;
+        }
+      }
+      /*We don't want any more data for this frame.*/
+      else return 0;
+    }
+    else{
+      int qti;
+      /*We're using a finite buffer:*/
+      if(curframe_num>_enc->rc.scale_window0){
+        /*Back out the last frame's statistics from the sliding window.*/
+        qti=_enc->rc.prev_metrics.frame_type;
+        _enc->rc.frames_left[qti]--;
+        _enc->rc.frames_left[2]-=_enc->rc.prev_metrics.dup_count;
+        _enc->rc.nframes[qti]--;
+        _enc->rc.nframes[2]-=_enc->rc.prev_metrics.dup_count;
+        _enc->rc.scale_sum[qti]-=_enc->rc.prev_metrics.scale;
+        _enc->rc.scale_window0=(int)curframe_num;
+        /*Free the corresponding entry in the circular buffer.*/
+        _enc->rc.nframe_metrics--;
+        _enc->rc.frame_metrics_head++;
+        if(_enc->rc.frame_metrics_head>=_enc->rc.cframe_metrics){
+          _enc->rc.frame_metrics_head=0;
+        }
+      }
+      if(_enc->rc.twopass_buffer_bytes==0){
+        int frames_needed;
+        frames_needed=OC_CLAMPI(0,_enc->rc.buf_delay
+         -(_enc->rc.scale_window_end-_enc->rc.scale_window0),
+         _enc->rc.frames_left[0]+_enc->rc.frames_left[1]
+         -_enc->rc.nframes[0]-_enc->rc.nframes[1]);
+        while(frames_needed>0){
+          if(!_buf){
+            return OC_RC_2PASS_PACKET_SZ*frames_needed
+           -_enc->rc.twopass_buffer_fill;
+          }
+          consumed=oc_rc_buffer_fill(&_enc->rc,
+           _buf,_bytes,consumed,OC_RC_2PASS_PACKET_SZ);
+          if(_enc->rc.twopass_buffer_fill>=OC_RC_2PASS_PACKET_SZ){
+            oc_frame_metrics *m;
+            int               fmi;
+            ogg_uint32_t      dup_count;
+            ogg_int32_t       scale;
+            /*Read the metrics for the next frame.*/
+            dup_count=oc_rc_unbuffer_val(&_enc->rc,4);
+            scale=oc_rc_unbuffer_val(&_enc->rc,4);
+            fmi=_enc->rc.frame_metrics_head+_enc->rc.nframe_metrics++;
+            if(fmi>=_enc->rc.cframe_metrics)fmi-=_enc->rc.cframe_metrics;
+            m=_enc->rc.frame_metrics+fmi;
+            m->scale=scale;
+            qti=(dup_count&0x80000000)>>31;
+            m->dup_count=dup_count&0x7FFFFFFF;
+            m->frame_type=qti;
+            _enc->rc.nframes[qti]++;
+            _enc->rc.nframes[2]+=m->dup_count;
+            _enc->rc.scale_sum[qti]+=m->scale;
+            _enc->rc.scale_window_end+=m->dup_count+1;
+            frames_needed=OC_CLAMPI(0,_enc->rc.buf_delay
+             -(_enc->rc.scale_window_end-_enc->rc.scale_window0),
+             _enc->rc.frames_left[0]+_enc->rc.frames_left[1]
+             -_enc->rc.nframes[0]-_enc->rc.nframes[1]);
+            /*Clear the buffer for the next frame.*/
+            _enc->rc.twopass_buffer_fill=0;
+            _enc->rc.twopass_buffer_bytes=0;
+          }
+          /*Go back for more data.*/
+          else break;
+        }
+        /*If we've got all the frames we need, fill in the current metrics.
+          We're ready to go.*/
+        if(frames_needed<=0){
+          int arg;
+          *&_enc->rc.cur_metrics=
+           *(_enc->rc.frame_metrics+_enc->rc.frame_metrics_head);
+          _enc->rc.twopass_force_kf=
+           _enc->rc.cur_metrics.frame_type==OC_INTRA_FRAME;
+          /*"Helpfully" set the dup count back to what it was in pass 1.*/
+          arg=_enc->rc.cur_metrics.dup_count;
+          th_encode_ctl(_enc,TH_ENCCTL_SET_DUP_COUNT,&arg,sizeof(arg));
+          /*Mark us ready for the next frame.*/
+          _enc->rc.twopass_buffer_bytes=1;
+        }
+      }
+      /*We don't want any more data for this frame.*/
+      else return 0;
+    }
+  }
+  return (int)consumed;
+}



More information about the commits mailing list