[xiph-commits] r16261 - branches/theora-thusnelda/examples

tterribe at svn.xiph.org tterribe at svn.xiph.org
Sat Jul 11 13:21:36 PDT 2009


Author: tterribe
Date: 2009-07-11 13:21:36 -0700 (Sat, 11 Jul 2009)
New Revision: 16261

Added:
   branches/theora-thusnelda/examples/dump_video.c
Removed:
   branches/theora-thusnelda/examples/dump_video.c
Log:
Switch to theora-exp's dump_video (with the new API), merge changes from the
 old dump_video, and add properly tag output with the chroma type.


Deleted: branches/theora-thusnelda/examples/dump_video.c
===================================================================
--- branches/theora-thusnelda/examples/dump_video.c	2009-07-11 20:19:36 UTC (rev 16260)
+++ branches/theora-thusnelda/examples/dump_video.c	2009-07-11 20:21:36 UTC (rev 16261)
@@ -1,450 +0,0 @@
-/********************************************************************
- *                                                                  *
- * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.   *
- * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
- * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
- * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
- *                                                                  *
- * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007                *
- * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
- *                                                                  *
- ********************************************************************
-
-  function: example dumpvid application; dumps  Theora streams
-  last mod: $Id$
-
- ********************************************************************/
-
-/* By Mauricio Piacentini (mauricio at xiph.org) */
-/*  simply dump decoded YUV data, for verification of theora bitstream */
-
-#if !defined(_GNU_SOURCE)
-#define _GNU_SOURCE
-#endif
-#if !defined(_LARGEFILE_SOURCE)
-#define _LARGEFILE_SOURCE
-#endif
-#if !defined(_LARGEFILE64_SOURCE)
-#define _LARGEFILE64_SOURCE
-#endif
-#if !defined(_FILE_OFFSET_BITS)
-#define _FILE_OFFSET_BITS 64
-#endif
-
-#include <stdio.h>
-#if !defined(_WIN32)
-#include <getopt.h>
-#include <unistd.h>
-#else
-#include "getopt.h"
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <sys/timeb.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-/*Yes, yes, we're going to hell.*/
-#if defined(_WIN32)
-#include <io.h>
-#endif
-#include <fcntl.h>
-#include <math.h>
-#include <signal.h>
-#include "theora/theora.h"
-
-const char *optstring = "o:rf";
-struct option options [] = {
-  {"output",required_argument,NULL,'o'},
-  {"raw",no_argument, NULL,'r'}, /*Disable YUV4MPEG2 headers:*/
-  {"fps-only",no_argument, NULL, 'f'}, /* Only interested in fps of decode loop */
-  {NULL,0,NULL,0}
-};
-
-/* Helper; just grab some more compressed bitstream and sync it for
-   page extraction */
-int buffer_data(FILE *in,ogg_sync_state *oy){
-  char *buffer=ogg_sync_buffer(oy,4096);
-  int bytes=fread(buffer,1,4096,in);
-  ogg_sync_wrote(oy,bytes);
-  return(bytes);
-}
-
-/* never forget that globals are a one-way ticket to Hell */
-/* Ogg and codec state for demux/decode */
-ogg_sync_state    oy;
-ogg_page          og;
-ogg_stream_state  vo;
-ogg_stream_state  to;
-theora_info       ti;
-theora_comment    tc;
-theora_state      td;
-
-int              theora_p=0;
-int              stateflag=0;
-
-/* single frame video buffering */
-int          videobuf_ready=0;
-ogg_int64_t  videobuf_granulepos=-1;
-double       videobuf_time=0;
-int          raw=0;
-
-FILE* outfile = NULL;
-
-int got_sigint=0;
-static void sigint_handler (int signal) {
-  got_sigint = 1;
-}
-
-/* this is a nop in the current implementation. we could
-   open a file here or something if so moved. */
-static void open_video(void){
-  return;
-}
-
-/* write out the planar YUV frame, uncropped */
-static void video_write(void){
-  int i;
-
-  yuv_buffer yuv;
-  theora_decode_YUVout(&td,&yuv);
-
-  if(outfile){
-    if(!raw)
-      fprintf(outfile, "FRAME\n");
-    for(i=0;i<yuv.y_height;i++)
-      fwrite(yuv.y+yuv.y_stride*i, 1, yuv.y_width, outfile);
-    for(i=0;i<yuv.uv_height;i++)
-      fwrite(yuv.u+yuv.uv_stride*i, 1, yuv.uv_width, outfile);
-    for(i=0;i<yuv.uv_height;i++)
-      fwrite(yuv.v+yuv.uv_stride*i, 1, yuv.uv_width, outfile);
-  }
-}
-
-/* dump the theora comment header */
-static int dump_comments(theora_comment *tc){
-  int i, len;
-  char *value;
-  FILE *out=stderr;
-
-  fprintf(out,"Encoded by %s\n",tc->vendor);
-  if(tc->comments){
-    fprintf(out, "theora comment header:\n");
-    for(i=0;i<tc->comments;i++){
-      if(tc->user_comments[i]){
-        len=tc->comment_lengths[i];
-        value=malloc(len+1);
-        memcpy(value,tc->user_comments[i],len);
-        value[len]='\0';
-        fprintf(out, "\t%s\n", value);
-        free(value);
-      }
-    }
-  }
-  return(0);
-}
-
-
-
-/* helper: push a page into the appropriate steam */
-/* this can be done blindly; a stream won't accept a page
-                that doesn't belong to it */
-static int queue_page(ogg_page *page){
-  if(theora_p)ogg_stream_pagein(&to,page);
-  return 0;
-}
-
-static void usage(void){
-  fprintf(stderr,
-          "Usage: dumpvid <file.ogv> > outfile\n"
-          "input is read from stdin if no file is passed on the command line\n"
-          "\n"
-  );
-}
-
-int main(int argc,char *const *argv){
-
-  ogg_packet op;
-
-  int long_option_index;
-  int c;
-
-  struct timeb start;
-  struct timeb after;
-  struct timeb last;
-  int fps_only=0;
-  int frames = 0;
-
-  FILE *infile = stdin;
-  outfile = stdout;
-
-#ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */
-  /* Beware the evil ifdef. We avoid these where we can, but this one we
-     cannot. Don't add any more, you'll probably go to hell if you do. */
-  _setmode( _fileno( stdin ), _O_BINARY );
-  _setmode( _fileno( stdout ), _O_BINARY );
-#endif
-
-  /* Process option arguments. */
-  while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
-    switch(c){
-    case 'o':
-      if(strcmp(optarg,"-")!=0){
-        outfile=fopen(optarg,"wb");
-        if(outfile==NULL){
-          fprintf(stderr,"Unable to open output file '%s'\n", optarg);
-          exit(1);
-        }
-      }else{
-        outfile=stdout;
-      }
-      break;
-
-    case 'r':
-      raw=1;
-      break;
-
-    case 'f':
-      fps_only = 1;
-      outfile = NULL;
-      break;
-
-    default:
-      usage();
-    }
-  }
-  if(optind<argc){
-    infile=fopen(argv[optind],"rb");
-    if(infile==NULL){
-      fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]);
-      exit(1);
-    }
-    if(++optind<argc){
-      usage();
-      exit(1);
-    }
-  }
-
-  /*
-     Ok, Ogg parsing. The idea here is we have a bitstream
-     that is made up of Ogg pages. The libogg sync layer will
-     find them for us. There may be pages from several logical
-     streams interleaved; we find the first theora stream and
-     ignore any others.
-
-     Then we pass the pages for our stream to the libogg stream
-     layer which assembles our original set of packets out of
-     them. It's the packets that libtheora actually knows how
-     to handle.
-  */
-
-  /* start up Ogg stream synchronization layer */
-  ogg_sync_init(&oy);
-
-  /* init supporting Theora structures needed in header parsing */
-  theora_comment_init(&tc);
-  theora_info_init(&ti);
-
-  /* Ogg file open; parse the headers */
-
-  /* Theora (like Vorbis) depends on some initial header packets
-     for decoder setup and initialization. We retrieve these first
-     before entering the main decode loop. */
-
-  /* Only interested in Theora streams */
-  while(!stateflag){
-    int ret=buffer_data(infile,&oy);
-    if(ret==0)break;
-    while(ogg_sync_pageout(&oy,&og)>0){
-      ogg_stream_state test;
-
-      /* is this a mandated initial header? If not, stop parsing */
-      if(!ogg_page_bos(&og)){
-        /* don't leak the page; get it into the appropriate stream */
-        queue_page(&og);
-        stateflag=1;
-        break;
-      }
-
-      ogg_stream_init(&test,ogg_page_serialno(&og));
-      ogg_stream_pagein(&test,&og);
-      ogg_stream_packetout(&test,&op);
-
-      /* identify the codec: try theora */
-      if(!theora_p && theora_decode_header(&ti,&tc,&op)>=0){
-        /* it is theora -- save this stream state */
-        memcpy(&to,&test,sizeof(test));
-        theora_p=1;
-      }else{
-        /* whatever it is, we don't care about it */
-        ogg_stream_clear(&test);
-      }
-    }
-    /* fall through to non-bos page parsing */
-  }
-
-  /* we're expecting more header packets. */
-  while(theora_p && theora_p<3){
-    int ret;
-
-    /* look for further theora headers */
-    while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){
-      if(ret<0){
-        fprintf(stderr,"Error parsing Theora stream headers; "
-         "corrupt stream?\n");
-        exit(1);
-      }
-      if(theora_decode_header(&ti,&tc,&op)){
-        fprintf(stderr,"Error parsing Theora stream headers; "
-         "corrupt stream?\n");
-        exit(1);
-      }
-      theora_p++;
-      if(theora_p==3)break;
-    }
-
-    /* The header pages/packets will arrive before anything else we
-       care about, or the stream is not obeying spec */
-
-    if(ogg_sync_pageout(&oy,&og)>0){
-      queue_page(&og); /* demux into the appropriate stream */
-    }else{
-      int ret=buffer_data(infile,&oy); /* someone needs more data */
-      if(ret==0){
-        fprintf(stderr,"End of file while searching for codec headers.\n");
-        exit(1);
-      }
-    }
-  }
-  dump_comments(&tc);
-
-  /* and now we have it all.  initialize decoders */
-  if(theora_p){
-    theora_decode_init(&td,&ti);
-    fprintf(stderr,"Ogg logical stream %lx is Theora %dx%d %.02f fps video\n"
-     "Encoded frame content is %dx%d with %dx%d offset\n",
-     to.serialno,ti.width,ti.height,
-     (double)ti.fps_numerator/ti.fps_denominator,
-     ti.frame_width,ti.frame_height,ti.offset_x,ti.offset_y);
-  }else{
-    /* tear down the partial theora setup */
-    theora_info_clear(&ti);
-    theora_comment_clear(&tc);
-  }
-
-  /* open video */
-  if(theora_p)open_video();
-
-  if(!raw && outfile){
-    fprintf(outfile,"YUV4MPEG2 W%d H%d F%d:%d I%c A%d:%d\n",
-     ti.width,ti.height,ti.fps_numerator,ti.fps_denominator,'p',
-     ti.aspect_numerator,ti.aspect_denominator);
-  }
-
-  /* install signal handler */
-  signal (SIGINT, sigint_handler);
-
-  /* Finally the main decode loop.
-
-     It's one Theora packet per frame, so this is pretty
-     straightforward if we're not trying to maintain sync
-     with other multiplexed streams.
-
-     the videobuf_ready flag is used to maintain the input
-     buffer in the libogg stream state. If there's no output
-     frame available at the end of the decode step, we must
-     need more input data. We could simplify this by just
-     using the return code on ogg_page_packetout(), but the
-     flag system extends easily to the case were you care
-     about more than one multiplexed stream (like with audio
-     playback). In that case, just maintain a flag for each
-     decoder you care about, and pull data when any one of
-     them stalls.
-
-     videobuf_time holds the presentation time of the currently
-     buffered video frame. We ignore this value.
-  */
-
-  stateflag=0; /* playback has not begun */
-  /* queue any remaining pages from data we buffered but that did not
-      contain headers */
-  while(ogg_sync_pageout(&oy,&og)>0){
-    queue_page(&og);
-  }
-
-  if(fps_only){
-    ftime(&start);
-    ftime(&last);
-  }
-
-  while(!got_sigint){
-
-    while(theora_p && !videobuf_ready){
-      /* theora is one in, one out... */
-      if(ogg_stream_packetout(&to,&op)>0){
-
-        theora_decode_packetin(&td,&op);
-        videobuf_granulepos=td.granulepos;
-        videobuf_time=theora_granule_time(&td,videobuf_granulepos);
-        videobuf_ready=1;
-        frames++;
-        if(fps_only)
-          ftime(&after);
-
-      }else
-        break;
-    }
-
-    if(fps_only && (videobuf_ready || fps_only==2)){
-      long ms =
-        after.time*1000.+after.millitm-
-        (last.time*1000.+last.millitm);
-
-      if(ms>500 || fps_only==1 ||
-         (feof(infile) && !videobuf_ready)){
-        float file_fps = (float)ti.fps_numerator/ti.fps_denominator;
-        fps_only=2;
-
-        ms = after.time*1000.+after.millitm-
-          (start.time*1000.+start.millitm);
-
-        fprintf(stderr,"\rframe:%d rate:%.2fx           ",
-                frames,
-                frames*1000./(ms*file_fps));
-        memcpy(&last,&after,sizeof(last));
-      }
-    }
-
-    if(!videobuf_ready && feof(infile))break;
-
-    if(!videobuf_ready){
-      /* no data yet for somebody.  Grab another page */
-      buffer_data(infile,&oy);
-      while(ogg_sync_pageout(&oy,&og)>0){
-        queue_page(&og);
-      }
-    }
-    /* dumpvideo frame, and get new one */
-    else if(outfile)video_write();
-
-    videobuf_ready=0;
-  }
-
-  /* end of decoder loop -- close everything */
-
-  if(theora_p){
-    ogg_stream_clear(&to);
-    theora_clear(&td);
-    theora_comment_clear(&tc);
-    theora_info_clear(&ti);
-  }
-  ogg_sync_clear(&oy);
-
-  if(infile && infile!=stdin)fclose(infile);
-  if(outfile && outfile!=stdout)fclose(outfile);
-
-  fprintf(stderr, "\n\n%d frames\n", frames);
-  fprintf(stderr, "\nDone.\n");
-
-  return(0);
-
-}

