[xiph-commits] r16420 - trunk/theora/examples

gmaxwell at svn.xiph.org gmaxwell at svn.xiph.org
Wed Aug 5 10:22:47 PDT 2009


Author: gmaxwell
Date: 2009-08-05 10:22:46 -0700 (Wed, 05 Aug 2009)
New Revision: 16420

Modified:
   trunk/theora/examples/Makefile.am
   trunk/theora/examples/encoder_example.c
   trunk/theora/examples/png2theora.c
Log:
png2theora: New rate control options (two-pass)

Modified: trunk/theora/examples/Makefile.am
===================================================================
--- trunk/theora/examples/Makefile.am	2009-08-05 14:03:13 UTC (rev 16419)
+++ trunk/theora/examples/Makefile.am	2009-08-05 17:22:46 UTC (rev 16420)
@@ -31,7 +31,7 @@
 
 png2theora_SOURCES = png2theora.c
 png2theora_CFLAGS = $(OGG_CFLAGS) $(PNG_CFLAGS)
-png2theora_LDADD = $(GETOPT_OBJS) $(LDADD) $(PNG_LIBS)
+png2theora_LDADD = $(GETOPT_OBJS) $(LDADDENC) $(PNG_LIBS)
 
 debug:
 	$(MAKE) all CFLAGS="@DEBUG@"

Modified: trunk/theora/examples/encoder_example.c
===================================================================
--- trunk/theora/examples/encoder_example.c	2009-08-05 14:03:13 UTC (rev 16419)
+++ trunk/theora/examples/encoder_example.c	2009-08-05 17:22:46 UTC (rev 16420)
@@ -1117,8 +1117,8 @@
      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.*/
+    This is only currently true of the 1.1 encoder; do NOT do this
+     with the 1.0 encoder.*/
   ycbcr[0].width=frame_w;
   ycbcr[0].height=frame_h;
   ycbcr[0].stride=pic_w;

Modified: trunk/theora/examples/png2theora.c
===================================================================
--- trunk/theora/examples/png2theora.c	2009-08-05 14:03:13 UTC (rev 16419)
+++ trunk/theora/examples/png2theora.c	2009-08-05 17:22:46 UTC (rev 16420)
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <time.h>
 #include <math.h>
 #include <libgen.h>
 #include <sys/types.h>
@@ -36,9 +37,8 @@
 
 #include <png.h>
 #include <ogg/ogg.h>
-#include "theora/theora.h"
+#include "theora/theoraenc.h"
 
-
 #define PROGRAM_NAME  "png2theora"
 #define PROGRAM_VERSION  "1.1"
 
@@ -47,32 +47,46 @@
 static int video_fps_denominator = 1;
 static int video_aspect_numerator = 0;
 static int video_aspect_denominator = 0;
-static int video_rate = 0;
-static int video_quality = 63;
-static int chroma_format = OC_PF_420;
+static int video_rate = -1;
+static int video_quality = -1;
+ogg_uint32_t keyframe_frequency=0;
+int buf_delay=-1;
+int vp3_compatible=0;
+static int chroma_format = TH_PF_420;
 
-static int theora_initialized = 0;
+static FILE *twopass_file = NULL;
+static  int twopass=0;
+static  int passno;
 
 static FILE *ogg_fp = NULL;
 static ogg_stream_state ogg_os;
+static ogg_packet op;   
+static ogg_page og;
+    
+static th_enc_ctx      *td;
+static th_info          ti;
 
-static theora_state theora_td;
-static theora_info theora_ti;
-
 static char *input_filter;
 
