forgot these two files
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 18 Feb 2009 10:16:51 +0000 (10:16 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 18 Feb 2009 10:16:51 +0000 (10:16 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8723 d7cf8633-e32d-0410-b094-e92efae38249

cap_ogg.c [new file with mode: 0644]
cap_ogg.h [new file with mode: 0644]

diff --git a/cap_ogg.c b/cap_ogg.c
new file mode 100644 (file)
index 0000000..410298d
--- /dev/null
+++ b/cap_ogg.c
@@ -0,0 +1,902 @@
+#include <sys/types.h>
+
+#include "quakedef.h"
+#include "client.h"
+#include "cap_ogg.h"
+
+// ogg.h stuff
+typedef int16_t ogg_int16_t;
+typedef u_int16_t ogg_uint16_t;
+typedef int32_t ogg_int32_t;
+typedef u_int32_t ogg_uint32_t;
+typedef int64_t ogg_int64_t;
+
+typedef struct {
+  long endbyte;
+  int  endbit;
+
+  unsigned char *buffer;
+  unsigned char *ptr;
+  long storage;
+} oggpack_buffer;
+
+/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/
+
+typedef struct {
+  unsigned char *header;
+  long header_len;
+  unsigned char *body;
+  long body_len;
+} ogg_page;
+
+/* ogg_stream_state contains the current encode/decode state of a logical
+   Ogg bitstream **********************************************************/
+
+typedef struct {
+  unsigned char   *body_data;    /* bytes from packet bodies */
+  long    body_storage;          /* storage elements allocated */
+  long    body_fill;             /* elements stored; fill mark */
+  long    body_returned;         /* elements of fill returned */
+
+
+  int     *lacing_vals;      /* The values that will go to the segment table */
+  ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
+                               this way, but it is simple coupled to the
+                               lacing fifo */
+  long    lacing_storage;
+  long    lacing_fill;
+  long    lacing_packet;
+  long    lacing_returned;
+
+  unsigned char    header[282];      /* working space for header encode */
+  int              header_fill;
+
+  int     e_o_s;          /* set when we have buffered the last packet in the
+                             logical bitstream */
+  int     b_o_s;          /* set after we've written the initial page
+                             of a logical bitstream */
+  long    serialno;
+  long    pageno;
+  ogg_int64_t  packetno;      /* sequence number for decode; the framing
+                             knows where there's a hole in the data,
+                             but we need coupling so that the codec
+                             (which is in a seperate abstraction
+                             layer) also knows about the gap */
+  ogg_int64_t   granulepos;
+
+} ogg_stream_state;
+
+/* ogg_packet is used to encapsulate the data and metadata belonging
+   to a single raw Ogg/Vorbis packet *************************************/
+
+typedef struct {
+  unsigned char *packet;
+  long  bytes;
+  long  b_o_s;
+  long  e_o_s;
+
+  ogg_int64_t  granulepos;
+  
+  ogg_int64_t  packetno;     /* sequence number for decode; the framing
+                               knows where there's a hole in the data,
+                               but we need coupling so that the codec
+                               (which is in a seperate abstraction
+                               layer) also knows about the gap */
+} ogg_packet;
+
+typedef struct {
+  unsigned char *data;
+  int storage;
+  int fill;
+  int returned;
+
+  int unsynced;
+  int headerbytes;
+  int bodybytes;
+} ogg_sync_state;
+
+/* Ogg BITSTREAM PRIMITIVES: encoding **************************/
+
+static int      (*qogg_stream_packetin) (ogg_stream_state *os, ogg_packet *op);
+static int      (*qogg_stream_pageout) (ogg_stream_state *os, ogg_page *og);
+static int      (*qogg_stream_flush) (ogg_stream_state *os, ogg_page *og);
+
+/* Ogg BITSTREAM PRIMITIVES: general ***************************/
+
+static int      (*qogg_stream_init) (ogg_stream_state *os,int serialno);
+static int      (*qogg_stream_clear) (ogg_stream_state *os);
+
+// end of ogg.h stuff
+
+// vorbis/codec.h stuff
+typedef struct vorbis_info{
+  int version;
+  int channels;
+  long rate;
+
+  /* The below bitrate declarations are *hints*.
+     Combinations of the three values carry the following implications:
+
+     all three set to the same value:
+       implies a fixed rate bitstream
+     only nominal set:
+       implies a VBR stream that averages the nominal bitrate.  No hard
+       upper/lower limit
+     upper and or lower set:
+       implies a VBR bitstream that obeys the bitrate limits. nominal
+       may also be set to give a nominal rate.
+     none set:
+       the coder does not care to speculate.
+  */
+
+  long bitrate_upper;
+  long bitrate_nominal;
+  long bitrate_lower;
+  long bitrate_window;
+
+  void *codec_setup;
+} vorbis_info;
+
+/* vorbis_dsp_state buffers the current vorbis audio
+   analysis/synthesis state.  The DSP state belongs to a specific
+   logical bitstream ****************************************************/
+typedef struct vorbis_dsp_state{
+  int analysisp;
+  vorbis_info *vi;
+
+  float **pcm;
+  float **pcmret;
+  int      pcm_storage;
+  int      pcm_current;
+  int      pcm_returned;
+
+  int  preextrapolate;
+  int  eofflag;
+
+  long lW;
+  long W;
+  long nW;
+  long centerW;
+
+  ogg_int64_t granulepos;
+  ogg_int64_t sequence;
+
+  ogg_int64_t glue_bits;
+  ogg_int64_t time_bits;
+  ogg_int64_t floor_bits;
+  ogg_int64_t res_bits;
+
+  void       *backend_state;
+} vorbis_dsp_state;
+
+typedef struct vorbis_block{
+  /* necessary stream state for linking to the framing abstraction */
+  float  **pcm;       /* this is a pointer into local storage */
+  oggpack_buffer opb;
+
+  long  lW;
+  long  W;
+  long  nW;
+  int   pcmend;
+  int   mode;
+
+  int         eofflag;
+  ogg_int64_t granulepos;
+  ogg_int64_t sequence;
+  vorbis_dsp_state *vd; /* For read-only access of configuration */
+
+  /* local storage to avoid remallocing; it's up to the mapping to
+     structure it */
+  void               *localstore;
+  long                localtop;
+  long                localalloc;
+  long                totaluse;
+  struct alloc_chain *reap;
+
+  /* bitmetrics for the frame */
+  long glue_bits;
+  long time_bits;
+  long floor_bits;
+  long res_bits;
+
+  void *internal;
+
+} vorbis_block;
+
+/* vorbis_block is a single block of data to be processed as part of
+the analysis/synthesis stream; it belongs to a specific logical
+bitstream, but is independant from other vorbis_blocks belonging to
+that logical bitstream. *************************************************/
+
+struct alloc_chain{
+  void *ptr;
+  struct alloc_chain *next;
+};
+
+/* vorbis_info contains all the setup information specific to the
+   specific compression/decompression mode in progress (eg,
+   psychoacoustic settings, channel setup, options, codebook
+   etc). vorbis_info and substructures are in backends.h.
+*********************************************************************/
+
+/* the comments are not part of vorbis_info so that vorbis_info can be
+   static storage */
+typedef struct vorbis_comment{
+  /* unlimited user comment fields.  libvorbis writes 'libvorbis'
+     whatever vendor is set to in encode */
+  char **user_comments;
+  int   *comment_lengths;
+  int    comments;
+  char  *vendor;
+
+} vorbis_comment;
+
+
+/* libvorbis encodes in two abstraction layers; first we perform DSP
+   and produce a packet (see docs/analysis.txt).  The packet is then
+   coded into a framed OggSquish bitstream by the second layer (see
+   docs/framing.txt).  Decode is the reverse process; we sync/frame
+   the bitstream and extract individual packets, then decode the
+   packet back into PCM audio.
+
+   The extra framing/packetizing is used in streaming formats, such as
+   files.  Over the net (such as with UDP), the framing and
+   packetization aren't necessary as they're provided by the transport
+   and the streaming layer is not used */
+
+/* Vorbis PRIMITIVES: general ***************************************/
+
+static void     (*qvorbis_info_init) (vorbis_info *vi);
+static void     (*qvorbis_info_clear) (vorbis_info *vi);
+static void     (*qvorbis_comment_init) (vorbis_comment *vc);
+static void     (*qvorbis_comment_clear) (vorbis_comment *vc);
+
+static int      (*qvorbis_block_init) (vorbis_dsp_state *v, vorbis_block *vb);
+static int      (*qvorbis_block_clear) (vorbis_block *vb);
+static void     (*qvorbis_dsp_clear) (vorbis_dsp_state *v);
+
+/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/
+
+static int      (*qvorbis_analysis_init) (vorbis_dsp_state *v,vorbis_info *vi);
+static int      (*qvorbis_commentheader_out) (vorbis_comment *vc, ogg_packet *op);
+static int      (*qvorbis_analysis_headerout) (vorbis_dsp_state *v,
+                                         vorbis_comment *vc,
+                                         ogg_packet *op,
+                                         ogg_packet *op_comm,
+                                         ogg_packet *op_code);
+static float ** (*qvorbis_analysis_buffer) (vorbis_dsp_state *v,int vals);
+static int      (*qvorbis_analysis_wrote) (vorbis_dsp_state *v,int vals);
+static int      (*qvorbis_analysis_blockout) (vorbis_dsp_state *v,vorbis_block *vb);
+static int      (*qvorbis_analysis) (vorbis_block *vb,ogg_packet *op);
+
+static int      (*qvorbis_bitrate_addblock) (vorbis_block *vb);
+static int      (*qvorbis_bitrate_flushpacket) (vorbis_dsp_state *vd,
+                                          ogg_packet *op);
+
+// end of vorbis/codec.h stuff
+
+// vorbisenc.h stuff
+static int (*qvorbis_encode_init_vbr) (vorbis_info *vi,
+                                 long channels,
+                                 long rate,
+
+                                 float base_quality /* quality level from 0. (lo) to 1. (hi) */
+                                 );
+// end of vorbisenc.h stuff
+
+// theora.h stuff
+typedef struct {
+    int   y_width;      /**< Width of the Y' luminance plane */
+    int   y_height;     /**< Height of the luminance plane */
+    int   y_stride;     /**< Offset in bytes between successive rows */
+
+    int   uv_width;     /**< Width of the Cb and Cr chroma planes */
+    int   uv_height;    /**< Height of the chroma planes */
+    int   uv_stride;    /**< Offset between successive chroma rows */
+    unsigned char *y;   /**< Pointer to start of luminance data */
+    unsigned char *u;   /**< Pointer to start of Cb data */
+    unsigned char *v;   /**< Pointer to start of Cr data */
+
+} yuv_buffer;
+
+/**
+ * A Colorspace.
+ */
+typedef enum {
+  OC_CS_UNSPECIFIED,    /**< The colorspace is unknown or unspecified */
+  OC_CS_ITU_REC_470M,   /**< This is the best option for 'NTSC' content */
+  OC_CS_ITU_REC_470BG,  /**< This is the best option for 'PAL' content */
+  OC_CS_NSPACES         /**< This marks the end of the defined colorspaces */
+} theora_colorspace;
+
+/**
+ * A Chroma subsampling
+ *
+ * These enumerate the available chroma subsampling options supported
+ * by the theora format. See Section 4.4 of the specification for
+ * exact definitions.
+ */
+typedef enum {
+  OC_PF_420,    /**< Chroma subsampling by 2 in each direction (4:2:0) */
+  OC_PF_RSVD,   /**< Reserved value */
+  OC_PF_422,    /**< Horizonatal chroma subsampling by 2 (4:2:2) */
+  OC_PF_444,    /**< No chroma subsampling at all (4:4:4) */
+} theora_pixelformat;
+/**
+ * Theora bitstream info.
+ * Contains the basic playback parameters for a stream,
+ * corresponding to the initial 'info' header packet.
+ * 
+ * Encoded theora frames must be a multiple of 16 in width and height.
+ * To handle other frame sizes, a crop rectangle is specified in
+ * frame_height and frame_width, offset_x and * offset_y. The offset
+ * and size should still be a multiple of 2 to avoid chroma sampling
+ * shifts. Offset values in this structure are measured from the
+ * upper left of the image.
+ *
+ * Frame rate, in frames per second, is stored as a rational
+ * fraction. Aspect ratio is also stored as a rational fraction, and
+ * refers to the aspect ratio of the frame pixels, not of the
+ * overall frame itself.
+ * 
+ * See <a href="http://svn.xiph.org/trunk/theora/examples/encoder_example.c">
+ * examples/encoder_example.c</a> for usage examples of the
+ * other paramters and good default settings for the encoder parameters.
+ */
+typedef struct {
+  ogg_uint32_t  width;      /**< encoded frame width  */
+  ogg_uint32_t  height;     /**< encoded frame height */
+  ogg_uint32_t  frame_width;    /**< display frame width  */
+  ogg_uint32_t  frame_height;   /**< display frame height */
+  ogg_uint32_t  offset_x;   /**< horizontal offset of the displayed frame */
+  ogg_uint32_t  offset_y;   /**< vertical offset of the displayed frame */
+  ogg_uint32_t  fps_numerator;      /**< frame rate numerator **/
+  ogg_uint32_t  fps_denominator;    /**< frame rate denominator **/
+  ogg_uint32_t  aspect_numerator;   /**< pixel aspect ratio numerator */
+  ogg_uint32_t  aspect_denominator; /**< pixel aspect ratio denominator */
+  theora_colorspace colorspace;     /**< colorspace */
+  int           target_bitrate;     /**< nominal bitrate in bits per second */
+  int           quality;  /**< Nominal quality setting, 0-63 */
+  int           quick_p;  /**< Quick encode/decode */
+
+  /* decode only */
+  unsigned char version_major;
+  unsigned char version_minor;
+  unsigned char version_subminor;
+
+  void *codec_setup;
+
+  /* encode only */
+  int           dropframes_p;
+  int           keyframe_auto_p;
+  ogg_uint32_t  keyframe_frequency;
+  ogg_uint32_t  keyframe_frequency_force;  /* also used for decode init to
+                                              get granpos shift correct */
+  ogg_uint32_t  keyframe_data_target_bitrate;
+  ogg_int32_t   keyframe_auto_threshold;
+  ogg_uint32_t  keyframe_mindistance;
+  ogg_int32_t   noise_sensitivity;
+  ogg_int32_t   sharpness;
+
+  theora_pixelformat pixelformat;   /**< chroma subsampling mode to expect */
+
+} theora_info;
+
+/** Codec internal state and context.
+ */
+typedef struct{
+  theora_info *i;
+  ogg_int64_t granulepos;
+
+  void *internal_encode;
+  void *internal_decode;
+
+} theora_state;
+
+/** 
+ * Comment header metadata.
+ *
+ * This structure holds the in-stream metadata corresponding to
+ * the 'comment' header packet.
+ *
+ * Meta data is stored as a series of (tag, value) pairs, in
+ * length-encoded string vectors. The first occurence of the 
+ * '=' character delimits the tag and value. A particular tag
+ * may occur more than once. The character set encoding for
+ * the strings is always UTF-8, but the tag names are limited
+ * to case-insensitive ASCII. See the spec for details.
+ *
+ * In filling in this structure, qtheora_decode_header() will
+ * null-terminate the user_comment strings for safety. However,
+ * the bitstream format itself treats them as 8-bit clean,
+ * and so the length array should be treated as authoritative
+ * for their length.
+ */
+typedef struct theora_comment{
+  char **user_comments;         /**< An array of comment string vectors */
+  int   *comment_lengths;       /**< An array of corresponding string vector lengths in bytes */
+  int    comments;              /**< The total number of comment string vectors */
+  char  *vendor;                /**< The vendor string identifying the encoder, null terminated */
+
+} theora_comment;
+static int (*qtheora_encode_init) (theora_state *th, theora_info *ti);
+static int (*qtheora_encode_YUVin) (theora_state *t, yuv_buffer *yuv);
+static int (*qtheora_encode_packetout) ( theora_state *t, int last_p,
+                                    ogg_packet *op);
+static int (*qtheora_encode_header) (theora_state *t, ogg_packet *op);
+static int (*qtheora_encode_comment) (theora_comment *tc, ogg_packet *op);
+static int (*qtheora_encode_tables) (theora_state *t, ogg_packet *op);
+static void (*qtheora_info_init) (theora_info *c);
+static void (*qtheora_info_clear) (theora_info *c);
+static void (*qtheora_clear) (theora_state *t);
+static void (*qtheora_comment_init) (theora_comment *tc);
+static void  (*qtheora_comment_clear) (theora_comment *tc);
+// end of theora.h stuff
+
+static dllfunction_t oggfuncs[] =
+{
+       {"ogg_stream_packetin", (void **) &qogg_stream_packetin},
+       {"ogg_stream_pageout", (void **) &qogg_stream_pageout},
+       {"ogg_stream_flush", (void **) &qogg_stream_flush},
+       {"ogg_stream_init", (void **) &qogg_stream_init},
+       {"ogg_stream_clear", (void **) &qogg_stream_clear},
+       {NULL, NULL}
+};
+
+static dllfunction_t vorbisencfuncs[] =
+{
+       {"vorbis_encode_init_vbr", (void **) &qvorbis_encode_init_vbr},
+       {NULL, NULL}
+};
+
+static dllfunction_t vorbisfuncs[] =
+{
+       {"vorbis_info_init", (void **) &qvorbis_info_init},
+       {"vorbis_info_clear", (void **) &qvorbis_info_clear},
+       {"vorbis_comment_init", (void **) &qvorbis_comment_init},
+       {"vorbis_comment_clear", (void **) &qvorbis_comment_clear},
+       {"vorbis_block_init", (void **) &qvorbis_block_init},
+       {"vorbis_block_clear", (void **) &qvorbis_block_clear},
+       {"vorbis_dsp_clear", (void **) &qvorbis_dsp_clear},
+       {"vorbis_analysis_init", (void **) &qvorbis_analysis_init},
+       {"vorbis_commentheader_out", (void **) &qvorbis_commentheader_out},
+       {"vorbis_analysis_headerout", (void **) &qvorbis_analysis_headerout},
+       {"vorbis_analysis_buffer", (void **) &qvorbis_analysis_buffer},
+       {"vorbis_analysis_wrote", (void **) &qvorbis_analysis_wrote},
+       {"vorbis_analysis_blockout", (void **) &qvorbis_analysis_blockout},
+       {"vorbis_analysis", (void **) &qvorbis_analysis},
+       {"vorbis_bitrate_addblock", (void **) &qvorbis_bitrate_addblock},
+       {"vorbis_bitrate_flushpacket", (void **) &qvorbis_bitrate_flushpacket},
+       {NULL, NULL}
+};
+
+static dllfunction_t theorafuncs[] =
+{
+       {"theora_info_init", (void **) &qtheora_info_init},
+       {"theora_info_clear", (void **) &qtheora_info_clear},
+       {"theora_comment_init", (void **) &qtheora_comment_init},
+       {"theora_comment_clear", (void **) &qtheora_comment_clear},
+       {"theora_encode_init", (void **) &qtheora_encode_init},
+       {"theora_encode_YUVin", (void **) &qtheora_encode_YUVin},
+       {"theora_encode_packetout", (void **) &qtheora_encode_packetout},
+       {"theora_encode_header", (void **) &qtheora_encode_header},
+       {"theora_encode_comment", (void **) &qtheora_encode_comment},
+       {"theora_encode_tables", (void **) &qtheora_encode_tables},
+       {"theora_clear", (void **) &qtheora_clear},
+       {NULL, NULL}
+};
+
+static dllhandle_t og_dll = NULL, vo_dll = NULL, ve_dll = NULL, th_dll = NULL;
+
+qboolean SCR_CaptureVideo_Ogg_OpenLibrary()
+{
+       const char* dllnames_og [] =
+       {
+#if defined(WIN64)
+               "libogg64.dll",
+#elif defined(WIN32)
+               "libogg.dll",
+               "ogg.dll",
+#elif defined(MACOSX)
+               "libogg.dylib",
+#else
+               "libogg.so.0",
+               "libogg.so",
+#endif
+               NULL
+       };
+       const char* dllnames_vo [] =
+       {
+#if defined(WIN64)
+               "libvorbis64.dll",
+#elif defined(WIN32)
+               "libvorbis.dll",
+               "vorbis.dll",
+#elif defined(MACOSX)
+               "libvorbis.dylib",
+#else
+               "libvorbis.so.0",
+               "libvorbis.so",
+#endif
+               NULL
+       };
+       const char* dllnames_ve [] =
+       {
+#if defined(WIN64)
+               "libvorbisenc64.dll",
+#elif defined(WIN32)
+               "libvorbisenc.dll",
+               "vorbisenc.dll",
+#elif defined(MACOSX)
+               "libvorbisenc.dylib",
+#else
+               "libvorbisenc.so.2",
+               "libvorbisenc.so",
+#endif
+               NULL
+       };
+       const char* dllnames_th [] =
+       {
+#if defined(WIN64)
+               "libtheora64.dll",
+#elif defined(WIN32)
+               "libtheora.dll",
+               "theora.dll",
+#elif defined(MACOSX)
+               "libtheora.dylib",
+#else
+               "libtheora.so.0",
+               "libtheora.so",
+#endif
+               NULL
+       };
+
+       return
+               Sys_LoadLibrary (dllnames_og, &og_dll, oggfuncs)
+               &&
+               Sys_LoadLibrary (dllnames_th, &th_dll, theorafuncs)
+               &&
+               Sys_LoadLibrary (dllnames_vo, &vo_dll, vorbisfuncs)
+               &&
+               Sys_LoadLibrary (dllnames_ve, &ve_dll, vorbisencfuncs);
+}
+
+void SCR_CaptureVideo_Ogg_Init()
+{
+       SCR_CaptureVideo_Ogg_OpenLibrary();
+}
+
+qboolean SCR_CaptureVideo_Ogg_Available()
+{
+       return og_dll && th_dll && vo_dll && ve_dll;
+}
+
+void SCR_CaptureVideo_Ogg_CloseDLL()
+{
+       Sys_UnloadLibrary (&ve_dll);
+       Sys_UnloadLibrary (&vo_dll);
+       Sys_UnloadLibrary (&th_dll);
+       Sys_UnloadLibrary (&og_dll);
+}
+
+typedef struct capturevideostate_ogg_formatspecific_s
+{
+       ogg_stream_state to, vo;
+       int serial1, serial2;
+       theora_info ti;
+       theora_state ts;
+       vorbis_info vi;
+       vorbis_dsp_state vd;
+       vorbis_block vb;
+       yuv_buffer yuv;
+       int channels;
+}
+capturevideostate_ogg_formatspecific_t;
+#define LOAD_FORMATSPECIFIC() capturevideostate_ogg_formatspecific_t *format = (capturevideostate_ogg_formatspecific_t *) cls.capturevideo.formatspecific
+
+void SCR_CaptureVideo_Ogg_Begin()
+{
+       cls.capturevideo.videofile = FS_OpenRealFile(va("%s.ogv", cls.capturevideo.basename), "wb", false);
+       cls.capturevideo.formatspecific = Mem_Alloc(tempmempool, sizeof(capturevideostate_ogg_formatspecific_t));
+       {
+               LOAD_FORMATSPECIFIC();
+               int num, denom;
+               ogg_page pg;
+               ogg_packet pt, pt2, pt3;
+               theora_comment tc;
+               vorbis_comment vc;
+
+               format->serial1 = rand();
+               qogg_stream_init(&format->to, format->serial1);
+
+               if(cls.capturevideo.soundrate)
+               {
+                       do
+                       {
+                               format->serial2 = rand();
+                       }
+                       while(format->serial1 == format->serial2);
+                       qogg_stream_init(&format->vo, format->serial2);
+               }
+
+               qtheora_info_init(&format->ti);
+               format->ti.frame_width = cls.capturevideo.width;
+               format->ti.frame_height = cls.capturevideo.height;
+               format->ti.width = (format->ti.frame_width + 15) & ~15;
+               format->ti.height = (format->ti.frame_height + 15) & ~15;
+               format->ti.offset_x = ((format->ti.width - format->ti.frame_width) / 2) & ~1;
+               format->ti.offset_y = ((format->ti.height - format->ti.frame_height) / 2) & ~1;
+
+               format->yuv.y_width = format->ti.width;
+               format->yuv.y_height = format->ti.height;
+               format->yuv.y_stride = format->ti.width;
+
+               format->yuv.uv_width = format->ti.width / 2;
+               format->yuv.uv_height = format->ti.height / 2;
+               format->yuv.uv_stride = format->ti.width / 2;
+
+               format->yuv.y = Mem_Alloc(tempmempool, format->yuv.y_stride * format->yuv.y_height);
+               format->yuv.u = Mem_Alloc(tempmempool, format->yuv.uv_stride * format->yuv.uv_height);
+               format->yuv.v = Mem_Alloc(tempmempool, format->yuv.uv_stride * format->yuv.uv_height);
+
+               FindFraction(cls.capturevideo.framerate, &num, &denom, 1001);
+               format->ti.fps_numerator = num;
+               format->ti.fps_denominator = denom;
+
+               FindFraction(1 / vid_pixelheight.value, &num, &denom, 1000);
+               format->ti.aspect_numerator = num;
+               format->ti.aspect_denominator = denom;
+
+               format->ti.colorspace = OC_CS_UNSPECIFIED;
+               format->ti.pixelformat = OC_PF_420;
+
+               format->ti.target_bitrate = -1;
+               format->ti.quality = 63; // TODO find good values here later
+               format->ti.quick_p = false;
+
+               format->ti.dropframes_p = false;
+               format->ti.keyframe_auto_p = false;
+               format->ti.keyframe_frequency = 64;
+               format->ti.keyframe_frequency_force = 64; // TODO
+               format->ti.keyframe_data_target_bitrate = -1;
+               format->ti.keyframe_auto_threshold = 80;
+               format->ti.keyframe_mindistance = 8;
+               format->ti.noise_sensitivity = 1; // TODO
+               format->ti.sharpness = 0;
+
+               qtheora_encode_init(&format->ts, &format->ti);
+
+               // vorbis?
+               if(cls.capturevideo.soundrate)
+               {
+                       qvorbis_info_init(&format->vi);
+                       qvorbis_encode_init_vbr(&format->vi, cls.capturevideo.soundchannels, cls.capturevideo.soundrate, 9);
+                       qvorbis_comment_init(&vc);
+                       qvorbis_analysis_init(&format->vd, &format->vi);
+                       qvorbis_block_init(&format->vd, &format->vb);
+               }
+
+               qtheora_comment_init(&tc);
+
+               /* create the remaining theora headers */
+               qtheora_encode_header(&format->ts, &pt);
+               qogg_stream_packetin(&format->to, &pt);
+               if (qogg_stream_pageout (&format->to, &pg) != 1)
+                       fprintf (stderr, "Internal Ogg library error.\n");
+               FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+               FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+
+               qtheora_encode_comment(&tc, &pt);
+               qogg_stream_packetin(&format->to, &pt);
+               qtheora_encode_tables(&format->ts, &pt);
+               qogg_stream_packetin (&format->to, &pt);
+
+               qtheora_comment_clear(&tc);
+
+               if(cls.capturevideo.soundrate)
+               {
+                       qvorbis_analysis_headerout(&format->vd, &vc, &pt, &pt2, &pt3);
+                       qogg_stream_packetin(&format->vo, &pt);
+                       if (qogg_stream_pageout (&format->vo, &pg) != 1)
+                               fprintf (stderr, "Internal Ogg library error.\n");
+                       FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+                       FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+
+                       qogg_stream_packetin(&format->vo, &pt2);
+                       qogg_stream_packetin(&format->vo, &pt3);
+
+                       qvorbis_comment_clear(&vc);
+               }
+
+               for(;;)
+               {
+                       int result = qogg_stream_flush (&format->to, &pg);
+                       if (result < 0)
+                               fprintf (stderr, "Internal Ogg library error.\n"); // TODO Host_Error
+                       if (result <= 0)
+                               break;
+                       FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+                       FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+               }
+
+               if(cls.capturevideo.soundrate)
+               for(;;)
+               {
+                       int result = qogg_stream_flush (&format->vo, &pg);
+                       if (result < 0)
+                               fprintf (stderr, "Internal Ogg library error.\n"); // TODO Host_Error
+                       if (result <= 0)
+                               break;
+                       FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+                       FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+               }
+       }
+}
+
+void SCR_CaptureVideo_Ogg_EndVideo()
+{
+       LOAD_FORMATSPECIFIC();
+       ogg_page pg;
+       ogg_packet pt;
+
+       // repeat the last frame so we can set the end-of-stream flag
+       qtheora_encode_YUVin(&format->ts, &format->yuv);
+       qtheora_encode_packetout(&format->ts, true, &pt);
+       qogg_stream_packetin(&format->to, &pt);
+
+       if(cls.capturevideo.soundrate)
+       {
+               qvorbis_analysis_wrote(&format->vd, 0);
+               while(qvorbis_analysis_blockout(&format->vd, &format->vb) == 1)
+               {
+                       qvorbis_analysis(&format->vb, NULL);
+                       qvorbis_bitrate_addblock(&format->vb);
+                       while(qvorbis_bitrate_flushpacket(&format->vd, &pt))
+                               qogg_stream_packetin(&format->vo, &pt);
+               }
+       }
+
+       if(qogg_stream_pageout(&format->to, &pg) > 0)
+       {
+               FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+               FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+       }
+
+       if(cls.capturevideo.soundrate)
+       {
+               if(qogg_stream_pageout(&format->vo, &pg) > 0)
+               {
+                       FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+                       FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+               }
+       }
+               
+       while (1) {
+               int result = qogg_stream_flush (&format->to, &pg);
+               if (result < 0)
+                       fprintf (stderr, "Internal Ogg library error.\n"); // TODO Host_Error
+               if (result <= 0)
+                       break;
+               FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+               FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+       }
+
+       if(cls.capturevideo.soundrate)
+       {
+               while (1) {
+                       int result = qogg_stream_flush (&format->vo, &pg);
+                       if (result < 0)
+                               fprintf (stderr, "Internal Ogg library error.\n"); // TODO Host_Error
+                       if (result <= 0)
+                               break;
+                       FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+                       FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+               }
+
+               qogg_stream_clear(&format->vo);
+               qvorbis_block_clear(&format->vb);
+               qvorbis_dsp_clear(&format->vd);
+               qvorbis_info_clear(&format->vi);
+       }
+
+       qogg_stream_clear(&format->to);
+       qtheora_clear(&format->ts);
+
+       Mem_Free(format);
+
+       // cl_screen.c does this
+       // FS_Close(cls.capturevideo.videofile);
+       // cls.capturevideo.videofile = NULL;
+}
+
+void SCR_CaptureVideo_Ogg_ConvertFrame_BGRA_to_YUV()
+{
+       LOAD_FORMATSPECIFIC();
+       int x, y;
+       int blockr, blockg, blockb;
+       unsigned char *b = cls.capturevideo.outbuffer;
+       int w = cls.capturevideo.width;
+       int h = cls.capturevideo.height;
+       int inpitch = w*4;
+
+       for(y = 0; y < h; ++y)
+       {
+               for(b = cls.capturevideo.outbuffer + (h-1-y)*w*4, x = 0; x < w; ++x)
+               {
+                       blockr = b[2];
+                       blockg = b[1];
+                       blockb = b[0];
+                       format->yuv.y[x + format->yuv.y_stride * y] =
+                               cls.capturevideo.yuvnormalizetable[0][cls.capturevideo.rgbtoyuvscaletable[0][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[0][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[0][2][blockb]];
+                       b += 4;
+               }
+
+               if((y & 1) == 0)
+               {
+                       for(b = cls.capturevideo.outbuffer + (h-2-y)*w*4, x = 0; x < w/2; ++x)
+                       {
+                               blockr = (b[2] + b[6] + b[inpitch+2] + b[inpitch+6]) >> 2;
+                               blockg = (b[1] + b[5] + b[inpitch+1] + b[inpitch+5]) >> 2;
+                               blockb = (b[0] + b[4] + b[inpitch+0] + b[inpitch+4]) >> 2;
+                               format->yuv.u[x + format->yuv.uv_stride * (y/2)] =
+                                       cls.capturevideo.yuvnormalizetable[1][cls.capturevideo.rgbtoyuvscaletable[1][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[1][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[1][2][blockb] + 128];
+                               format->yuv.v[x + format->yuv.uv_stride * (y/2)] =
+                                       cls.capturevideo.yuvnormalizetable[2][cls.capturevideo.rgbtoyuvscaletable[2][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[2][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[2][2][blockb] + 128];
+                               b += 8;
+                       }
+               }
+       }
+}
+
+void SCR_CaptureVideo_Ogg_VideoFrame()
+{
+       LOAD_FORMATSPECIFIC();
+       ogg_page pg;
+       ogg_packet pt;
+
+       // data is in cls.capturevideo.outbuffer as BGRA and has size width*height
+
+       SCR_CaptureVideo_Ogg_ConvertFrame_BGRA_to_YUV();
+       qtheora_encode_YUVin(&format->ts, &format->yuv);
+       qtheora_encode_packetout(&format->ts, false, &pt);
+       qogg_stream_packetin(&format->to, &pt);
+
+       while(qogg_stream_pageout(&format->to, &pg) > 0)
+       {
+               FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+               FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+       }
+}
+
+void SCR_CaptureVideo_Ogg_SoundFrame(const portable_sampleframe_t *paintbuffer, size_t length)
+{
+       LOAD_FORMATSPECIFIC();
+       float **vorbis_buffer;
+       size_t i;
+       int j;
+       ogg_page pg;
+       ogg_packet pt;
+
+       vorbis_buffer = qvorbis_analysis_buffer(&format->vd, length);
+       for(i = 0; i < length; ++i)
+       {
+               for(j = 0; j < cls.capturevideo.soundchannels; ++j)
+                       vorbis_buffer[j][i] = paintbuffer[i].sample[j] / 32768.0f;
+       }
+       qvorbis_analysis_wrote(&format->vd, length);
+
+       while(qvorbis_analysis_blockout(&format->vd, &format->vb) == 1)
+       {
+               qvorbis_analysis(&format->vb, NULL);
+               qvorbis_bitrate_addblock(&format->vb);
+
+               while(qvorbis_bitrate_flushpacket(&format->vd, &pt))
+                       qogg_stream_packetin(&format->vo, &pt);
+       }
+
+       while(qogg_stream_pageout(&format->vo, &pg) > 0)
+       {
+               FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
+               FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
+       }
+}
diff --git a/cap_ogg.h b/cap_ogg.h
new file mode 100644 (file)
index 0000000..287e96a
--- /dev/null
+++ b/cap_ogg.h
@@ -0,0 +1,8 @@
+#include "snd_main.h"
+void SCR_CaptureVideo_Ogg_Init();
+qboolean SCR_CaptureVideo_Ogg_Available();
+void SCR_CaptureVideo_Ogg_Begin();
+void SCR_CaptureVideo_Ogg_EndVideo();
+void SCR_CaptureVideo_Ogg_VideoFrame();
+void SCR_CaptureVideo_Ogg_SoundFrame(const portable_sampleframe_t *paintbuffer, size_t length);
+void SCR_CaptureVideo_Ogg_CloseDLL();