[xiph-commits] r7714 - in experimental/derf/theora-exp: examples tools

tterribe at motherfish-iii.xiph.org tterribe at motherfish-iii.xiph.org
Mon Sep 6 17:55:05 PDT 2004


Author: tterribe
Date: 2004-09-06 17:55:05 -0700 (Mon, 06 Sep 2004)
New Revision: 7714

Modified:
   experimental/derf/theora-exp/examples/encoder_example.c
   experimental/derf/theora-exp/tools/yuv2yuv4mpeg.c
Log:
Added addtional chroma type support to the tools.

yuy2yuv4mpeg now supports all of the chroma types allowed by mjpegtools.
It was also updated to allow a few different input multiplexing styles
 (separate files for each frame, separate files for each component, and any
 combination).

encoder_example now accepts all of these chroma types as input, and will
 automatically convert to one of the formats Theora supports.
This includes performing filtering to re-site chroma samples from different
 standards.
There are some additional types in use that don't have a standard yuv4mpeg tag
 which we could still support easily, but we'll defer making up new tag names
 for now.

Specifically, there's no tag for Theora's own 4:2:2 chroma sampling locations!
These are the same as JPEG's, yet there seems to be a great deal of confusion
 over what JPEG actually uses.
Many people seem to think JPEG uses the same locations as MPEG2 for 4:2:2.
The JNG specification even gets the two types exactly backwards (or they
 intentionally define their sampling locations to be the different from JPEG's;
 it's unclear from the exposition).
However, the JFIF specification is very clear on the matter.


Modified: experimental/derf/theora-exp/examples/encoder_example.c
===================================================================
--- experimental/derf/theora-exp/examples/encoder_example.c	2004-09-07 00:52:14 UTC (rev 7713)
+++ experimental/derf/theora-exp/examples/encoder_example.c	2004-09-07 00:55:05 UTC (rev 7714)
@@ -80,11 +80,31 @@
 int pic_h=0;
 int pic_x=0;
 int pic_y=0;
-int video_hzn=-1;
-int video_hzd=-1;
-int video_an=-1;
-int video_ad=-1;
+int video_fps_n=-1;
+int video_fps_d=-1;
+int video_par_n=-1;
+int video_par_d=-1;
+char interlace;
+int src_c_dec_h=2;
+int src_c_dec_v=2;
+int dst_c_dec_h=2;
+int dst_c_dec_v=2;
+char chroma_type[16];
 
+/*The size of each converted frame buffer.*/
+size_t y4m_dst_buf_sz;
+/*The amount to read directly into the converted frame buffer.*/
+size_t y4m_dst_buf_read_sz;
+/*The size of the auxilliary buffer.*/
+size_t y4m_aux_buf_sz;
+/*The amount to read into the auxilliary buffer.*/
+size_t y4m_aux_buf_read_sz;
+
+/*The function used perform chroma conversion.*/
+typedef void (*y4m_convert_func)(unsigned char *_dst,unsigned char *_aux);
+
+y4m_convert_func y4m_convert=NULL;
+
 int video_r=-1;
 int video_q=48;
 
@@ -124,11 +144,491 @@
   exit(1);
 }
 