-const char *optstring = "o:hv:\4:\2:V:s:S:f:F:";
+const char *optstring = "o:hv:\4:\2:V:s:S:f:F:ck:d:\1\2\3\4\5\6";
 struct option options [] = {
  {"output",required_argument,NULL,'o'},
  {"help",no_argument,NULL,'h'},
- {"chroma-444",no_argument,NULL,'\4'},
- {"chroma-422",no_argument,NULL,'\2'},
+ {"chroma-444",no_argument,NULL,'\5'},
+ {"chroma-422",no_argument,NULL,'\6'},
  {"video-rate-target",required_argument,NULL,'V'},
  {"video-quality",required_argument,NULL,'v'},
  {"aspect-numerator",required_argument,NULL,'s'},
  {"aspect-denominator",required_argument,NULL,'S'},
  {"framerate-numerator",required_argument,NULL,'f'},
  {"framerate-denominator",required_argument,NULL,'F'},
+ {"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}
 };
 
@@ -84,18 +98,44 @@
           "  i.e. file-%%06d.png to look for files file000001.png to file9999999.png \n\n"
           "Options: \n\n"
           "  -o --output <filename.ogv>      file name for encoded output (required);\n"
-          "  -V --video-rate-target <n>      bitrate target for Theora video\n\n"
           "  -v --video-quality <n>          Theora quality selector fro 0 to 10\n"
           "                                  (0 yields smallest files but lowest\n"
           "                                  video quality. 10 yields highest\n"
           "                                  fidelity but large files)\n\n"
-          "      --chroma-444                Use 4:4:4 chroma subsampling\n"
-          "      --chroma-422                Use 4:2:2 chroma subsampling\n"
+          "  -V --video-rate-target <n>      bitrate target for Theora video\n\n"
+          "     --soft-target                Use a large reservoir and treat the rate\n"
+          "                                  as a soft target; rate control is less\n"
+          "                                  strict but resulting quality is usually\n"
+          "                                  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 performs 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 come from a first encoding pass\n"
+          "                                  using identical input video to work\n"
+          "                                  properly.\n\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"
+          "  --chroma-444                    Use 4:4:4 chroma subsampling\n"
+          "  --chroma-422                    Use 4:2:2 chroma subsampling\n"
           "                                  (4:2:0 is default)\n\n" 
-          "   -s --aspect-numerator <n>      Aspect ratio numerator, default is 0\n"
-          "   -S --aspect-denominator <n>    Aspect ratio denominator, default is 0\n"
-          "   -f --framerate-numerator <n>   Frame rate numerator\n"
-          "   -F --framerate-denominator <n> Frame rate denominator\n"
+          "  -s --aspect-numerator <n>       Aspect ratio numerator, default is 0\n"
+          "  -S --aspect-denominator <n>     Aspect ratio denominator, default is 0\n"
+          "  -f --framerate-numerator <n>    Frame rate numerator\n"
+          "  -F --framerate-denominator <n>  Frame rate denominator\n"
           "                                  The frame rate nominator divided by this\n"
           "                                  determines the frame rate in units per tick\n"
           ,PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_NAME
@@ -103,69 +143,10 @@
   exit(0);
 }
 
-
 static int