Copied: branches/theora-thusnelda/examples/dump_video.c (from rev 15592, trunk/theora-exp/examples/dump_video.c)
===================================================================
--- branches/theora-thusnelda/examples/dump_video.c	                        (rev 0)
+++ branches/theora-thusnelda/examples/dump_video.c	2009-07-11 20:21:36 UTC (rev 16261)
@@ -0,0 +1,496 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2006                *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: example dumpvid application; dumps  Theora streams
+  last mod: $Id: dump_video.c,v 1.2 2004/03/24 19:12:42 derf Exp $
+
+ ********************************************************************/
+
+/* By Mauricio Piacentini (mauricio at xiph.org) */
+/*  simply dump decoded YUV data, for verification of theora bitstream */
+
+#if !defined(_REENTRANT)
+#define _REENTRANT
+#endif
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+#if !defined(_LARGEFILE_SOURCE)
+#define _LARGEFILE_SOURCE
+#endif
+#if !defined(_LARGEFILE64_SOURCE)
+#define _LARGEFILE64_SOURCE
+#endif
+#if !defined(_FILE_OFFSET_BITS)
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/timeb.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/*Yes, yes, we're going to hell.*/
+#if defined(_WIN32)
+#include <io.h>
+#endif
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <signal.h>
+#include "getopt.h"
+#include "theora/theoradec.h"
+
+const char *optstring = "o:rf";
+struct option options [] = {
+  {"output",required_argument,NULL,'o'},
+  {"raw",no_argument, NULL,'r'}, /*Disable YUV4MPEG2 headers:*/
+  {"fps-only",no_argument, NULL, 'f'}, /* Only interested in fps of decode loop */
+  {NULL,0,NULL,0}
+};
+
+/* Helper; just grab some more compressed bitstream and sync it for
+   page extraction */
+int buffer_data(FILE *in,ogg_sync_state *oy){
+  char *buffer=ogg_sync_buffer(oy,4096);
+  int bytes=fread(buffer,1,4096,in);
+  ogg_sync_wrote(oy,bytes);
+  return(bytes);
+}
+
+/* never forget that globals are a one-way ticket to Hell */
+/* Ogg and codec state for demux/decode */
+ogg_sync_state    oy;
+ogg_page          og;
+ogg_stream_state  vo;
+ogg_stream_state  to;
+th_info           ti;
+th_comment        tc;
+th_setup_info    *ts;
+th_dec_ctx       *td;
+
+int              theora_p=0;
+int              theora_processing_headers;
+int              stateflag=0;
+
+/* single frame video buffering */
+int          videobuf_ready=0;
+ogg_int64_t  videobuf_granulepos=-1;
+double       videobuf_time=0;
+int          raw=0;
+
+FILE* outfile = NULL;
+
+int got_sigint=0;
+static void sigint_handler (int signal) {
+  got_sigint = 1;
+}
+
+static th_ycbcr_buffer ycbcr;
+
+static void stripe_decoded(th_ycbcr_buffer _dst,th_ycbcr_buffer _src,
+ int _fragy0,int _fragy_end){
+  int pli;
+  for(pli=0;pli<3;pli++){
+    int yshift;
+    int y_end;
+    int y;
+    yshift=pli!=0&&!(ti.pixel_fmt&2);
+    y_end=_fragy_end<<3-yshift;
+    /*An implemention intending to display this data would need to check the
+       crop rectangle before proceeding.*/
+    for(y=_fragy0<<3-yshift;y<y_end;y++){
+      memcpy(_dst[pli].data+y*_dst[pli].stride,
+       _src[pli].data+y*_src[pli].stride,_src[pli].width);
+    }
+  }
+}
+
+static void open_video(void){
+  th_stripe_callback cb;
+  int                pli;
+  /*Here we allocate a buffer so we can use the striped decode feature.
+    There's no real reason to do this in this application, because we want to
+     write to the file top-down, but the frame gets decoded bottom up, so we
+     have to buffer it all anyway.
+    But this illustrates how the API works.*/
+  for(pli=0;pli<3;pli++){
+    int xshift;
+    int yshift;
+    xshift=pli!=0&&!(ti.pixel_fmt&1);
+    yshift=pli!=0&&!(ti.pixel_fmt&2);
+    ycbcr[pli].data=(unsigned char *)malloc(
+     (ti.frame_width>>xshift)*(ti.frame_height>>yshift)*sizeof(char));
+    ycbcr[pli].stride=ti.frame_width>>xshift;
+    ycbcr[pli].width=ti.frame_width>>xshift;
+    ycbcr[pli].height=ti.frame_height>>yshift;
+  }
+  /*Similarly, since ycbcr is a global, there's no real reason to pass it as
+     the context.
+    In a more object-oriented decoder, we could pass the "this" pointer
+     instead (though in C++, platform-dependent calling convention differences
+     prevent us from using a real member function pointer).*/
+  cb.ctx=ycbcr;
+  cb.stripe_decoded=(th_stripe_decoded_func)stripe_decoded;
+  th_decode_ctl(td,TH_DECCTL_SET_STRIPE_CB,&cb,sizeof(cb));
+}
+
+/*Write out the planar YUV frame, uncropped.*/
+static void video_write(void){
+  int pli;
+  int i;
+  /*Uncomment the following to do normal, non-striped decoding.
+  th_ycbcr_buffer ycbcr;
+  th_decode_ycbcr_out(td,ycbcr);*/
+  if(outfile){
+    if(!raw)fprintf(outfile, "FRAME\n");
+    for(pli=0;pli<3;pli++){
+      for(i=0;i<ycbcr[pli].height;i++){
+        fwrite(ycbcr[pli].data+ycbcr[pli].stride*i, 1,
+         ycbcr[pli].width, outfile);
+      }
+    }
+  }
+}
+
+/* dump the theora comment header */
+static int dump_comments(th_comment *_tc){
+  int   i;
+  int   len;
+  FILE *out;
+  out=stderr;
+  fprintf(out,"Encoded by %s\n",_tc->vendor);
+  if(_tc->comments){
+    fprintf(out,"theora comment header:\n");
+    for(i=0;i<_tc->comments;i++){
+      if(_tc->user_comments[i]){
+        len=_tc->comment_lengths[i]<INT_MAX?_tc->comment_lengths[i]:INT_MAX;
+        fprintf(out,"\t%.*s\n",len,_tc->user_comments[i]);
+      }
+    }
+  }
+  return 0;
+}
+
+/* helper: push a page into the appropriate steam */
+/* this can be done blindly; a stream won't accept a page
+                that doesn't belong to it */
+static int queue_page(ogg_page *page){
+  if(theora_p)ogg_stream_pagein(&to,page);
+  return 0;
+}
+
+static void usage(void){
+  fprintf(stderr,
+          "Usage: dumpvid <file.ogv> > outfile\n"
+          "input is read from stdin if no file is passed on the command line\n"
+          "\n"
+  );
+}
+
+int main(int argc,char *argv[]){
+
+  ogg_packet op;
+
+  int long_option_index;
+  int c;
+
+  struct timeb start;
+  struct timeb after;
+  struct timeb last;
+  int fps_only=0;
+  int frames = 0;
+
+  FILE *infile = stdin;
+  outfile = stdout;
+
+#ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */
+  /* Beware the evil ifdef. We avoid these where we can, but this one we
+     cannot. Don't add any more, you'll probably go to hell if you do. */
+  _setmode( _fileno( stdin ), _O_BINARY );
+  _setmode( _fileno( stdout ), _O_BINARY );
+#endif
+
+  /* Process option arguments. */
+  while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+    switch(c){
+    case 'o':
+      if(strcmp(optarg,"-")!=0){
+        outfile=fopen(optarg,"wb");
+        if(outfile==NULL){
+          fprintf(stderr,"Unable to open output file '%s'\n", optarg);
+          exit(1);
+        }
+      }else{
+        outfile=stdout;
+      }
+      break;
+
+    case 'r':
+      raw=1;
+      break;
+
+    case 'f':
+      fps_only = 1;
+      outfile = NULL;
+      break;
+
+    default:
+      usage();
+    }
+  }
+  if(optind<argc){
+    infile=fopen(argv[optind],"rb");
+    if(infile==NULL){
+      fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]);
+      exit(1);
+    }
+    if(++optind<argc){
+      usage();
+      exit(1);
+    }
+  }
+  /*Ok, Ogg parsing.
+    The idea here is we have a bitstream that is made up of Ogg pages.
+    The libogg sync layer will find them for us.
+    There may be pages from several logical streams interleaved; we find the
+     first theora stream and ignore any others.
+    Then we pass the pages for our stream to the libogg stream layer which
+     assembles our original set of packets out of them.
+    It's the packets that libtheora actually knows how to handle.*/
+
+  /* start up Ogg stream synchronization layer */
+  ogg_sync_init(&oy);
+
+  /* init supporting Theora structures needed in header parsing */
+  th_comment_init(&tc);
+  th_info_init(&ti);
+
+  /*Ogg file open; parse the headers.
+    Theora (like Vorbis) depends on some initial header packets for decoder
+     setup and initialization.
+    We retrieve these first before entering the main decode loop.*/
+
+  /* Only interested in Theora streams */
+  while(!stateflag){
+    int ret=buffer_data(infile,&oy);
+    if(ret==0)break;
+    while(ogg_sync_pageout(&oy,&og)>0){
+      ogg_stream_state test;
+
+      /* is this a mandated initial header? If not, stop parsing */
+      if(!ogg_page_bos(&og)){
+        /* don't leak the page; get it into the appropriate stream */
+        queue_page(&og);
+        stateflag=1;
+        break;
+      }
+
+      ogg_stream_init(&test,ogg_page_serialno(&og));
+      ogg_stream_pagein(&test,&og);
+      ogg_stream_packetpeek(&test,&op);
+
+      /* identify the codec: try theora */
+      if(!theora_p && (theora_processing_headers=
+       th_decode_headerin(&ti,&tc,&ts,&op))>=0){
+        /* it is theora -- save this stream state */
+        memcpy(&to,&test,sizeof(test));
+        theora_p=1;
+        /*Advance past the successfully processed header.*/
+        if(theora_processing_headers)ogg_stream_packetout(&to,NULL);
+      }else{
+        /* whatever it is, we don't care about it */
+        ogg_stream_clear(&test);
+      }
+    }
+    /* fall through to non-bos page parsing */
+  }
+
+  /* we're expecting more header packets. */
+  while(theora_p && theora_processing_headers){
+    int ret;
+
+    /* look for further theora headers */
+    while(theora_processing_headers&&(ret=ogg_stream_packetpeek(&to,&op))){
+      if(ret<0)continue;
+      theora_processing_headers=th_decode_headerin(&ti,&tc,&ts,&op);
+      if(theora_processing_headers<0){
+        fprintf(stderr,"Error parsing Theora stream headers; "
+         "corrupt stream?\n");
+        exit(1);
+      }
+      else if(theora_processing_headers>0){
+        /*Advance past the successfully processed header.*/
+        ogg_stream_packetout(&to,NULL);
+      }
+      theora_p++;
+    }
+
+    /*Stop now so we don't fail if there aren't enough pages in a short
+       stream.*/
+    if(!(theora_p && theora_processing_headers))break;
+
+    /* The header pages/packets will arrive before anything else we
+       care about, or the stream is not obeying spec */
+
+    if(ogg_sync_pageout(&oy,&og)>0){
+      queue_page(&og); /* demux into the appropriate stream */
+    }else{
+      int ret=buffer_data(infile,&oy); /* someone needs more data */
+      if(ret==0){
+        fprintf(stderr,"End of file while searching for codec headers.\n");
+        exit(1);
+      }
+    }
+  }
+
+  /* and now we have it all.  initialize decoders */
+  if(theora_p){
+    dump_comments(&tc);
+    td=th_decode_alloc(&ti,ts);
+    fprintf(stderr,"Ogg logical stream %lx is Theora %dx%d %.02f fps video\n"
+     "Encoded frame content is %dx%d with %dx%d offset\n",
+     to.serialno,ti.frame_width,ti.frame_height,
+     (double)ti.fps_numerator/ti.fps_denominator,
+     ti.pic_width,ti.pic_height,ti.pic_x,ti.pic_y);
+  }else{
+    /* tear down the partial theora setup */
+    th_info_clear(&ti);
+    th_comment_clear(&tc);
+  }
+  /*Either way, we're done with the codec setup data.*/
+  th_setup_free(ts);
+
+  /* open video */
+  if(theora_p)open_video();
+
+  if(!raw && outfile){
+    static const char *CHROMA_TYPES[4]={"420jpeg",NULL,"422","444"};
+    if(ti.pixel_fmt>=4||ti.pixel_fmt==TH_PF_RSVD){
+      fprintf(stderr,"Unknown pixel format: %i\n",ti.pixel_fmt);
+      exit(1);
+    }
+    fprintf(outfile,"YUV4MPEG2 C%s W%d H%d F%d:%d I%c A%d:%d\n",
+     CHROMA_TYPES[ti.pixel_fmt],ti.frame_width,ti.frame_height,
+     ti.fps_numerator,ti.fps_denominator,'p',
+     ti.aspect_numerator,ti.aspect_denominator);
+  }
+
+  /* install signal handler */
+  signal (SIGINT, sigint_handler);
+
+  /*Finally the main decode loop.
+
+    It's one Theora packet per frame, so this is pretty straightforward if
+     we're not trying to maintain sync with other multiplexed streams.
+
+    The videobuf_ready flag is used to maintain the input buffer in the libogg
+     stream state.
+    If there's no output frame available at the end of the decode step, we must
+     need more input data.
+    We could simplify this by just using the return code on
+     ogg_page_packetout(), but the flag system extends easily to the case where
+     you care about more than one multiplexed stream (like with audio
+     playback).
+    In that case, just maintain a flag for each decoder you care about, and
+     pull data when any one of them stalls.
+
+    videobuf_time holds the presentation time of the currently buffered video
+     frame.
+    We ignore this value.*/
+
+  stateflag=0; /* playback has not begun */
+  /* queue any remaining pages from data we buffered but that did not
+      contain headers */
+  while(ogg_sync_pageout(&oy,&og)>0){
+    queue_page(&og);
+  }
+
+  if(fps_only){
+    ftime(&start);
+    ftime(&last);
+  }
+
+  while(!got_sigint){
+
+    while(theora_p && !videobuf_ready){
+      /* theora is one in, one out... */
+      if(ogg_stream_packetout(&to,&op)>0){
+
+        if(th_decode_packetin(td,&op,&videobuf_granulepos)>=0){
+          videobuf_time=th_granule_time(td,videobuf_granulepos);
+          videobuf_ready=1;
+          frames++;
+          if(fps_only)
+            ftime(&after);
+        }
+
+      }else
+        break;
+    }
+
+    if(fps_only && (videobuf_ready || fps_only==2)){
+      long ms =
+        after.time*1000.+after.millitm-
+        (last.time*1000.+last.millitm);
+
+      if(ms>500 || fps_only==1 ||
+         (feof(infile) && !videobuf_ready)){
+        float file_fps = (float)ti.fps_numerator/ti.fps_denominator;
+        fps_only=2;
+
+        ms = after.time*1000.+after.millitm-
+          (start.time*1000.+start.millitm);
+
+        fprintf(stderr,"\rframe:%d rate:%.2fx           ",
+                frames,
+                frames*1000./(ms*file_fps));
+        memcpy(&last,&after,sizeof(last));
+      }
+    }
+
+    if(!videobuf_ready && feof(infile))break;
+
+    if(!videobuf_ready){
+      /* no data yet for somebody.  Grab another page */
+      buffer_data(infile,&oy);
+      while(ogg_sync_pageout(&oy,&og)>0){
+        queue_page(&og);
+      }
+    }
+    /* dumpvideo frame, and get new one */
+    else if(outfile)video_write();
+
+    videobuf_ready=0;
+  }
+
+  /* end of decoder loop -- close everything */
+
+  if(theora_p){
+    ogg_stream_clear(&to);
+    th_decode_free(td);
+    th_comment_clear(&tc);
+    th_info_clear(&ti);
+  }
+  ogg_sync_clear(&oy);
+
+  if(infile && infile!=stdin)fclose(infile);
+  if(outfile && outfile!=stdout)fclose(outfile);
+
+  fprintf(stderr, "\n\n%d frames\n", frames);
+  fprintf(stderr, "\nDone.\n");
+
+  return(0);
+
+}



More information about the commits mailing list