+static int y4m_parse_tags(char *_tags){
+  int   got_w;
+  int   got_h;
+  int   got_fps;
+  int   got_interlace;
+  int   got_par;
+  int   got_chroma;
+  int   tmp_video_fps_n;
+  int   tmp_video_fps_d;
+  int   tmp_video_par_n;
+  int   tmp_video_par_d;
+  char *p;
+  char *q;
+  got_w=got_h=got_fps=got_interlace=got_par=got_chroma=0;
+  for(p=_tags;;p=q){
+    /*Skip any leading spaces.*/
+    while(*p==' ')p++;
+    /*If that's all we have, stop.*/
+    if(p[0]=='\0')break;
+    /*Find the end of this tag.*/
+    for(q=p+1;*q!='\0'&&*q!=' ';q++);
+    /*Process the tag.*/
+    switch(p[0]){
+      case 'W':{
+        if(sscanf(p+1,"%d",&pic_w)!=1)return -1;
+        got_w=1;
+      }break;
+      case 'H':{
+        if(sscanf(p+1,"%d",&pic_h)!=1)return -1;
+        got_h=1;
+      }break;
+      case 'F':{
+        if(sscanf(p+1,"%d:%d",&tmp_video_fps_n,&tmp_video_fps_d)!=2)return -1;
+        got_fps=1;
+      }break;
+      case 'I':{
+        interlace=p[1];
+        got_interlace=1;
+      }break;
+      case 'A':{
+        if(sscanf(p+1,"%d:%d",&tmp_video_par_n,&tmp_video_par_d)!=2)return -1;
+        got_par=1;
+      }break;
+      case 'C':{
+        if(q-p>16)return -1;
+        memcpy(chroma_type,p+1,q-p-1);
+        chroma_type[q-p-1]='\0';
+        got_chroma=1;
+      }break;
+      /*Ignore unknown tags.*/
+    }
+  }
+  if(!got_w||!got_h||!got_fps||!got_interlace||!got_par)return -1;
+  /*Chroma-type is not specified in older files, e.g., those generated by
+     mplayer.*/
+  if(!got_chroma)strcpy(chroma_type,"420");
+  /*Update fps and aspect ratio globals if not specified in the command line.*/
+  if(video_fps_n==-1)video_fps_n=tmp_video_fps_n;
+  if(video_fps_d==-1)video_fps_d=tmp_video_fps_d;
+  if(video_par_n==-1)video_par_n=tmp_video_par_n;
+  if(video_par_d==-1)video_par_d=tmp_video_par_d;
+  return 0;
+}
+
+/*All anti-aliasing filters in the following conversion functions are based on
+   one of two window functions:
+  The 6-tap Lanczos window (for down-sampling and shifts):
+   sinc(\pi*t)*sinc(\pi*t/3), |t|<3  (sinc(t)==sin(t)/t)
+   0,                         |t|>=3
+  The 4-tap Mitchell window (for up-sampling):
+   7|t|^3-12|t|^2+16/3,             |t|<1
+   -(7/3)|x|^3+12|x|^2-20|x|+32/3,  |t|<2
+   0,                               |t|>=2
+  The number of taps is intentionally kept small to reduce computational
+   overhead and limit ringing.
+
+  The taps from these filters are scaled so that their sum is 1, and the result
+   is scaled by 128 and rounded to integers to create a filter whose
+   intermediate values fit inside 16 bits.
+  Coefficients are rounded in such a way as to ensure their sum is still 128,
+   which is usually equivalent to normal rounding.*/
+
+#define OC_MINI(_a,_b)      ((_a)>(_b)?(_b):(_a))
+#define OC_MAXI(_a,_b)      ((_a)<(_b)?(_b):(_a))
+#define OC_CLAMPI(_a,_b,_c) (OC_MAXI(_a,OC_MINI(_b,_c)))
+
+/*420jpeg chroma samples are sited like:
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |   BR  |       |   BR  |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |   BR  |       |   BR  |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+
+  420mpeg2 chroma samples are sited like:
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  BR      |       BR      |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  BR      |       BR      |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+
+  We use a resampling filter to shift the site locations one quarter pixel (at
+   the chroma plane's resolution) to the right.
+  The 4:2:2 modes look exactly the same, except there are twice as many chroma
+   lines, and they are vertically co-sited with the luma samples in both the
+   mpeg2 and jpeg cases (thus requiring no vertical resampling).*/
+static void y4m_convert_42xmpeg2_42xjpeg(unsigned char *_dst,
+ unsigned char *_aux){
+  int c_w;
+  int c_h;
+  int pli;
+  int y;
+  int x;
+  /*Skip past the luma data.*/
+  _dst+=pic_w*pic_h;
+  /*Compute the size of each chroma plane.*/
+  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;
+  for(pli=1;pli<3;pli++){
+    for(y=0;y<c_h;y++){
+      /*Filter: [4 -17 114 35 -9 1]/128, derived from a 6-tap Lanczos
+         window.*/
+      for(x=0;x<OC_MINI(c_w,2);x++){
+        _dst[x]=OC_CLAMPI(0,4*_aux[0]-17*_aux[OC_MAXI(x-1,0)]+
+         114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+         _aux[OC_MINI(x+3,c_w-1)]+64>>7,255);
+      }
+      for(;x<c_w-3;x++){
+        _dst[x]=OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+114*_aux[x]+
+         35*_aux[x+1]-9*_aux[x+2]+_aux[x+3]+64>>7,255);
+      }
+      for(;x<c_w;x++){
+        _dst[x]=OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+         114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+         _aux[c_w-1]+64>>7,255);
+      }
+      _dst+=c_w;
+      _aux+=c_w;
+    }
+  }
+}
+
+/*This format is only used for interlaced content, but is included for
+   completeness.
+
+  420jpeg chroma samples are sited like:
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |   BR  |       |   BR  |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |   BR  |       |   BR  |
+  |       |       |       |
+  Y-------Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+
+  420paldv chroma samples are sited like:
+  YR------Y-------YR------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  YB------Y-------YB------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  YR------Y-------YR------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  YB------Y-------YB------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+
+  We use a resampling filter to shift the site locations one quarter pixel (at
+   the chroma plane's resolution) to the right.
+  Then we use another filter to move the C_r location down one quarter pixel,
+   and the C_b location up one quarter pixel.*/
+static void y4m_convert_42xpaldv_42xjpeg(unsigned char *_dst,
+ unsigned char *_aux){
+  unsigned char *tmp;
+  int            c_w;
+  int            c_h;
+  int            c_sz;
+  int            pli;
+  int            y;
+  int            x;
+  /*Skip past the luma data.*/
+  _dst+=pic_w*pic_h;
+  /*Compute the size of each chroma plane.*/
+  c_w=(pic_w+1)/2;
+  c_h=(pic_h+dst_c_dec_h-1)/dst_c_dec_h;
+  c_sz=c_w*c_h;
+  /*First do the horizontal re-sampling.
+    This is the same as the mpeg2 case, except that after the horizontal case,
+     we need to apply a second vertical filter.*/
+  tmp=_aux+2*c_sz;
+  for(pli=1;pli<3;pli++){
+    for(y=0;y<c_h;y++){
+      /*Filter: [4 -17 114 35 -9 1]/128, derived from a 6-tap Lanczos
+         window.*/
+      for(x=0;x<OC_MINI(c_w,2);x++){
+        tmp[x]=OC_CLAMPI(0,4*_aux[0]-17*_aux[OC_MAXI(x-1,0)]+
+         114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+         _aux[OC_MINI(x+3,c_w-1)]+64>>7,255);
+      }
+      for(;x<c_w-3;x++){
+        tmp[x]=OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+114*_aux[x]+
+         35*_aux[x+1]-9*_aux[x+2]+_aux[x+3]+64>>7,255);
+      }
+      for(;x<c_w;x++){
+        tmp[x]=OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+         114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+         _aux[c_w-1]+64>>7,255);
+      }
+      tmp+=c_w;
+      _aux+=c_w;
+    }
+    /*For actual interlaced material, this would have to be done separately on
+       each field, and the shift amounts would be different.
+      C_r moves down 1/8, C_b up 3/8 in the top field, and C_r moves down 3/8,
+       C_b up 1/8 in the bottom field.
+      The corresponding filters are:
+       Down 1/8 (reverse order for up): [3 -11 125 15 -4 0]/128
+       Down 3/8 (reverse order for up): [4 -19 98 56 -13 2]/128*/
+    switch(pli){
+      case 1:{
+        tmp-=c_sz;
+        /*Slide C_b up a quarter-pel.
+          This is the same filter used above, but in the other order.*/
+        for(x=0;x<c_w;x++){
+          for(y=0;y<OC_MINI(c_h,3);y++){
+            _dst[y*c_w]=OC_CLAMPI(0,tmp[0]-9*tmp[OC_MAXI(y-2,0)*c_w]+
+             35*tmp[OC_MAXI(y-1,0)*c_w]+114*tmp[y*c_w]-
+             17*tmp[OC_MINI(y+1,c_h-1)*c_w]+4*tmp[OC_MINI(y+2,c_h-1)*c_w]+
+             64>>7,255);
+          }
+          for(;y<c_h-2;y++){
+            _dst[y*c_w]=OC_CLAMPI(0,tmp[(y-3)*c_w]-9*tmp[(y-2)*c_w]+
+             35*tmp[(y-1)*c_w]+114*tmp[y*c_w]-17*tmp[(y+1)*c_w]+
+             4*tmp[(y+2)*c_w]+64>>7,255);
+          }
+          for(;y<c_h;y++){
+            _dst[y*c_w]=OC_CLAMPI(0,tmp[(y-3)*c_w]-9*tmp[(y-2)*c_w]+
+             35*tmp[(y-1)*c_w]+114*tmp[y*c_w]-17*tmp[OC_MINI(y+1,c_h-1)*c_w]+
+             4*tmp[(c_h-1)*c_w]+64>>7,255);
+          }
+          _dst++;
+          tmp++;
+        }
+        _dst+=c_sz-c_w;
+        tmp-=c_w;
+      }break;
+      case 2:{
+        tmp-=c_sz;
+        /*Slide C_r down a quarter-pel.
+          This is the same as the horizonal filter.*/
+        for(x=0;x<c_w;x++){
+          for(y=0;y<OC_MINI(c_h,2);y++){
+            _dst[y*c_w]=OC_CLAMPI(0,4*tmp[0]-17*tmp[OC_MAXI(y-1,0)*c_w]+
+             114*tmp[y*c_w]+35*tmp[OC_MINI(y+1,c_h-1)*c_w]-
+             9*tmp[OC_MINI(y+2,c_h-1)*c_w]+tmp[OC_MINI(y+3,c_h-1)*c_w]+
+             64>>7,255);
+          }
+          for(;y<c_h-3;y++){
+            _dst[y*c_w]=OC_CLAMPI(0,4*tmp[(y-2)*c_w]-17*tmp[(y-1)*c_w]+
+             114*tmp[y*c_w]+35*tmp[(y+1)*c_w]-9*tmp[(y+2)*c_w]+
+             tmp[(y+3)*c_w]+64>>7,255);
+          }
+          for(;y<c_h;y++){
+            _dst[y*c_w]=OC_CLAMPI(0,4*tmp[(y-2)*c_w]-17*tmp[(y-1)*c_w]+
+             114*tmp[y*c_w]+35*tmp[OC_MINI(y+1,c_h-1)*c_w]-
+             9*tmp[OC_MINI(y+2,c_h-1)*c_w]+tmp[(c_h-1)*c_w]+64>>7,255);
+          }
+          _dst++;
+          tmp++;
+        }
+      }break;
+    }
+  }
+}
+
+/*422jpeg chroma samples are sited like:
+  Y---BR--Y-------Y---BR--Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  Y---BR--Y-------Y---BR--Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  Y---BR--Y-------Y---BR--Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  Y---BR--Y-------Y---BR--Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+
+  411 chroma samples are sited like:
+  YBR-----Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  YBR-----Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  YBR-----Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+  YBR-----Y-------Y-------Y-------
+  |       |       |       |
+  |       |       |       |
+  |       |       |       |
+
+  We use a filter to resample at site locations one eighth pixel (at the source
+   chroma plane's horizontal resolution) and five eighths of a pixel to the
+   right.*/
+static void y4m_convert_411_422jpeg(unsigned char *_dst,
+ unsigned char *_aux){
+  int c_w;
+  int dst_c_w;
+  int c_h;
+  int pli;
+  int y;
+  int x;
+  /*Skip past the luma data.*/
+  _dst+=pic_w*pic_h;
+  /*Compute the size of each chroma plane.*/
+  c_w=(pic_w+src_c_dec_h-1)/src_c_dec_h;
+  dst_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;
+  for(pli=1;pli<3;pli++){
+    for(y=0;y<c_h;y++){
+      /*Filters: [1 110 18 -1]/128 and [-3 50 86 -5]/128, both derived from a
+         4-tap Mitchell window.*/
+      for(x=0;x<OC_MINI(c_w,1);x++){
+        _dst[x<<1]=OC_CLAMPI(0,111*_aux[0]+18*_aux[OC_MINI(1,c_w-1)]-
+         _aux[OC_MINI(2,c_w-1)]+64>>7,255);
+        _dst[x<<1|1]=OC_CLAMPI(0,47*_aux[0]+86*_aux[OC_MINI(1,c_w-1)]-
+         5*_aux[OC_MINI(2,c_w-1)]+64>>7,255);
+      }
+      for(;x<c_w-2;x++){
+        _dst[x<<1]=OC_CLAMPI(0,_aux[x-1]+110*_aux[x]+18*_aux[x+1]-_aux[x+2]+
+         64>>7,255);
+        _dst[x<<1|1]=OC_CLAMPI(0,-3*_aux[x-1]+50*_aux[x]+86*_aux[x+1]-
+         5*_aux[x+2]+64>>7,255);
+      }
+      for(;x<c_w;x++){
+        _dst[x<<1]=OC_CLAMPI(0,_aux[x-1]+110*_aux[x]+
+         18*_aux[OC_MINI(x+1,c_w-1)]-_aux[c_w-1]+64>>7,255);
+        if((x<<1|1)<dst_c_w){
+          _dst[x<<1|1]=OC_CLAMPI(0,-3*_aux[x-1]+50*_aux[x]+
+           86*_aux[OC_MINI(x+1,c_w-1)]-5*_aux[c_w-1]+64>>7,255);
+        }
+      }
+      _dst+=dst_c_w;
+      _aux+=c_w;
+    }
+  }
+}
+
+/*The image is padded with empty chroma components at 4:2:0.
+  This costs about 17 bits a frame to code.*/
+static void y4m_convert_mono_420jpeg(unsigned char *_dst,
+ unsigned char *_aux){
+  int c_sz;
+  _dst+=pic_w*pic_h;
+  c_sz=((pic_w+dst_c_dec_h-1)/dst_c_dec_h)*((pic_h+dst_c_dec_v-1)/dst_c_dec_v);
+  memset(_dst,128,c_sz*2);
+}
+
+#if 0
+/*Right now just 444 to 420.
+  Not too hard to generalize.*/
+static void y4m_convert_4xxjpeg_42xjpeg(unsigned char *_dst,
+ unsigned char *_aux){
+  unsigned char *tmp;
+  int            c_w;
+  int            c_h;
+  int            pic_sz;
+  int            tmp_sz;
+  int            c_sz;
+  int            pli;
+  int            y;
+  int            x;
+  /*Compute the size of each chroma plane.*/
+  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;
+  pic_sz=pic_w*pic_h;
+  tmp_sz=c_w*pic_h;
+  c_sz=c_w*c_h;
+  _dst+=pic_sz;
+  for(pli=1;pli<3;pli++){
+    tmp=_aux+pic_sz;
+    /*In reality, the horizontal and vertical steps could be pipelined, for
+       less memory consumption and better cache performance, but we do them
+       separately for simplicity.*/
+    /*First do horizontal filtering (convert to 4:2:2)*/
+    /*Filter: [3 -17 78 78 -17 3]/128, derived from a 6-tap Lanczos window.*/
+    for(y=0;y<pic_h;y++){
+      for(x=0;x<OC_MINI(pic_w,2);x+=2){
+        tmp[x>>1]=OC_CLAMPI(0,64*_aux[0]+78*_aux[OC_MINI(1,pic_w-1)]-
+         17*_aux[OC_MINI(2,pic_w-1)]+3*_aux[OC_MINI(3,pic_w-1)]+64>>7,255);
+      }
+      for(;x<pic_w-3;x+=2){
+        tmp[x>>1]=OC_CLAMPI(0,3*(_aux[x-2]+_aux[x+3])-17*(_aux[x-1]+_aux[x+2])+
+         78*(_aux[x]+_aux[x+1])+64>>7,255);
+      }
+      for(;x<pic_w;x+=2){
+        tmp[x>>1]=OC_CLAMPI(0,3*(_aux[x-2]+_aux[pic_w-1])-
+         17*(_aux[x-1]+_aux[OC_MINI(x+2,pic_w-1)])+
+         78*(_aux[x]+_aux[OC_MINI(x+1,pic_w-1)])+64>>7,255);
+      }
+      tmp+=c_w;
+      _aux+=pic_w;
+    }
+    _aux-=pic_sz;
+    tmp-=tmp_sz;
+    /*Now do the vertical filtering.*/
+    for(x=0;x<c_w;x++){
+      for(y=0;y<OC_MINI(pic_h,2);y+=2){
+        _dst[(y>>1)*c_w]=OC_CLAMPI(0,64*tmp[0]+78*tmp[OC_MINI(1,pic_h-1)*c_w]-
+         17*tmp[OC_MINI(2,pic_h-1)*c_w]+3*tmp[OC_MINI(3,pic_h-1)*c_w]+
+         64>>7,255);
+      }
+      for(;y<pic_h-3;y+=2){
+        _dst[(y>>1)*c_w]=OC_CLAMPI(0,3*(tmp[(y-2)*c_w]+tmp[(y+3)*c_w])-
+         17*(tmp[(y-1)*c_w]+tmp[(y+2)*c_w])+78*(tmp[y*c_w]+tmp[(y+1)*c_w])+
+         64>>7,255);
+      }
+      for(;y<pic_h;y+=2){
+        _dst[(y>>1)*c_w]=OC_CLAMPI(0,3*(tmp[(y-2)*c_w]+tmp[(pic_h-1)*c_w])-
+         17*(tmp[(y-1)*c_w]+tmp[OC_MINI(y+2,pic_h-1)*c_w])+
+         78*(tmp[y*c_w]+tmp[OC_MINI(y+1,pic_h-1)*c_w])+64>>7,255);
+      }
+      tmp++;
+      _dst++;
+    }
+    _dst-=c_w;
+  }
+}
+#endif
+
+
+/*No conversion function needed.*/
+static void y4m_convert_null(unsigned char *_dst,
+ unsigned char *_aux){
+}
+
 static void id_file(char *f){
   FILE *test;
   unsigned char buffer[80];
   int ret;
-  int tmp_video_hzn, tmp_video_hzd, tmp_video_an, tmp_video_ad;
 
   /* open it, look for magic */
 
@@ -230,7 +730,6 @@
     buffer[i]='\0';
 
     if(!memcmp(buffer,"MPEG",4)){
-      char interlace;
 
       if(video){
         /* umm, we already have one */
@@ -242,29 +741,94 @@
         fprintf(stderr,"Incorrect YUV input file version; YUV4MPEG2 required.\n");
       }
 
-      ret=sscanf((char *)buffer,"MPEG2 W%d H%d F%d:%d I%c A%d:%d",
-                 &pic_w,&pic_h,&tmp_video_hzn,&tmp_video_hzd,&interlace,
-                 &tmp_video_an,&tmp_video_ad);
-      if(ret<7){
+      ret=y4m_parse_tags(buffer+5);
+      if(ret<0){
         fprintf(stderr,"Error parsing YUV4MPEG2 header in file %s.\n",f);
         exit(1);
       }
 
-      /*update fps and aspect ratio globals if not specified in the command line*/
-      if (video_hzn==-1) video_hzn = tmp_video_hzn;
-      if (video_hzd==-1) video_hzd = tmp_video_hzd;
-      if (video_an==-1) video_an = tmp_video_an;
-      if (video_ad==-1) video_ad = tmp_video_ad;
-
       if(interlace!='p'){
         fprintf(stderr,"Input video is interlaced; Theora handles only progressive scan\n");
         exit(1);
       }
 
+      if(strcmp(chroma_type,"420")==0||strcmp(chroma_type,"420jpeg")==0){
+        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+        y4m_dst_buf_read_sz=pic_w*pic_h+2*((pic_w+1)/2)*((pic_h+1)/2);
+        y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
+        y4m_convert=y4m_convert_null;
+      }
+      else if(strcmp(chroma_type,"420mpeg2")==0){
+        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+        y4m_dst_buf_read_sz=pic_w*pic_h;
+        /*Chroma filter required: read into the aux buf first.*/
+        y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+1)/2)*((pic_h+1)/2);
+        y4m_convert=y4m_convert_42xmpeg2_42xjpeg;
+      }
+      else if(strcmp(chroma_type,"420paldv")==0){
+        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+        y4m_dst_buf_read_sz=pic_w*pic_h;
+        /*Chroma filter required: read into the aux buf first.
+          We need to make two filter passes, so we need some extra space in the
+           aux buffer.*/
+        y4m_aux_buf_sz=3*((pic_w+1)/2)*((pic_h+1)/2);
+        y4m_aux_buf_read_sz=2*((pic_w+1)/2)*((pic_h+1)/2);
+        y4m_convert=y4m_convert_42xpaldv_42xjpeg;
+      }
+      else if(strcmp(chroma_type,"422")==0){
+        src_c_dec_h=dst_c_dec_h=2;
+        src_c_dec_v=dst_c_dec_v=1;
+        y4m_dst_buf_read_sz=pic_w*pic_h;
+        /*Chroma filter required: read into the aux buf first.*/
+        y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+1)/2)*pic_h;
+        y4m_convert=y4m_convert_42xmpeg2_42xjpeg;
+      }
+      else if(strcmp(chroma_type,"411")==0){
+        src_c_dec_h=4;
+        /*We don't want to introduce any additional sub-sampling, so we
+           promote 4:1:1 material to 4:2:2, as the closest format Theora can
+           handle.*/
+        dst_c_dec_h=2;
+        src_c_dec_v=dst_c_dec_v=1;
+        y4m_dst_buf_read_sz=pic_w*pic_h;
+        /*Chroma filter required: read into the aux buf first.*/
+        y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+3)/4)*pic_h;
+        y4m_convert=y4m_convert_411_422jpeg;
+      }
+      else if(strcmp(chroma_type,"444")==0){
+        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=1;
+        y4m_dst_buf_read_sz=pic_w*pic_h*3;
+        y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
+        y4m_convert=y4m_convert_null;
+      }
+      else if(strcmp(chroma_type,"444alpha")==0){
+        src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=1;
+        y4m_dst_buf_read_sz=pic_w*pic_h*3;
+        /*Read the extra alpha plane into the aux buf.
+          It will be discarded.*/
+        y4m_aux_buf_sz=y4m_aux_buf_read_sz=pic_w*pic_h;
+        y4m_convert=y4m_convert_null;
+      }
+      else if(strcmp(chroma_type,"mono")==0){
+        src_c_dec_h=src_c_dec_v=0;
+        dst_c_dec_h=dst_c_dec_v=2;
+        y4m_dst_buf_read_sz=pic_w*pic_h;
+        y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
+        y4m_convert=y4m_convert_mono_420jpeg;
+      }
+      else{
+        fprintf(stderr,"Unknown chroma sampling type: %s\n",chroma_type);
+        exit(1);
+      }
+      /*The size of the final frame buffers is always computed from the
+         destination chroma decimation type.*/
+      y4m_dst_buf_sz=pic_w*pic_h+2*((pic_w+dst_c_dec_h-1)/dst_c_dec_h)*
+       ((pic_h+dst_c_dec_v-1)/dst_c_dec_v);
+
       video=test;
 