-theora_open(const char *pathname)
-{
-  ogg_packet op;
-  ogg_page og;
-  theora_comment tc;
-
-  ogg_fp = fopen(pathname, "wb");
-  if(!ogg_fp) {
-    fprintf(stderr, "%s: error: %s\n",
-      pathname, "couldn't open output file");
-    return 1;
-  }
-
-  if(ogg_stream_init(&ogg_os, rand())) {
-    fprintf(stderr, "%s: error: %s\n",
-      pathname, "couldn't create ogg stream state");
-    return 1;
-  }
-
-  if(theora_encode_init(&theora_td, &theora_ti)) {
-    fprintf(stderr, "%s: error: %s\n",
-      pathname, "couldn't initialize theora encoding");
-    return 1;
-  }
-
-  theora_encode_header(&theora_td, &op);
-  ogg_stream_packetin(&ogg_os, &op);
-  if(ogg_stream_pageout(&ogg_os, &og)) {
-    fwrite(og.header, og.header_len, 1, ogg_fp);
-    fwrite(og.body, og.body_len, 1, ogg_fp);
-  }
-  /* libogg flushes automatically after the first header */
-
-  theora_comment_init(&tc);
-  theora_encode_comment(&tc, &op);
-  ogg_stream_packetin(&ogg_os, &op);
-  while(ogg_stream_pageout(&ogg_os, &og)) {
-    fwrite(og.header, og.header_len, 1, ogg_fp);
-    fwrite(og.body, og.body_len, 1, ogg_fp);
-  }
-
-  theora_encode_tables(&theora_td, &op);
-  ogg_stream_packetin(&ogg_os, &op);
-  while(ogg_stream_pageout(&ogg_os, &og)) {
-    fwrite(og.header, og.header_len, 1, ogg_fp);
-    fwrite(og.body, og.body_len, 1, ogg_fp);
-  }
-
-  /* flush at the end of the headers */
-  if(ogg_stream_flush(&ogg_os, &og)) {
-    fwrite(og.header, og.header_len, 1, ogg_fp);
-    fwrite(og.body, og.body_len, 1, ogg_fp);
-  }
-
-  return 0;
-}
-
-static int
 theora_write_frame(unsigned long w, unsigned long h, unsigned char *yuv, int last)
 {
-  yuv_buffer yuv_buf;
+  th_ycbcr_buffer ycbcr;
   ogg_packet op;
   ogg_page og;
 
@@ -185,28 +166,27 @@
   /* Must hold: yuv_h >= h */
   yuv_h = (h + 15) & ~15;
 
-  yuv_buf.uv_width = (chroma_format == OC_PF_444) ? yuv_w : (yuv_w >> 1);
-  yuv_buf.uv_stride = yuv_buf.uv_width;
-  yuv_buf.uv_height = (chroma_format == OC_PF_420) ? (yuv_h >> 1) : yuv_h;
+  ycbcr[0].width = yuv_w;
+  ycbcr[0].height = yuv_h;
+  ycbcr[0].stride = yuv_w;
+  ycbcr[1].width = (chroma_format == TH_PF_444) ? yuv_w : (yuv_w >> 1);
+  ycbcr[1].stride = ycbcr[1].width;
+  ycbcr[1].height = (chroma_format == TH_PF_420) ? (yuv_h >> 1) : yuv_h;
+  ycbcr[2].width = ycbcr[1].width;
+  ycbcr[2].stride = ycbcr[1].stride;
+  ycbcr[2].height = ycbcr[1].height;
 
-  yuv_y = malloc(yuv_w * yuv_h);
-  yuv_u = malloc(yuv_buf.uv_width * yuv_buf.uv_height);
-  yuv_v = malloc(yuv_buf.uv_width * yuv_buf.uv_height);
+  ycbcr[0].data = yuv_y = malloc(ycbcr[0].stride * ycbcr[0].height);
+  ycbcr[1].data = yuv_u = malloc(ycbcr[1].stride * ycbcr[1].height);
+  ycbcr[2].data = yuv_v = malloc(ycbcr[2].stride * ycbcr[2].height);
 
-  yuv_buf.y_width = yuv_w;
-  yuv_buf.y_height = yuv_h;
-  yuv_buf.y_stride = yuv_w;
-  yuv_buf.y = yuv_y;
-  yuv_buf.u = yuv_u;
-  yuv_buf.v = yuv_v;
-
   for(y = 0; y < h; y++) {
     for(x = 0; x < w; x++) {
       yuv_y[x + y * yuv_w] = yuv[3 * (x + y * w) + 0];
     }
   }
 
-  if (chroma_format == OC_PF_420) {
+  if (chroma_format == TH_PF_420) {
     for(y = 0; y < h; y += 2) {
       for(x = 0; x < w; x += 2) {
         yuv_u[(x >> 1) + (y >> 1) * (yuv_w >> 1)] =
@@ -215,41 +195,94 @@
           yuv[3 * (x + y * w) + 2];
       }
     }
-  } else if (chroma_format == OC_PF_444) {
+  } else if (chroma_format == TH_PF_444) {
     for(y = 0; y < h; y++) {
       for(x = 0; x < w; x++) {
-        yuv_u[x + y * yuv_buf.uv_stride] = yuv[3 * (x + y * w) + 1];
-        yuv_v[x + y * yuv_buf.uv_stride] = yuv[3 * (x + y * w) + 2];
+        yuv_u[x + y * ycbcr[1].stride] = yuv[3 * (x + y * w) + 1];
+        yuv_v[x + y * ycbcr[2].stride] = yuv[3 * (x + y * w) + 2];
       }
     }
-  } else {  /* OC_PF_422 */
+  } else {  /* TH_PF_422 */
     for(y = 0; y < h; y += 1) {
       for(x = 0; x < w; x += 2) {
-        yuv_u[(x >> 1) + y * yuv_buf.uv_stride] =
+        yuv_u[(x >> 1) + y * ycbcr[1].stride] =
           yuv[3 * (x + y * w) + 1];
-        yuv_v[(x >> 1) + y * yuv_buf.uv_stride] =
+        yuv_v[(x >> 1) + y * ycbcr[2].stride] =
           yuv[3 * (x + y * w) + 2];
       }
     }    
   }
 
-  if(theora_encode_YUVin(&theora_td, &yuv_buf)) {
+  /* 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){
+    int ret;
+    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 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);
+      }
+      /*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;
+    }
+  }
+
+  if(th_encode_ycbcr_in(td, ycbcr)) {
     fprintf(stderr, "%s: error: could not encode frame\n",
       option_output);
     return 1;
   }
 
-  if(!theora_encode_packetout(&theora_td, last, &op)) {
+  /* 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(!th_encode_packetout(td, last, &op)) {
     fprintf(stderr, "%s: error: could not read packets\n",
       option_output);
     return 1;
   }
 
-  ogg_stream_packetin(&ogg_os, &op);
-  while(ogg_stream_pageout(&ogg_os, &og)) {
-    fwrite(og.header, og.header_len, 1, ogg_fp);
-    fwrite(og.body, og.body_len, 1, ogg_fp);
-  }
+  if (passno!=1) { 
+    ogg_stream_packetin(&ogg_os, &op);
+    while(ogg_stream_pageout(&ogg_os, &og)) {
+      fwrite(og.header, og.header_len, 1, ogg_fp);
+      fwrite(og.body, og.body_len, 1, ogg_fp);
+    }
+  }  
 
   free(yuv_y);
   free(yuv_u);
@@ -258,33 +291,6 @@
   return 0;
 }
 
-static void
-theora_close(void)
-{
-  ogg_packet op;
-  ogg_page og;
-
-  if (theora_initialized) {
-    theora_encode_packetout(&theora_td, 1, &op);
-    while(ogg_stream_pageout(&ogg_os, &og)) {
-      fwrite(og.header, og.header_len, 1, ogg_fp);
-      fwrite(og.body, og.body_len, 1, ogg_fp);
-    }
-    if(ogg_stream_flush(&ogg_os, &og)) {
-      fwrite(og.header, og.header_len, 1, ogg_fp);
-      fwrite(og.body, og.body_len, 1, ogg_fp);
-    }
-
-    theora_info_clear(&theora_ti);
-    theora_clear(&theora_td);
-
-    fflush(ogg_fp);
-    fclose(ogg_fp);
-  }
-
-  ogg_stream_clear(&ogg_os);
-}
-
 static unsigned char
 clamp(double d)
 {
@@ -359,8 +365,6 @@
     return 1;
   }
 
-  fprintf(stderr, "%s\n", pathname);
-
   fread(header, 1, 8, fp);
   if(png_sig_cmp(header, 0, 8)) {
     fprintf(stderr, "%s: error: %s\n",
@@ -445,6 +449,12 @@
   return !strcmp(name, de->d_name);
 }
 
+static int ilog(unsigned _v){
+  int ret;
+  for(ret=0;_v;ret++)_v>>=1;
+  return ret;
+}
+      
 int
 main(int argc, char *argv[])
 {
@@ -453,8 +463,11 @@
   char *input_mask;
   char *input_directory;
   char *scratch;
+  th_comment       tc;
   struct dirent **png_files;
-
+  int soft_target=0;
+  int ret;
+      
   while(1) {
 
     c=getopt_long(argc,argv,optstring,options,&long_option_index);
@@ -484,6 +497,27 @@
         }
         video_quality=0;
        break;
+    case '\1':
+      soft_target=1;
+      break;
+    case 'c':
+      vp3_compatible=1;
+      break;
+    case 'k':
+      keyframe_frequency=rint(atof(optarg));
+      if(keyframe_frequency<1 || keyframe_frequency>2147483647){
+        fprintf(stderr,"Illegal keyframe frequency\n");
+        exit(1);
+      }
+      break;
+
+    case 'd':
+      buf_delay=atoi(optarg);
+      if(buf_delay<=0){
+        fprintf(stderr,"Illegal buffer delay\n");
+        exit(1);
+      }
+      break;
      case 's':
        video_aspect_numerator=rint(atof(optarg));
        break;
@@ -496,12 +530,36 @@
      case 'F':
        video_fps_denominator=rint(atof(optarg));
        break;
-     case '\2':
-       chroma_format=OC_PF_422;
+     case '\5':
+       chroma_format=TH_PF_444;
        break;
-     case '\4':
-       chroma_format=OC_PF_444;
+     case '\6':
+       chroma_format=TH_PF_422;
        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();
         break;
@@ -512,6 +570,26 @@
     usage();
   }
 
+  if(soft_target){
+    if(video_rate<=0){
+      fprintf(stderr,"Soft rate target (--soft-tagret) requested without a bitrate (-V).\n");
+      exit(1);
+    }
+    if(video_quality==-1)
+      video_quality=0;
+  }else{
+    if(video_rate>0)
+      video_quality=0;
+    if(video_quality==-1)
+      video_quality=48;
+  }
+
+  if(keyframe_frequency<=0){
+    /*Use a default keyframe frequency of 64 for 1-pass (streaming) mode, and
+       256 for two-pass mode.*/
+    keyframe_frequency=twopass?256:64;
+  }
+
   input_mask = argv[optind];
   if (!input_mask) {
     fprintf(stderr, "no input files specified; run with -h for help.\n");
@@ -530,67 +608,262 @@
 	input_directory, input_filter);
 #endif
   n = scandir (input_directory, &png_files, include_files, alphasort);
-  for(i=0;i<n;i++) {
+
+  if (!n) {
+    fprintf(stderr, "no input files found; run with -h for help.\n");
+    exit(1);
+  }
+
+  ogg_fp = fopen(option_output, "wb");
+  if(!ogg_fp) {
+    fprintf(stderr, "%s: error: %s\n",
+      option_output, "couldn't open output file");
+    return 1;
+  }
+
+  srand(time(NULL));
+  if(ogg_stream_init(&ogg_os, rand())) {
+    fprintf(stderr, "%s: error: %s\n",
+      option_output, "couldn't create ogg stream state");
+    return 1;
+  }
+
+  for(passno=(twopass==3?1:twopass);passno<=(twopass==3?2:twopass);passno++){
     unsigned int w;
     unsigned int h;
     unsigned char *yuv;
     char input_png[1024];
     int last = 0;
 
-    sprintf(input_png, "%s/%s", input_directory, png_files[i]->d_name);
-
+    snprintf(input_png, 1023,"%s/%s", input_directory, png_files[0]->d_name);
     if(png_read(input_png, &w, &h, &yuv)) {
       fprintf(stderr, "could not read %s\n", input_png);
-      theora_close();
       exit(1);
     }
 
-    if(!theora_initialized) {
-      theora_info_init(&theora_ti);
+    if (passno!=2) fprintf(stderr,"%d frames, %dx%d\n",n,w,h);    
 
-      theora_ti.width = ((w + 15) >>4)<<4;
-      theora_ti.height = ((h + 15)>>4)<<4;
-      theora_ti.frame_width = w;
-      theora_ti.frame_height = h;
-      theora_ti.offset_x = 0;
-      theora_ti.offset_y = 0;
-      theora_ti.fps_numerator = video_fps_numerator;
-      theora_ti.fps_denominator = video_fps_denominator;
-      theora_ti.aspect_numerator = video_aspect_numerator;
-      theora_ti.aspect_denominator = video_aspect_denominator;
-      theora_ti.colorspace = OC_CS_UNSPECIFIED;
-      theora_ti.pixelformat = chroma_format;
-      theora_ti.target_bitrate = video_rate;
-      theora_ti.quality = video_quality;
+    /* 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;
+    }
 
-      theora_ti.dropframes_p = 0;
-      theora_ti.quick_p = 1;
-      theora_ti.keyframe_auto_p = 1;
-      theora_ti.keyframe_frequency = 64;
-      theora_ti.keyframe_frequency_force = 64;
-      theora_ti.keyframe_data_target_bitrate = video_rate * 1.5;
-      theora_ti.keyframe_mindistance = 8;
-      theora_ti.noise_sensitivity = 1;
+    fprintf(stderr, "%s\n", input_png); 
 
-      if(theora_open(option_output)) {
-        /* XXX: cleanup */
-        return 1;
-      }
+    th_info_init(&ti);    
+    ti.frame_width = ((w + 15) >>4)<<4;
+    ti.frame_height = ((h + 15)>>4)<<4;
+    ti.pic_width = w;
+    ti.pic_height = h;
+    ti.pic_x = 0;
+    ti.pic_y = 0;
+    ti.fps_numerator = video_fps_numerator;
+    ti.fps_denominator = video_fps_denominator;
+    ti.aspect_numerator = video_aspect_numerator;
+    ti.aspect_denominator = video_aspect_denominator;
+    ti.colorspace = TH_CS_UNSPECIFIED;
+    ti.pixel_fmt = chroma_format;
+    ti.target_bitrate = video_rate;
+    ti.quality = video_quality;
+    ti.keyframe_granule_shift=ilog(keyframe_frequency-1);
 
-      theora_initialized = 1;
+    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_numerator/video_fps_denominator)
+          arg=keyframe_frequency*7>>1;
+        else
+          arg=30*video_fps_numerator/video_fps_denominator;
+        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;
+      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 the second pass here.
+        We make this call just to set the encoder into 2-pass mode, because
+         by default enabling two-pass sets the buffer delay to the whole file
+         (because there's no way to explicitly request that behavior).
+        If we waited until we were actually encoding, it would overwite our
+         settings.*/
+      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){
+        if(fseek(twopass_file,0,SEEK_SET)<0){
+          fprintf(stderr,"Unable to seek in two-pass data file.\n");
+          exit(1);
+        }
+      }
+    }
+    /*Now we can set the buffer delay if the user requested a non-default one
+       (this has to be done after two-pass is enabled).*/
+    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); 
+    }
+    th_comment_clear(&tc);
+    if(passno!=1){
+      ogg_stream_packetin(&ogg_os,&op);
+      if(ogg_stream_pageout(&ogg_os,&og)!=1){
+        fprintf(stderr,"Internal Ogg library error.\n");
+        exit(1);
+      }
+      fwrite(og.header,1,og.header_len,ogg_fp);
+      fwrite(og.body,1,og.body_len,ogg_fp);
+    }
+    /* create the remaining theora headers */
+    for(;;){
+      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(&ogg_os,&op);
+    }
+    /* 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(&ogg_os,&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,ogg_fp);
+        fwrite(og.body,1,og.body_len,ogg_fp);
+      }
+    }
 
-    if(i >= n-1) last = 1;
-    if(theora_write_frame(w, h, yuv, last)) {
-      theora_close();
-      free(input_directory);
-      free(input_filter);
-      exit(1);
+    i=0; last=0;
+    do {
+      if(i >= n-1) last = 1;
+      if(theora_write_frame(w, h, yuv, last)) {
+        goto close;
+        exit(1);
+      }
+      free(yuv);    
+      i++;
+      if (!last) {
+        snprintf(input_png, 1023,"%s/%s", input_directory, png_files[i]->d_name);
+        if(png_read(input_png, &w, &h, &yuv)) {
+          fprintf(stderr, "could not read %s\n", input_png);
+          exit(1);
+        }
+       fprintf(stderr, "%s\n", input_png);
+      }      
+    } while (!last);
+
+    if(passno==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);
     }
+    th_encode_free(td);
+  }
 
-    free(yuv);
+close: 
+  if(ogg_stream_flush(&ogg_os, &og)) {
+    fwrite(og.header, og.header_len, 1, ogg_fp);
+    fwrite(og.body, og.body_len, 1, ogg_fp);
+  }            
+
+  free(input_directory);
+  free(input_filter);
+
+  while (n--) free(png_files[n]);
+  free(png_files);  
+
+  if(ogg_fp){
+    fflush(ogg_fp);
+    if(ogg_fp!=stdout)fclose(ogg_fp);
   }
 
-  theora_close();
+  ogg_stream_clear(&ogg_os);
+  if(twopass_file)fclose(twopass_file);
+  fprintf(stderr,"\r   \ndone.\n\n");
+
   return 0;
 }



More information about the commits mailing list