Blame examples/dump_video.c

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