-      fprintf(stderr,"File %s is %dx%d %.02f fps YUV12 video.\n",
-              f,pic_w,pic_h,(double)video_hzn/video_hzd);
+      fprintf(stderr,"File %s is %dx%d %.02f fps %s video.\n",
+              f,pic_w,pic_h,(double)video_fps_n/video_fps_d,chroma_type);
 
       return;
     }
@@ -356,31 +920,31 @@
                             int videoflag){
   /* You'll go to Hell for using static variables */
   static int                 state=-1;
-  static unsigned char      *yuvframe[2];
-  static unsigned char      *upsamp_uvdata;
+  static unsigned char      *yuvframe[3];
   static int                 framenum;
   static theora_ycbcr_buffer ycbcr;
-  unsigned char             *line;
   ogg_packet                 op;
+  int                        pic_sz;
+  int                        frame_c_w;
+  int                        frame_c_h;
+  int                        c_w;
+  int                        c_h;
+  int                        c_sz;
   int i, e;
 
+  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;
+
   if(state==-1){
         /* initialize the double frame buffer */
-    yuvframe[0]=malloc(frame_w*frame_h*3/2);
-    yuvframe[1]=malloc(frame_w*frame_h*3/2);
+    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);
 
-        /* clear initial frame as it may be larger than actual video data */
-        /* fill Y plane with 0x10 and UV planes with 0X80, for black data */
-    memset(yuvframe[0],0x10,frame_w*frame_h);
-    memset(yuvframe[0]+frame_w*frame_h,0x80,frame_w*frame_h/2);
-    memset(yuvframe[1],0x10,frame_w*frame_h);
-    memset(yuvframe[1]+frame_w*frame_h,0x80,frame_w*frame_h/2);
-#if defined(OC_444_MODE)
-    upsamp_uvdata=malloc(2*frame_w*frame_h);
-#elif defined(OC_422_MODE)
-    upsamp_uvdata=malloc(frame_w*frame_h);
-#endif
-
     state=0;
   }
 
