[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