@@ -421,30 +985,20 @@
             exit(1);
           }
         }
-
-        /* read the Y plane into our frame buffer with centering */
-        line=yuvframe[i]+frame_w*pic_y+pic_x;
-        for(e=0;e<pic_h;e++){
-          ret=fread(line,1,pic_w,video);
-            if(ret!=pic_w) break;
-          line+=frame_w;
+        /*Read the frame data that needs no conversion.*/
+        if(fread(yuvframe[i],1,y4m_dst_buf_read_sz,video)!=
+         y4m_dst_buf_read_sz){
+          fprintf(stderr,"Error reading YUV frame data.\n");
+          exit(1);
         }
-        /* now get Cb plane*/
-        line=yuvframe[i]+(frame_w*frame_h)
-          +(frame_w/2)*(pic_y/2)+pic_x/2;
-        for(e=0;e<pic_h/2;e++){
-          ret=fread(line,1,pic_w/2,video);
-            if(ret!=pic_w/2) break;
-          line+=frame_w/2;
+        /*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);
         }
-        /* and the Cr plane*/
-        line=yuvframe[i]+(frame_w*frame_h*5/4)
-                  +(frame_w/2)*(pic_y/2)+pic_x/2;
-        for(e=0;e<pic_h/2;e++){
-          ret=fread(line,1,pic_w/2,video);
-            if(ret!=pic_w/2) break;
-          line+=frame_w/2;
-        }
+        /*Now convert the just read frame.*/
+        (*y4m_convert)(yuvframe[i],yuvframe[2]);
         state++;
       }
 
@@ -457,207 +1011,26 @@
       /* 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].ystride=frame_w;
-      ycbcr[0].data=yuvframe[0];
+      ycbcr[0].ystride=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].ystride=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].ystride=c_w;
+      ycbcr[2].data=ycbcr[1].data+c_sz;
 
-#if defined(OC_444_MODE)
-      {
-        int cboffset=frame_w*frame_h;
-        int croffset=cboffset+(frame_w/2)*(frame_h/2);
-        int f;
-
-        ycbcr[1].width=frame_w;
-        ycbcr[1].height=frame_h;
-        ycbcr[1].ystride=frame_w;
-        ycbcr[1].data=upsamp_uvdata;
-
-        for(e=0;e<frame_h/2;e++){
-          for(f=0;f<frame_w/2;f++){
-            if(e+1<frame_h/2){
-              if(f+1<frame_w/2){
-                ycbcr[1].data[e*2*frame_w+f*2]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-                ycbcr[1].data[e*2*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][cboffset+e*(frame_w/2)+f]+
-                 yuvframe[0][cboffset+e*(frame_w/2)+f+1]+1>>1);
-                ycbcr[1].data[(e*2+1)*frame_w+f*2]=(unsigned char)
-                 (yuvframe[0][cboffset+e*(frame_w/2)+f]+
-                 yuvframe[0][cboffset+(e+1)*(frame_w/2)+f]+1>>1);
-                ycbcr[1].data[(e*2+1)*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][cboffset+e*(frame_w/2)+f]+
-                 yuvframe[0][cboffset+e*(frame_w/2)+f+1]+
-                 yuvframe[0][cboffset+(e+1)*(frame_w/2)+f]+
-                 yuvframe[0][cboffset+(e+1)*(frame_w/2)+f+1]+2>>2);
-              }
-              else{
-                ycbcr[1].data[e*2*frame_w+f*2]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-                ycbcr[1].data[e*2*frame_w+f*2+1]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-                ycbcr[1].data[(e*2+1)*frame_w+f*2]=(unsigned char)
-                 (yuvframe[0][cboffset+e*(frame_w/2)+f]+
-                 yuvframe[0][cboffset+(e+1)*(frame_w/2)+f]+1>>1);
-                ycbcr[1].data[(e*2+1)*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][cboffset+e*(frame_w/2)+f]+
-                 yuvframe[0][cboffset+(e+1)*(frame_w/2)+f]+1>>1);
-              }
-            }
-            else{
-              if(f+1<frame_w/2){
-                ycbcr[1].data[e*2*frame_w+f*2]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-                ycbcr[1].data[e*2*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][cboffset+e*(frame_w/2)+f]+
-                 yuvframe[0][cboffset+e*(frame_w/2)+f+1]+1>>1);
-                ycbcr[1].data[(e*2+1)*frame_w+f*2]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-                ycbcr[1].data[(e*2+1)*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][cboffset+e*(frame_w/2)+f]+
-                 yuvframe[0][cboffset+e*(frame_w/2)+f+1]+1>>1);
-              }
-              else{
-                ycbcr[1].data[e*2*frame_w+f*2]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-                ycbcr[1].data[e*2*frame_w+f*2+1]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-                ycbcr[1].data[(e*2+1)*frame_w+f*2]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-                ycbcr[1].data[(e*2+1)*frame_w+f*2+1]=
-                 yuvframe[0][cboffset+e*(frame_w/2)+f];
-              }
-            }
-          }
-        }
-
-        ycbcr[2].width=frame_w;
-        ycbcr[2].height=frame_h;
-        ycbcr[2].ystride=frame_w;
-        ycbcr[2].data=upsamp_uvdata+frame_w*frame_h;
-
-        for(e=0;e<frame_h/2;e++){
-          for(f=0;f<frame_w/2;f++){
-            if(e+1<frame_h/2){
-              if(f+1<frame_w/2){
-                ycbcr[2].data[e*2*frame_w+f*2]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-                ycbcr[2].data[e*2*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][croffset+e*(frame_w/2)+f]+
-                 yuvframe[0][croffset+e*(frame_w/2)+f+1]+1>>1);
-                ycbcr[2].data[(e*2+1)*frame_w+f*2]=(unsigned char)
-                 (yuvframe[0][croffset+e*(frame_w/2)+f]+
-                 yuvframe[0][croffset+(e+1)*(frame_w/2)+f]+1>>1);
-                ycbcr[2].data[(e*2+1)*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][croffset+e*(frame_w/2)+f]+
-                 yuvframe[0][croffset+e*(frame_w/2)+f+1]+
-                 yuvframe[0][croffset+(e+1)*(frame_w/2)+f]+
-                 yuvframe[0][croffset+(e+1)*(frame_w/2)+f+1]+2>>2);
-              }
-              else{
-                ycbcr[2].data[e*2*frame_w+f*2]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-                ycbcr[2].data[e*2*frame_w+f*2+1]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-                ycbcr[2].data[(e*2+1)*frame_w+f*2]=(unsigned char)
-                 (yuvframe[0][croffset+e*(frame_w/2)+f]+
-                 yuvframe[0][croffset+(e+1)*(frame_w/2)+f]+1>>1);
-                ycbcr[2].data[(e*2+1)*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][croffset+e*(frame_w/2)+f]+
-                 yuvframe[0][croffset+(e+1)*(frame_w/2)+f]+1>>1);
-              }
-            }
-            else{
-              if(f+1<frame_w/2){
-                ycbcr[2].data[e*2*frame_w+f*2]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-                ycbcr[2].data[e*2*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][croffset+e*(frame_w/2)+f]+
-                 yuvframe[0][croffset+e*(frame_w/2)+f+1]+1>>1);
-                ycbcr[2].data[(e*2+1)*frame_w+f*2]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-                ycbcr[2].data[(e*2+1)*frame_w+f*2+1]=(unsigned char)
-                 (yuvframe[0][croffset+e*(frame_w/2)+f]+
-                 yuvframe[0][croffset+e*(frame_w/2)+f+1]+1>>1);
-              }
-              else{
-                ycbcr[2].data[e*2*frame_w+f*2]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-                ycbcr[2].data[e*2*frame_w+f*2+1]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-                ycbcr[2].data[(e*2+1)*frame_w+f*2]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-                ycbcr[2].data[(e*2+1)*frame_w+f*2+1]=
-                 yuvframe[0][croffset+e*(frame_w/2)+f];
-              }
-            }
-          }
-        }
-      }
-#elif defined(OC_422_MODE)
-      {
-        int cboffset=frame_w*frame_h;
-        int croffset=cboffset+(frame_w/2)*(frame_h/2);
-        int f;
-
-        ycbcr[1].width=frame_w/2;
-        ycbcr[1].height=frame_h;
-        ycbcr[1].ystride=frame_w/2;
-        ycbcr[1].data=upsamp_uvdata;
-
-        for(e=0;e<frame_h/2;e++){
-          for(f=0;f<frame_w/2;f++){
-            if(e+1<frame_h/2){
-              ycbcr[1].data[e*2*(frame_w/2)+f]=
-               yuvframe[0][cboffset+e*(frame_w/2)+f];
-              ycbcr[1].data[(e*2+1)*(frame_w/2)+f]=(unsigned char)
-               (yuvframe[0][cboffset+e*(frame_w/2)+f]+
-               yuvframe[0][cboffset+(e+1)*(frame_w/2)+f]+1>>1);
-            }
-            else{
-              ycbcr[1].data[e*2*(frame_w/2)+f]=
-               yuvframe[0][cboffset+e*(frame_w/2)+f];
-              ycbcr[1].data[(e*2+1)*(frame_w/2)+f]=
-               yuvframe[0][cboffset+e*(frame_w/2)+f];
-            }
-          }
-        }
-
-        ycbcr[2].width=frame_w/2;
-        ycbcr[2].height=frame_h;
-        ycbcr[2].ystride=frame_w/2;
-        ycbcr[2].data=upsamp_uvdata+frame_h*(frame_w/2);
-
-        for(e=0;e<frame_h/2;e++){
-          for(f=0;f<frame_w/2;f++){
-            if(e+1<frame_h/2){
-              ycbcr[2].data[e*2*(frame_w/2)+f]=
-               yuvframe[0][croffset+e*(frame_w/2)+f];
-              ycbcr[2].data[(e*2+1)*(frame_w/2)+f]=(unsigned char)
-               (yuvframe[0][croffset+e*(frame_w/2)+f]+
-               yuvframe[0][croffset+(e+1)*(frame_w/2)+f]+1>>1);
-            }
-            else{
-              ycbcr[2].data[e*2*(frame_w/2)+f]=
-               yuvframe[0][croffset+e*(frame_w/2)+f];
-              ycbcr[2].data[(e*2+1)*(frame_w/2)+f]=
-               yuvframe[0][croffset+e*(frame_w/2)+f];
-            }
-          }
-        }
-      }
-#else
-      ycbcr[1].width=frame_w/2;
-      ycbcr[1].height=frame_h/2;
-      ycbcr[1].ystride=frame_w/2;
-      ycbcr[1].data=yuvframe[0]+frame_w*frame_h;
-
-      ycbcr[2].width=frame_w/2;
-      ycbcr[2].height=frame_h/2;
-      ycbcr[2].ystride=frame_w/2;
-      ycbcr[2].data=yuvframe[0]+frame_w*frame_h*5/4;
-#endif
-
       theora_encode_ycbcr_in(td,ycbcr);
 
       /* if there's only one frame, it's the last in the stream */
@@ -765,19 +1138,19 @@
      break;
 
     case 's':
-      video_an=(int)rint(atof(optarg));
+      video_par_n=(int)rint(atof(optarg));
       break;
 
     case 'S':
-      video_ad=(int)rint(atof(optarg));
+      video_par_d=(int)rint(atof(optarg));
       break;
 
     case 'f':
-      video_hzn=(int)rint(atof(optarg));
+      video_fps_n=(int)rint(atof(optarg));
       break;
 
     case 'F':
-      video_hzd=(int)rint(atof(optarg));
+      video_fps_d=(int)rint(atof(optarg));
       break;
 
     default:
@@ -805,6 +1178,8 @@
   /* 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;
 
@@ -815,22 +1190,20 @@
   ti.pic_height=pic_h;
   ti.pic_x=pic_x;
   ti.pic_y=pic_y;
-  ti.fps_numerator=video_hzn;
-  ti.fps_denominator=video_hzd;
-  ti.aspect_numerator=video_an;
-  ti.aspect_denominator=video_ad;
+  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=OC_CS_UNSPECIFIED;
   ti.target_bitrate=video_r;
   ti.quality=video_q;
   ti.keyframe_granule_shift=6;
 
-#if defined(OC_444_MODE)
-  ti.pixel_fmt=OC_PF_444;
-#elif defined(OC_422_MODE)
-  ti.pixel_fmt=OC_PF_422;
-#else
-  ti.pixel_fmt=OC_PF_420;
-#endif
+  if(dst_c_dec_h==2){
+    if(dst_c_dec_v==2)ti.pixel_fmt=OC_PF_420;
+    else ti.pixel_fmt=OC_PF_422;
+  }
+  else ti.pixel_fmt=OC_PF_444;
 
   td=theora_encode_alloc(&ti);
   theora_info_clear(&ti);

Modified: experimental/derf/theora-exp/tools/yuv2yuv4mpeg.c
===================================================================
--- experimental/derf/theora-exp/tools/yuv2yuv4mpeg.c	2004-09-07 00:52:14 UTC (rev 7713)
+++ experimental/derf/theora-exp/tools/yuv2yuv4mpeg.c	2004-09-07 00:55:05 UTC (rev 7714)
@@ -5,10 +5,23 @@
 #include <stdlib.h>
 #include <string.h>
 
+/*This is a small utility used to convert many of the standard test sequences
+   into a common format, the YUV4MPEG format used by mjpegtools.
+  This is the format that the example encoder takes as input.
+
+  The set of file name patterns/extentions it supports is intentionally very
+   limited to keep the code simple.
+  However, it is not too difficult to rename existing file names to fit these
+   patterns.
+  E.g., to add leading 0's to 1- and 2-digit frame numbers with /bin/sh,
+    for i in tennis?.yuv ; do mv "$i" "tennis00${i##tennis}" ; done
+    for i in tennis??.yuv ; do mv "$i" "tennis0${i##tennis}" ; done*/
 int main(int _argc,char *_argv[]){
   FILE          *in_y;
   FILE          *in_u;
   FILE          *in_v;
+  FILE          *in_a;
+  FILE          *in_f;
   FILE          *out_yuv;
   int            y_w;
   int            y_h;
@@ -20,14 +33,48 @@
   int            par_n;
   int            par_d;
   char          *chroma;
+  int            have_chroma;
+  int            have_alpha;
+  int            frame_digits;
+  int            separate_planes;
   unsigned char *buf;
   char           fname[8192];
+  int            frame_num;
   int            argi;
   if(_argc<2){
-    fprintf(stderr,"Usage: qcif2yuv4mpeg <sequence_qcif> [<options]\n"
-     "Converts <sequence>.y, <sequence>.u, and <sequence>.v into a single\n"
-     "<sequence>.yuv file.\n"
+    fprintf(stderr,
+     "Usage: yuv2yuv4mpeg <sequence_qcif> [-p | -d<digits>] [<options>]\n"
+     "Converts one or more raw data files in a sequence into a single\n"
+     " YUV4MPEG file with appropriate headers to describe the video format.\n"
      "\n"
+     "The type of input read is controlled by one or more of:\n"
+     " -p              Input is split by component.\n"
+     "                 Opens up three files, <sequence>.y, <sequence>.u,\n"
+     "                  and <sequence>.v containing raw, unpadded image\n"
+     "                  data for each component, respectively.\n"
+     "                 If only a Y' component is present, <sequence>.y is\n"
+     "                  used.\n"
+     "                 If an alpha component is present, <sequence>.a is\n"
+     "                  used.\n"
+     "                 Which components are present is determined from the\n"
+     "                  -c flag described below.\n"
+     " -d<digits>      Input is split by frame.\n"
+     "                 Opens up <sequence><ddd>.yuv for frame <ddd>\n"
+     "                  containing raw, unpadded image data for all three\n"
+     "                  components.\n"
+     "                 <digits> specifies the number of decimal digits in\n"
+     "                  the frame number <ddd>.\n"
+     " If both of these options are be specified, the three files\n"
+     "  <sequence><ddd>.y, <sequence><ddd>.u, and <sequence><ddd>.v are\n"
+     "  opened for each frame.\n"
+     " If neither of these options are specified, a single <sequence>.raw\n"
+     "  file is opened, containing, the raw, unpadded image data for all\n"
+     "  components of all frames.\n"
+     " This must be organized in a planar fashion, with each complete frame\n"
+     "  in one block, in the order Y, U (Cb), V (Cr), alpha.\n"
+     " Which components are present is determined from the -c flag described\n"
+     "  below.\n"
+     "\n"
      "The default input video format is 29.97 fps progressive QCIF.\n"
      "Options:\n"
      " -w<width>\n"
@@ -35,36 +82,31 @@
      " -fn<frame rate numerator>\n"
      " -fd<frame rate denominator>\n"
      " -i<p|t|b>       Progressive or Top-field first or Bottom-field first.\n"
-     " -an<pixel aspect numerator>\n"
-     " -ad<pixel aspect denominator>\n"
      " -c<chroma>      Chroma subsampling format. One of:\n"
      "                   420jpeg\n"
      "                   420mpeg2\n"
      "                   420paldv\n"
-     "                   411*\n"
+     "                   411\n"
      "                   422\n"
      "                   444\n"
-     "                   444alpha*\n"
-     "                   mono*\n"
-     "                  *=Format not supported.\n");
+     "                   444alpha\n"
+     "                   mono\n"
+     " -an<pixel aspect numerator>\n"
+     " -ad<pixel aspect denominator>\n"
+     " Note: Most common frame sizes do not use square pixels:\n"
+     "  Name  Width  Height  -an   -ad\n"
+     "        720    576     128   117\n"
+     "  CIF   352    288     128   117\n"
+     "  QCIF  144    176     128   117\n"
+     "        720    480     4320  4739\n"
+     "  SIF   352    240     4320  4739\n"
+     " The HD TV sizes (1920x1080, 1280x720), really do use square pixels.\n"
+     " -o<offset>      The first frame number to use in -d mode.\n"
+     "                 If unspecified, 0 is used.\n");
     return -1;
   }
-  if(strlen(_argv[1])>8187){
-    fprintf(stderr,"File name too long.\n");
-    return -1;
-  }
-  sprintf(fname,"%s.y",_argv[1]);
-  in_y=fopen(fname,"rb");
-  sprintf(fname,"%s.u",_argv[1]);
-  in_u=fopen(fname,"rb");
-  sprintf(fname,"%s.v",_argv[1]);
-  in_v=fopen(fname,"rb");
-  sprintf(fname,"%s.yuv",_argv[1]);
-  out_yuv=fopen(fname,"wb");
-  if(in_y==NULL||in_u==NULL||in_v==NULL||out_yuv==NULL){
-    fprintf(stderr,"Error opening files.\n");
-    return -1;
-  }
+  frame_digits=frame_num=separate_planes=0;
+  in_y=in_u=in_v=in_a=in_f=NULL;
   y_w=176;
   y_h=144;
   fps_n=30000;
@@ -79,6 +121,8 @@
       return -1;
     }
     switch(_argv[argi][1]){
+      case 'p':separate_planes=1;break;
+      case 'd':frame_digits=atoi(_argv[argi]+2);break;
       case 'w':y_w=atoi(_argv[argi]+2);break;
       case 'h':y_h=atoi(_argv[argi]+2);break;
       case 'i':ip=_argv[argi][2];break;
@@ -103,6 +147,7 @@
           break;
         }
       }
+      case 'o':frame_num=atoi(_argv[argi]+2);break;
       default:{
         fprintf(stderr,"Error parsing arguments: unknown switch '%c'.\n",
          _argv[argi][1]);
@@ -113,20 +158,94 @@
   if(strncmp(chroma,"420",3)==0){
     c_w=y_w+1>>1;
     c_h=y_h+1>>1;
+    have_chroma=1;
   }
+  else if(strncmp(chroma,"411",3)==0){
+    c_w=y_w+3>>2;
+    c_h=y_h;
+    have_chroma=1;
+  }
   else if(strcmp(chroma,"422")==0){
     c_w=y_w+1>>1;
     c_h=y_h;
+    have_chroma=1;
   }
   else if(strncmp(chroma,"444",3)==0){
     c_w=y_w;
     c_h=y_h;
+    have_chroma=1;
   }
+  else if(strncmp(chroma,"mono",4)==0){
+    c_w=c_h=0;
+    have_chroma=0;
+  }
   else{
     fprintf(stderr,"Error parsing arguments: Unsupported chroma mode: %s.\n",
      chroma);
     return -1;
   }
+  have_alpha=strstr(chroma,"alpha")!=NULL;
+  if(frame_digits<0)frame_digits=0;
+  if(strlen(_argv[1])>8178-frame_digits){
+    fprintf(stderr,"File name too long.\n");
+    return -1;
+  }
+  /*Open input and output files.*/
+  if(separate_planes){
+    if(frame_digits>0){
+      sprintf(fname,"%s%0*d.y",_argv[1],frame_digits,frame_num);
+      in_y=fopen(fname,"rb");
+      if(have_chroma){
+         sprintf(fname,"%s%0*d.u",_argv[1],frame_digits,frame_num);
+         in_u=fopen(fname,"rb");
+         sprintf(fname,"%s%0*d.v",_argv[1],frame_digits,frame_num);
+         in_v=fopen(fname,"rb");
+      }
+      if(have_alpha){
+         sprintf(fname,"%s%0*d.a",_argv[1],frame_digits,frame_num);
+         in_a=fopen(fname,"rb");
+      }
+    }
+    else{
+      sprintf(fname,"%s.y",_argv[1]);
+      in_y=fopen(fname,"rb");
+      if(have_chroma){
+        sprintf(fname,"%s.u",_argv[1]);
+        in_u=fopen(fname,"rb");
+        sprintf(fname,"%s.v",_argv[1]);
+        in_v=fopen(fname,"rb");
+      }
+      if(have_alpha){
+        sprintf(fname,"%s.a",_argv[1]);
+        in_a=fopen(fname,"rb");
+      }
+    }
+  }
+  else{
+    if(frame_digits>0){
+      sprintf(fname,"%s%0*d.yuv",_argv[1],frame_digits,frame_num);
+      in_f=fopen(fname,"rb");
+    }
+    else{
+      sprintf(fname,"%s.raw",_argv[1]);
+      in_f=fopen(fname,"rb");
+    }
+    in_y=in_f;
+    if(have_chroma)in_u=in_v=in_f;
+    if(have_alpha)in_a=in_f;
+  }
+  if(in_y==NULL||have_chroma&&(in_u==NULL||in_v==NULL)||
+   have_alpha&&in_a==NULL){
+    fprintf(stderr,"Error opening input file(s).\n");
+    return -1;
+  }
+  sprintf(fname,"%s.yuv",_argv[1]);
+  out_yuv=fopen(fname,"wb");
+  if(out_yuv==NULL){
+    fprintf(stderr,"Error opening output file.\n");
+    return -1;
+  }
+  /*Start output.*/
   fprintf(out_yuv,"YUV4MPEG2 W%i H%i F%i:%i I%c A%i:%i",
    y_w,y_h,fps_n,fps_d,ip,par_n,par_d);
   if(strcmp(chroma,"420jpeg")!=0)fprintf(out_yuv," C%s",chroma);
@@ -136,10 +255,49 @@
     if(fread(buf,y_w,y_h,in_y)<y_h)break;
     fprintf(out_yuv,"FRAME\n");
     fwrite(buf,y_w,y_h,out_yuv);
-    fread(buf,c_w,c_h,in_u);
-    fwrite(buf,c_w,c_h,out_yuv);
-    fread(buf,c_w,c_h,in_v);
-    fwrite(buf,c_w,c_h,out_yuv);
+    if(have_chroma){
+      fread(buf,c_w,c_h,in_u);
+      fwrite(buf,c_w,c_h,out_yuv);
+      fread(buf,c_w,c_h,in_v);
+      fwrite(buf,c_w,c_h,out_yuv);
+    }
+    if(have_alpha){
+      fread(buf,y_w,y_h,in_a);
+      fwrite(buf,y_w,y_h,out_yuv);
+    }
+    if(frame_digits>0){
+      frame_num++;
+      if(separate_planes){
+        sprintf(fname,"%s%0*d.y",_argv[1],frame_digits,frame_num);
+        fclose(in_y);
+        in_y=fopen(fname,"rb");
+        if(have_chroma){
+          sprintf(fname,"%s%0*d.u",_argv[1],frame_digits,frame_num);
+          fclose(in_u);
+          in_u=fopen(fname,"rb");
+          sprintf(fname,"%s%0*d.v",_argv[1],frame_digits,frame_num);
+          fclose(in_v);
+          in_v=fopen(fname,"rb");
+        }
+        if(have_alpha){
+          sprintf(fname,"%s%0*d.a",_argv[1],frame_digits,frame_num);
+          fclose(in_a);
+          in_a=fopen(fname,"rb");
+        }
+      }
+      else{
+        sprintf(fname,"%s%0*d.yuv",_argv[1],frame_digits,frame_num);
+        fclose(in_f);
+        in_f=fopen(fname,"rb");
+        in_y=in_f;
+        if(have_chroma)in_u=in_v=in_f;
+        if(have_alpha)in_a=in_f;
+      }
+      if(in_y==NULL||have_chroma&&(in_u==NULL||in_v==NULL)||
+       have_alpha&&in_a==NULL){
+        break;
+      }
+    }
   }
   return 0;
 }



More information about the commits mailing list