]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cap_ogg.c
make offsetmapping bias actually work
[xonotic/darkplaces.git] / cap_ogg.c
1 #ifndef _MSC_VER
2 #include <stdint.h>
3 #endif
4 #include <sys/types.h>
5
6 #include "quakedef.h"
7 #include "client.h"
8 #include "cap_ogg.h"
9
10 // video capture cvars
11 static cvar_t cl_capturevideo_ogg_theora_vp3compat = {CVAR_SAVE, "cl_capturevideo_ogg_theora_vp3compat", "1", "make VP3 compatible theora streams"};
12 static cvar_t cl_capturevideo_ogg_theora_quality = {CVAR_SAVE, "cl_capturevideo_ogg_theora_quality", "48", "video quality factor (0 to 63), or -1 to use bitrate only; higher is better; setting both to -1 achieves unlimited quality"};
13 static cvar_t cl_capturevideo_ogg_theora_bitrate = {CVAR_SAVE, "cl_capturevideo_ogg_theora_bitrate", "-1", "video bitrate (45 to 2000 kbps), or -1 to use quality only; higher is better; setting both to -1 achieves unlimited quality"};
14 static cvar_t cl_capturevideo_ogg_theora_keyframe_bitrate_multiplier = {CVAR_SAVE, "cl_capturevideo_ogg_theora_keyframe_bitrate_multiplier", "1.5", "how much more bit rate to use for keyframes, specified as a factor of at least 1"};
15 static cvar_t cl_capturevideo_ogg_theora_keyframe_maxinterval = {CVAR_SAVE, "cl_capturevideo_ogg_theora_keyframe_maxinterval", "64", "maximum keyframe interval (1 to 1000)"};
16 static cvar_t cl_capturevideo_ogg_theora_keyframe_mininterval = {CVAR_SAVE, "cl_capturevideo_ogg_theora_keyframe_mininterval", "8", "minimum keyframe interval (1 to 1000)"};
17 static cvar_t cl_capturevideo_ogg_theora_keyframe_auto_threshold = {CVAR_SAVE, "cl_capturevideo_ogg_theora_keyframe_auto_threshold", "80", "threshold for key frame decision (0 to 100)"};
18 static cvar_t cl_capturevideo_ogg_theora_noise_sensitivity = {CVAR_SAVE, "cl_capturevideo_ogg_theora_noise_sensitivity", "1", "video noise sensitivity (0 to 6); lower is better"};
19 static cvar_t cl_capturevideo_ogg_theora_sharpness = {CVAR_SAVE, "cl_capturevideo_ogg_theora_sharpness", "0", "sharpness (0 to 2); lower is sharper"};
20 static cvar_t cl_capturevideo_ogg_vorbis_quality = {CVAR_SAVE, "cl_capturevideo_ogg_vorbis_quality", "3", "audio quality (-1 to 10); higher is better"};
21
22 // ogg.h stuff
23 #ifdef _MSC_VER
24 typedef __int16 ogg_int16_t;
25 typedef unsigned __int16 ogg_uint16_t;
26 typedef __int32 ogg_int32_t;
27 typedef unsigned __int32 ogg_uint32_t;
28 typedef __int64 ogg_int64_t;
29 #else
30 typedef int16_t ogg_int16_t;
31 typedef uint16_t ogg_uint16_t;
32 typedef int32_t ogg_int32_t;
33 typedef uint32_t ogg_uint32_t;
34 typedef int64_t ogg_int64_t;
35 #endif
36
37 typedef struct {
38   long endbyte;
39   int  endbit;
40
41   unsigned char *buffer;
42   unsigned char *ptr;
43   long storage;
44 } oggpack_buffer;
45
46 /* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/
47
48 typedef struct {
49   unsigned char *header;
50   long header_len;
51   unsigned char *body;
52   long body_len;
53 } ogg_page;
54
55 /* ogg_stream_state contains the current encode/decode state of a logical
56    Ogg bitstream **********************************************************/
57
58 typedef struct {
59   unsigned char   *body_data;    /* bytes from packet bodies */
60   long    body_storage;          /* storage elements allocated */
61   long    body_fill;             /* elements stored; fill mark */
62   long    body_returned;         /* elements of fill returned */
63
64
65   int     *lacing_vals;      /* The values that will go to the segment table */
66   ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
67                                 this way, but it is simple coupled to the
68                                 lacing fifo */
69   long    lacing_storage;
70   long    lacing_fill;
71   long    lacing_packet;
72   long    lacing_returned;
73
74   unsigned char    header[282];      /* working space for header encode */
75   int              header_fill;
76
77   int     e_o_s;          /* set when we have buffered the last packet in the
78                              logical bitstream */
79   int     b_o_s;          /* set after we've written the initial page
80                              of a logical bitstream */
81   long    serialno;
82   long    pageno;
83   ogg_int64_t  packetno;      /* sequence number for decode; the framing
84                              knows where there's a hole in the data,
85                              but we need coupling so that the codec
86                              (which is in a seperate abstraction
87                              layer) also knows about the gap */
88   ogg_int64_t   granulepos;
89
90 } ogg_stream_state;
91
92 /* ogg_packet is used to encapsulate the data and metadata belonging
93    to a single raw Ogg/Vorbis packet *************************************/
94
95 typedef struct {
96   unsigned char *packet;
97   long  bytes;
98   long  b_o_s;
99   long  e_o_s;
100
101   ogg_int64_t  granulepos;
102   
103   ogg_int64_t  packetno;     /* sequence number for decode; the framing
104                                 knows where there's a hole in the data,
105                                 but we need coupling so that the codec
106                                 (which is in a seperate abstraction
107                                 layer) also knows about the gap */
108 } ogg_packet;
109
110 typedef struct {
111   unsigned char *data;
112   int storage;
113   int fill;
114   int returned;
115
116   int unsynced;
117   int headerbytes;
118   int bodybytes;
119 } ogg_sync_state;
120
121 /* Ogg BITSTREAM PRIMITIVES: encoding **************************/
122
123 static int      (*qogg_stream_packetin) (ogg_stream_state *os, ogg_packet *op);
124 static int      (*qogg_stream_pageout) (ogg_stream_state *os, ogg_page *og);
125 static int      (*qogg_stream_flush) (ogg_stream_state *os, ogg_page *og);
126
127 /* Ogg BITSTREAM PRIMITIVES: general ***************************/
128
129 static int      (*qogg_stream_init) (ogg_stream_state *os,int serialno);
130 static int      (*qogg_stream_clear) (ogg_stream_state *os);
131 static ogg_int64_t  (*qogg_page_granulepos) (ogg_page *og);
132
133 // end of ogg.h stuff
134
135 // vorbis/codec.h stuff
136 typedef struct vorbis_info{
137   int version;
138   int channels;
139   long rate;
140
141   /* The below bitrate declarations are *hints*.
142      Combinations of the three values carry the following implications:
143
144      all three set to the same value:
145        implies a fixed rate bitstream
146      only nominal set:
147        implies a VBR stream that averages the nominal bitrate.  No hard
148        upper/lower limit
149      upper and or lower set:
150        implies a VBR bitstream that obeys the bitrate limits. nominal
151        may also be set to give a nominal rate.
152      none set:
153        the coder does not care to speculate.
154   */
155
156   long bitrate_upper;
157   long bitrate_nominal;
158   long bitrate_lower;
159   long bitrate_window;
160
161   void *codec_setup;
162 } vorbis_info;
163
164 /* vorbis_dsp_state buffers the current vorbis audio
165    analysis/synthesis state.  The DSP state belongs to a specific
166    logical bitstream ****************************************************/
167 typedef struct vorbis_dsp_state{
168   int analysisp;
169   vorbis_info *vi;
170
171   float **pcm;
172   float **pcmret;
173   int      pcm_storage;
174   int      pcm_current;
175   int      pcm_returned;
176
177   int  preextrapolate;
178   int  eofflag;
179
180   long lW;
181   long W;
182   long nW;
183   long centerW;
184
185   ogg_int64_t granulepos;
186   ogg_int64_t sequence;
187
188   ogg_int64_t glue_bits;
189   ogg_int64_t time_bits;
190   ogg_int64_t floor_bits;
191   ogg_int64_t res_bits;
192
193   void       *backend_state;
194 } vorbis_dsp_state;
195
196 typedef struct vorbis_block{
197   /* necessary stream state for linking to the framing abstraction */
198   float  **pcm;       /* this is a pointer into local storage */
199   oggpack_buffer opb;
200
201   long  lW;
202   long  W;
203   long  nW;
204   int   pcmend;
205   int   mode;
206
207   int         eofflag;
208   ogg_int64_t granulepos;
209   ogg_int64_t sequence;
210   vorbis_dsp_state *vd; /* For read-only access of configuration */
211
212   /* local storage to avoid remallocing; it's up to the mapping to
213      structure it */
214   void               *localstore;
215   long                localtop;
216   long                localalloc;
217   long                totaluse;
218   struct alloc_chain *reap;
219
220   /* bitmetrics for the frame */
221   long glue_bits;
222   long time_bits;
223   long floor_bits;
224   long res_bits;
225
226   void *internal;
227
228 } vorbis_block;
229
230 /* vorbis_block is a single block of data to be processed as part of
231 the analysis/synthesis stream; it belongs to a specific logical
232 bitstream, but is independant from other vorbis_blocks belonging to
233 that logical bitstream. *************************************************/
234
235 struct alloc_chain{
236   void *ptr;
237   struct alloc_chain *next;
238 };
239
240 /* vorbis_info contains all the setup information specific to the
241    specific compression/decompression mode in progress (eg,
242    psychoacoustic settings, channel setup, options, codebook
243    etc). vorbis_info and substructures are in backends.h.
244 *********************************************************************/
245
246 /* the comments are not part of vorbis_info so that vorbis_info can be
247    static storage */
248 typedef struct vorbis_comment{
249   /* unlimited user comment fields.  libvorbis writes 'libvorbis'
250      whatever vendor is set to in encode */
251   char **user_comments;
252   int   *comment_lengths;
253   int    comments;
254   char  *vendor;
255
256 } vorbis_comment;
257
258
259 /* libvorbis encodes in two abstraction layers; first we perform DSP
260    and produce a packet (see docs/analysis.txt).  The packet is then
261    coded into a framed OggSquish bitstream by the second layer (see
262    docs/framing.txt).  Decode is the reverse process; we sync/frame
263    the bitstream and extract individual packets, then decode the
264    packet back into PCM audio.
265
266    The extra framing/packetizing is used in streaming formats, such as
267    files.  Over the net (such as with UDP), the framing and
268    packetization aren't necessary as they're provided by the transport
269    and the streaming layer is not used */
270
271 /* Vorbis PRIMITIVES: general ***************************************/
272
273 static void     (*qvorbis_info_init) (vorbis_info *vi);
274 static void     (*qvorbis_info_clear) (vorbis_info *vi);
275 static void     (*qvorbis_comment_init) (vorbis_comment *vc);
276 static void     (*qvorbis_comment_clear) (vorbis_comment *vc);
277
278 static int      (*qvorbis_block_init) (vorbis_dsp_state *v, vorbis_block *vb);
279 static int      (*qvorbis_block_clear) (vorbis_block *vb);
280 static void     (*qvorbis_dsp_clear) (vorbis_dsp_state *v);
281 static double   (*qvorbis_granule_time) (vorbis_dsp_state *v,
282                                     ogg_int64_t granulepos);
283
284 /* Vorbis PRIMITIVES: analysis/DSP layer ****************************/
285
286 static int      (*qvorbis_analysis_init) (vorbis_dsp_state *v,vorbis_info *vi);
287 static int      (*qvorbis_commentheader_out) (vorbis_comment *vc, ogg_packet *op);
288 static int      (*qvorbis_analysis_headerout) (vorbis_dsp_state *v,
289                                           vorbis_comment *vc,
290                                           ogg_packet *op,
291                                           ogg_packet *op_comm,
292                                           ogg_packet *op_code);
293 static float ** (*qvorbis_analysis_buffer) (vorbis_dsp_state *v,int vals);
294 static int      (*qvorbis_analysis_wrote) (vorbis_dsp_state *v,int vals);
295 static int      (*qvorbis_analysis_blockout) (vorbis_dsp_state *v,vorbis_block *vb);
296 static int      (*qvorbis_analysis) (vorbis_block *vb,ogg_packet *op);
297
298 static int      (*qvorbis_bitrate_addblock) (vorbis_block *vb);
299 static int      (*qvorbis_bitrate_flushpacket) (vorbis_dsp_state *vd,
300                                            ogg_packet *op);
301
302 // end of vorbis/codec.h stuff
303
304 // vorbisenc.h stuff
305 static int (*qvorbis_encode_init_vbr) (vorbis_info *vi,
306                                   long channels,
307                                   long rate,
308
309                                   float base_quality /* quality level from 0. (lo) to 1. (hi) */
310                                   );
311 // end of vorbisenc.h stuff
312
313 // theora.h stuff
314
315 #define TH_ENCCTL_SET_VP3_COMPATIBLE (10)
316
317 typedef struct {
318     int   y_width;      /**< Width of the Y' luminance plane */
319     int   y_height;     /**< Height of the luminance plane */
320     int   y_stride;     /**< Offset in bytes between successive rows */
321
322     int   uv_width;     /**< Width of the Cb and Cr chroma planes */
323     int   uv_height;    /**< Height of the chroma planes */
324     int   uv_stride;    /**< Offset between successive chroma rows */
325     unsigned char *y;   /**< Pointer to start of luminance data */
326     unsigned char *u;   /**< Pointer to start of Cb data */
327     unsigned char *v;   /**< Pointer to start of Cr data */
328
329 } yuv_buffer;
330
331 /**
332  * A Colorspace.
333  */
334 typedef enum {
335   OC_CS_UNSPECIFIED,    /**< The colorspace is unknown or unspecified */
336   OC_CS_ITU_REC_470M,   /**< This is the best option for 'NTSC' content */
337   OC_CS_ITU_REC_470BG,  /**< This is the best option for 'PAL' content */
338   OC_CS_NSPACES         /**< This marks the end of the defined colorspaces */
339 } theora_colorspace;
340
341 /**
342  * A Chroma subsampling
343  *
344  * These enumerate the available chroma subsampling options supported
345  * by the theora format. See Section 4.4 of the specification for
346  * exact definitions.
347  */
348 typedef enum {
349   OC_PF_420,    /**< Chroma subsampling by 2 in each direction (4:2:0) */
350   OC_PF_RSVD,   /**< Reserved value */
351   OC_PF_422,    /**< Horizonatal chroma subsampling by 2 (4:2:2) */
352   OC_PF_444     /**< No chroma subsampling at all (4:4:4) */
353 } theora_pixelformat;
354 /**
355  * Theora bitstream info.
356  * Contains the basic playback parameters for a stream,
357  * corresponding to the initial 'info' header packet.
358  * 
359  * Encoded theora frames must be a multiple of 16 in width and height.
360  * To handle other frame sizes, a crop rectangle is specified in
361  * frame_height and frame_width, offset_x and * offset_y. The offset
362  * and size should still be a multiple of 2 to avoid chroma sampling
363  * shifts. Offset values in this structure are measured from the
364  * upper left of the image.
365  *
366  * Frame rate, in frames per second, is stored as a rational
367  * fraction. Aspect ratio is also stored as a rational fraction, and
368  * refers to the aspect ratio of the frame pixels, not of the
369  * overall frame itself.
370  * 
371  * See <a href="http://svn.xiph.org/trunk/theora/examples/encoder_example.c">
372  * examples/encoder_example.c</a> for usage examples of the
373  * other paramters and good default settings for the encoder parameters.
374  */
375 typedef struct {
376   ogg_uint32_t  width;      /**< encoded frame width  */
377   ogg_uint32_t  height;     /**< encoded frame height */
378   ogg_uint32_t  frame_width;    /**< display frame width  */
379   ogg_uint32_t  frame_height;   /**< display frame height */
380   ogg_uint32_t  offset_x;   /**< horizontal offset of the displayed frame */
381   ogg_uint32_t  offset_y;   /**< vertical offset of the displayed frame */
382   ogg_uint32_t  fps_numerator;      /**< frame rate numerator **/
383   ogg_uint32_t  fps_denominator;    /**< frame rate denominator **/
384   ogg_uint32_t  aspect_numerator;   /**< pixel aspect ratio numerator */
385   ogg_uint32_t  aspect_denominator; /**< pixel aspect ratio denominator */
386   theora_colorspace colorspace;     /**< colorspace */
387   int           target_bitrate;     /**< nominal bitrate in bits per second */
388   int           quality;  /**< Nominal quality setting, 0-63 */
389   int           quick_p;  /**< Quick encode/decode */
390
391   /* decode only */
392   unsigned char version_major;
393   unsigned char version_minor;
394   unsigned char version_subminor;
395
396   void *codec_setup;
397
398   /* encode only */
399   int           dropframes_p;
400   int           keyframe_auto_p;
401   ogg_uint32_t  keyframe_frequency;
402   ogg_uint32_t  keyframe_frequency_force;  /* also used for decode init to
403                                               get granpos shift correct */
404   ogg_uint32_t  keyframe_data_target_bitrate;
405   ogg_int32_t   keyframe_auto_threshold;
406   ogg_uint32_t  keyframe_mindistance;
407   ogg_int32_t   noise_sensitivity;
408   ogg_int32_t   sharpness;
409
410   theora_pixelformat pixelformat;   /**< chroma subsampling mode to expect */
411
412 } theora_info;
413
414 /** Codec internal state and context.
415  */
416 typedef struct{
417   theora_info *i;
418   ogg_int64_t granulepos;
419
420   void *internal_encode;
421   void *internal_decode;
422
423 } theora_state;
424
425 /** 
426  * Comment header metadata.
427  *
428  * This structure holds the in-stream metadata corresponding to
429  * the 'comment' header packet.
430  *
431  * Meta data is stored as a series of (tag, value) pairs, in
432  * length-encoded string vectors. The first occurence of the 
433  * '=' character delimits the tag and value. A particular tag
434  * may occur more than once. The character set encoding for
435  * the strings is always UTF-8, but the tag names are limited
436  * to case-insensitive ASCII. See the spec for details.
437  *
438  * In filling in this structure, qtheora_decode_header() will
439  * null-terminate the user_comment strings for safety. However,
440  * the bitstream format itself treats them as 8-bit clean,
441  * and so the length array should be treated as authoritative
442  * for their length.
443  */
444 typedef struct theora_comment{
445   char **user_comments;         /**< An array of comment string vectors */
446   int   *comment_lengths;       /**< An array of corresponding string vector lengths in bytes */
447   int    comments;              /**< The total number of comment string vectors */
448   char  *vendor;                /**< The vendor string identifying the encoder, null terminated */
449
450 } theora_comment;
451 static int (*qtheora_encode_init) (theora_state *th, theora_info *ti);
452 static int (*qtheora_encode_YUVin) (theora_state *t, yuv_buffer *yuv);
453 static int (*qtheora_encode_packetout) ( theora_state *t, int last_p,
454                                     ogg_packet *op);
455 static int (*qtheora_encode_header) (theora_state *t, ogg_packet *op);
456 static int (*qtheora_encode_comment) (theora_comment *tc, ogg_packet *op);
457 static int (*qtheora_encode_tables) (theora_state *t, ogg_packet *op);
458 static void (*qtheora_info_init) (theora_info *c);
459 static void (*qtheora_info_clear) (theora_info *c);
460 static void (*qtheora_clear) (theora_state *t);
461 static void (*qtheora_comment_init) (theora_comment *tc);
462 static void  (*qtheora_comment_clear) (theora_comment *tc);
463 static double (*qtheora_granule_time) (theora_state *th,ogg_int64_t granulepos);
464 static int (*qtheora_control) (theora_state *th,int req,void *buf,size_t buf_sz);
465 // end of theora.h stuff
466
467 static dllfunction_t oggfuncs[] =
468 {
469         {"ogg_stream_packetin", (void **) &qogg_stream_packetin},
470         {"ogg_stream_pageout", (void **) &qogg_stream_pageout},
471         {"ogg_stream_flush", (void **) &qogg_stream_flush},
472         {"ogg_stream_init", (void **) &qogg_stream_init},
473         {"ogg_stream_clear", (void **) &qogg_stream_clear},
474         {"ogg_page_granulepos", (void **) &qogg_page_granulepos},
475         {NULL, NULL}
476 };
477
478 static dllfunction_t vorbisencfuncs[] =
479 {
480         {"vorbis_encode_init_vbr", (void **) &qvorbis_encode_init_vbr},
481         {NULL, NULL}
482 };
483
484 static dllfunction_t vorbisfuncs[] =
485 {
486         {"vorbis_info_init", (void **) &qvorbis_info_init},
487         {"vorbis_info_clear", (void **) &qvorbis_info_clear},
488         {"vorbis_comment_init", (void **) &qvorbis_comment_init},
489         {"vorbis_comment_clear", (void **) &qvorbis_comment_clear},
490         {"vorbis_block_init", (void **) &qvorbis_block_init},
491         {"vorbis_block_clear", (void **) &qvorbis_block_clear},
492         {"vorbis_dsp_clear", (void **) &qvorbis_dsp_clear},
493         {"vorbis_analysis_init", (void **) &qvorbis_analysis_init},
494         {"vorbis_commentheader_out", (void **) &qvorbis_commentheader_out},
495         {"vorbis_analysis_headerout", (void **) &qvorbis_analysis_headerout},
496         {"vorbis_analysis_buffer", (void **) &qvorbis_analysis_buffer},
497         {"vorbis_analysis_wrote", (void **) &qvorbis_analysis_wrote},
498         {"vorbis_analysis_blockout", (void **) &qvorbis_analysis_blockout},
499         {"vorbis_analysis", (void **) &qvorbis_analysis},
500         {"vorbis_bitrate_addblock", (void **) &qvorbis_bitrate_addblock},
501         {"vorbis_bitrate_flushpacket", (void **) &qvorbis_bitrate_flushpacket},
502         {"vorbis_granule_time", (void **) &qvorbis_granule_time},
503         {NULL, NULL}
504 };
505
506 static dllfunction_t theorafuncs[] =
507 {
508         {"theora_info_init", (void **) &qtheora_info_init},
509         {"theora_info_clear", (void **) &qtheora_info_clear},
510         {"theora_comment_init", (void **) &qtheora_comment_init},
511         {"theora_comment_clear", (void **) &qtheora_comment_clear},
512         {"theora_encode_init", (void **) &qtheora_encode_init},
513         {"theora_encode_YUVin", (void **) &qtheora_encode_YUVin},
514         {"theora_encode_packetout", (void **) &qtheora_encode_packetout},
515         {"theora_encode_header", (void **) &qtheora_encode_header},
516         {"theora_encode_comment", (void **) &qtheora_encode_comment},
517         {"theora_encode_tables", (void **) &qtheora_encode_tables},
518         {"theora_clear", (void **) &qtheora_clear},
519         {"theora_granule_time", (void **) &qtheora_granule_time},
520         {"theora_control", (void **) &qtheora_control},
521         {NULL, NULL}
522 };
523
524 static dllhandle_t og_dll = NULL, vo_dll = NULL, ve_dll = NULL, th_dll = NULL;
525
526 qboolean SCR_CaptureVideo_Ogg_OpenLibrary(void)
527 {
528         const char* dllnames_og [] =
529         {
530 #if defined(WIN32)
531                 "libogg-0.dll",
532                 "libogg.dll",
533                 "ogg.dll",
534 #elif defined(MACOSX)
535                 "libogg.dylib",
536 #else
537                 "libogg.so.0",
538                 "libogg.so",
539 #endif
540                 NULL
541         };
542         const char* dllnames_vo [] =
543         {
544 #if defined(WIN32)
545                 "libvorbis-0.dll",
546                 "libvorbis.dll",
547                 "vorbis.dll",
548 #elif defined(MACOSX)
549                 "libvorbis.dylib",
550 #else
551                 "libvorbis.so.0",
552                 "libvorbis.so",
553 #endif
554                 NULL
555         };
556         const char* dllnames_ve [] =
557         {
558 #if defined(WIN32)
559                 "libvorbisenc-2.dll",
560                 "libvorbisenc.dll",
561                 "vorbisenc.dll",
562 #elif defined(MACOSX)
563                 "libvorbisenc.dylib",
564 #else
565                 "libvorbisenc.so.2",
566                 "libvorbisenc.so",
567 #endif
568                 NULL
569         };
570         const char* dllnames_th [] =
571         {
572 #if defined(WIN32)
573                 "libtheora-0.dll",
574                 "libtheora.dll",
575                 "theora.dll",
576 #elif defined(MACOSX)
577                 "libtheora.dylib",
578 #else
579                 "libtheora.so.0",
580                 "libtheora.so",
581 #endif
582                 NULL
583         };
584
585         return
586                 Sys_LoadLibrary (dllnames_og, &og_dll, oggfuncs)
587                 &&
588                 Sys_LoadLibrary (dllnames_th, &th_dll, theorafuncs)
589                 &&
590                 Sys_LoadLibrary (dllnames_vo, &vo_dll, vorbisfuncs)
591                 &&
592                 Sys_LoadLibrary (dllnames_ve, &ve_dll, vorbisencfuncs);
593 }
594
595 void SCR_CaptureVideo_Ogg_Init(void)
596 {
597         SCR_CaptureVideo_Ogg_OpenLibrary();
598
599         Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_vp3compat);
600         Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_quality);
601         Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_bitrate);
602         Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_keyframe_bitrate_multiplier);
603         Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_keyframe_maxinterval);
604         Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_keyframe_mininterval);
605         Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_keyframe_auto_threshold);
606         Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_noise_sensitivity);
607         Cvar_RegisterVariable(&cl_capturevideo_ogg_vorbis_quality);
608 }
609
610 qboolean SCR_CaptureVideo_Ogg_Available(void)
611 {
612         return og_dll && th_dll && vo_dll && ve_dll;
613 }
614
615 void SCR_CaptureVideo_Ogg_CloseDLL(void)
616 {
617         Sys_UnloadLibrary (&ve_dll);
618         Sys_UnloadLibrary (&vo_dll);
619         Sys_UnloadLibrary (&th_dll);
620         Sys_UnloadLibrary (&og_dll);
621 }
622
623 // this struct should not be needed
624 // however, libogg appears to pull the ogg_page's data element away from our
625 // feet before we get to write the data due to interleaving
626 // so this struct is used to keep the page data around until it actually gets
627 // written
628 typedef struct allocatedoggpage_s
629 {
630         size_t len;
631         double time;
632         unsigned char data[65307];
633         // this number is from RFC 3533. In case libogg writes more, we'll have to increase this
634         // but we'll get a Host_Error in this case so we can track it down
635 }
636 allocatedoggpage_t;
637
638 typedef struct capturevideostate_ogg_formatspecific_s
639 {
640         ogg_stream_state to, vo;
641         int serial1, serial2;
642         theora_state ts;
643         vorbis_dsp_state vd;
644         vorbis_block vb;
645         vorbis_info vi;
646         yuv_buffer yuv[2];
647         int yuvi;
648         int lastnum;
649         int channels;
650
651         allocatedoggpage_t videopage, audiopage;
652 }
653 capturevideostate_ogg_formatspecific_t;
654 #define LOAD_FORMATSPECIFIC_OGG() capturevideostate_ogg_formatspecific_t *format = (capturevideostate_ogg_formatspecific_t *) cls.capturevideo.formatspecific
655
656 static void SCR_CaptureVideo_Ogg_Interleave(void)
657 {
658         LOAD_FORMATSPECIFIC_OGG();
659         ogg_page pg;
660
661         if(!cls.capturevideo.soundrate)
662         {
663                 while(qogg_stream_pageout(&format->to, &pg) > 0)
664                 {
665                         FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
666                         FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
667                 }
668                 return;
669         }
670
671         for(;;)
672         {
673                 // first: make sure we have a page of both types
674                 if(!format->videopage.len)
675                         if(qogg_stream_pageout(&format->to, &pg) > 0)
676                         {
677                                 format->videopage.len = pg.header_len + pg.body_len;
678                                 format->videopage.time = qtheora_granule_time(&format->ts, qogg_page_granulepos(&pg));
679                                 if(format->videopage.len > sizeof(format->videopage.data))
680                                         Host_Error("video page too long");
681                                 memcpy(format->videopage.data, pg.header, pg.header_len);
682                                 memcpy(format->videopage.data + pg.header_len, pg.body, pg.body_len);
683                         }
684                 if(!format->audiopage.len)
685                         if(qogg_stream_pageout(&format->vo, &pg) > 0)
686                         {
687                                 format->audiopage.len = pg.header_len + pg.body_len;
688                                 format->audiopage.time = qvorbis_granule_time(&format->vd, qogg_page_granulepos(&pg));
689                                 if(format->audiopage.len > sizeof(format->audiopage.data))
690                                         Host_Error("audio page too long");
691                                 memcpy(format->audiopage.data, pg.header, pg.header_len);
692                                 memcpy(format->audiopage.data + pg.header_len, pg.body, pg.body_len);
693                         }
694
695                 if(format->videopage.len && format->audiopage.len)
696                 {
697                         // output the page that ends first
698                         if(format->videopage.time < format->audiopage.time)
699                         {
700                                 FS_Write(cls.capturevideo.videofile, format->videopage.data, format->videopage.len);
701                                 format->videopage.len = 0;
702                         }
703                         else
704                         {
705                                 FS_Write(cls.capturevideo.videofile, format->audiopage.data, format->audiopage.len);
706                                 format->audiopage.len = 0;
707                         }
708                 }
709                 else
710                         break;
711         }
712 }
713
714 static void SCR_CaptureVideo_Ogg_FlushInterleaving(void)
715 {
716         LOAD_FORMATSPECIFIC_OGG();
717
718         if(cls.capturevideo.soundrate)
719         if(format->audiopage.len)
720         {
721                 FS_Write(cls.capturevideo.videofile, format->audiopage.data, format->audiopage.len);
722                 format->audiopage.len = 0;
723         }
724
725         if(format->videopage.len)
726         {
727                 FS_Write(cls.capturevideo.videofile, format->videopage.data, format->videopage.len);
728                 format->videopage.len = 0;
729         }
730 }
731
732 static void SCR_CaptureVideo_Ogg_EndVideo(void)
733 {
734         LOAD_FORMATSPECIFIC_OGG();
735         ogg_page pg;
736         ogg_packet pt;
737
738         if(format->yuvi >= 0)
739         {
740                 // send the previous (and last) frame
741                 while(format->lastnum-- > 0)
742                 {
743                         qtheora_encode_YUVin(&format->ts, &format->yuv[format->yuvi]);
744
745                         while(qtheora_encode_packetout(&format->ts, !format->lastnum, &pt))
746                                 qogg_stream_packetin(&format->to, &pt);
747
748                         SCR_CaptureVideo_Ogg_Interleave();
749                 }
750         }
751
752         if(cls.capturevideo.soundrate)
753         {
754                 qvorbis_analysis_wrote(&format->vd, 0);
755                 while(qvorbis_analysis_blockout(&format->vd, &format->vb) == 1)
756                 {
757                         qvorbis_analysis(&format->vb, NULL);
758                         qvorbis_bitrate_addblock(&format->vb);
759                         while(qvorbis_bitrate_flushpacket(&format->vd, &pt))
760                                 qogg_stream_packetin(&format->vo, &pt);
761                         SCR_CaptureVideo_Ogg_Interleave();
762                 }
763         }
764
765         SCR_CaptureVideo_Ogg_FlushInterleaving();
766
767         while(qogg_stream_pageout(&format->to, &pg) > 0)
768         {
769                 FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
770                 FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
771         }
772
773         if(cls.capturevideo.soundrate)
774         {
775                 while(qogg_stream_pageout(&format->vo, &pg) > 0)
776                 {
777                         FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
778                         FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
779                 }
780         }
781                 
782         while (1) {
783                 int result = qogg_stream_flush (&format->to, &pg);
784                 if (result < 0)
785                         fprintf (stderr, "Internal Ogg library error.\n"); // TODO Host_Error
786                 if (result <= 0)
787                         break;
788                 FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
789                 FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
790         }
791
792         if(cls.capturevideo.soundrate)
793         {
794                 while (1) {
795                         int result = qogg_stream_flush (&format->vo, &pg);
796                         if (result < 0)
797                                 fprintf (stderr, "Internal Ogg library error.\n"); // TODO Host_Error
798                         if (result <= 0)
799                                 break;
800                         FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
801                         FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
802                 }
803
804                 qogg_stream_clear(&format->vo);
805                 qvorbis_block_clear(&format->vb);
806                 qvorbis_dsp_clear(&format->vd);
807         }
808
809         qogg_stream_clear(&format->to);
810         qtheora_clear(&format->ts);
811         qvorbis_info_clear(&format->vi);
812
813         Mem_Free(format->yuv[0].y);
814         Mem_Free(format->yuv[0].u);
815         Mem_Free(format->yuv[0].v);
816         Mem_Free(format->yuv[1].y);
817         Mem_Free(format->yuv[1].u);
818         Mem_Free(format->yuv[1].v);
819         Mem_Free(format);
820
821         FS_Close(cls.capturevideo.videofile);
822         cls.capturevideo.videofile = NULL;
823 }
824
825 static void SCR_CaptureVideo_Ogg_ConvertFrame_BGRA_to_YUV(void)
826 {
827         LOAD_FORMATSPECIFIC_OGG();
828         yuv_buffer *yuv;
829         int x, y;
830         int blockr, blockg, blockb;
831         unsigned char *b = cls.capturevideo.outbuffer;
832         int w = cls.capturevideo.width;
833         int h = cls.capturevideo.height;
834         int inpitch = w*4;
835
836         yuv = &format->yuv[format->yuvi];
837
838         for(y = 0; y < h; ++y)
839         {
840                 for(b = cls.capturevideo.outbuffer + (h-1-y)*w*4, x = 0; x < w; ++x)
841                 {
842                         blockr = b[2];
843                         blockg = b[1];
844                         blockb = b[0];
845                         yuv->y[x + yuv->y_stride * y] =
846                                 cls.capturevideo.yuvnormalizetable[0][cls.capturevideo.rgbtoyuvscaletable[0][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[0][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[0][2][blockb]];
847                         b += 4;
848                 }
849
850                 if ((y & 1) == 0 && y/2 < h/2) // if h is odd, this skips the last row
851                 {
852                         for(b = cls.capturevideo.outbuffer + (h-2-y)*w*4, x = 0; x < w/2; ++x)
853                         {
854                                 blockr = (b[2] + b[6] + b[inpitch+2] + b[inpitch+6]) >> 2;
855                                 blockg = (b[1] + b[5] + b[inpitch+1] + b[inpitch+5]) >> 2;
856                                 blockb = (b[0] + b[4] + b[inpitch+0] + b[inpitch+4]) >> 2;
857                                 yuv->u[x + yuv->uv_stride * (y/2)] =
858                                         cls.capturevideo.yuvnormalizetable[1][cls.capturevideo.rgbtoyuvscaletable[1][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[1][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[1][2][blockb] + 128];
859                                 yuv->v[x + yuv->uv_stride * (y/2)] =
860                                         cls.capturevideo.yuvnormalizetable[2][cls.capturevideo.rgbtoyuvscaletable[2][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[2][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[2][2][blockb] + 128];
861                                 b += 8;
862                         }
863                 }
864         }
865 }
866
867 static void SCR_CaptureVideo_Ogg_VideoFrames(int num)
868 {
869         LOAD_FORMATSPECIFIC_OGG();
870         ogg_packet pt;
871
872         // data is in cls.capturevideo.outbuffer as BGRA and has size width*height
873
874         if(format->yuvi >= 0)
875         {
876                 // send the previous frame
877                 while(format->lastnum-- > 0)
878                 {
879                         qtheora_encode_YUVin(&format->ts, &format->yuv[format->yuvi]);
880
881                         while(qtheora_encode_packetout(&format->ts, false, &pt))
882                                 qogg_stream_packetin(&format->to, &pt);
883
884                         SCR_CaptureVideo_Ogg_Interleave();
885                 }
886         }
887
888         format->yuvi = (format->yuvi + 1) % 2;
889         SCR_CaptureVideo_Ogg_ConvertFrame_BGRA_to_YUV();
890         format->lastnum = num;
891
892         // TODO maybe send num-1 frames from here already
893 }
894
895 typedef int channelmapping_t[8];
896 channelmapping_t mapping[8] =
897 {
898         { 0, -1, -1, -1, -1, -1, -1, -1 }, // mono
899         { 0, 1, -1, -1, -1, -1, -1, -1 }, // stereo
900         { 0, 1, 2, -1, -1, -1, -1, -1 }, // L C R
901         { 0, 1, 2, 3, -1, -1, -1, -1 }, // surround40
902         { 0, 2, 3, 4, 1, -1, -1, -1 }, // FL FC FR RL RR
903         { 0, 2, 3, 4, 1, 5, -1, -1 }, // surround51
904         { 0, 2, 3, 4, 1, 5, 6, -1 }, // (not defined by vorbis spec)
905         { 0, 2, 3, 4, 1, 5, 6, 7 } // surround71 (not defined by vorbis spec)
906 };
907
908 static void SCR_CaptureVideo_Ogg_SoundFrame(const portable_sampleframe_t *paintbuffer, size_t length)
909 {
910         LOAD_FORMATSPECIFIC_OGG();
911         float **vorbis_buffer;
912         size_t i;
913         int j;
914         ogg_packet pt;
915         int *map = mapping[bound(1, cls.capturevideo.soundchannels, 8) - 1];
916
917         vorbis_buffer = qvorbis_analysis_buffer(&format->vd, length);
918         for(j = 0; j < cls.capturevideo.soundchannels; ++j)
919         {
920                 float *b = vorbis_buffer[map[j]];
921                 for(i = 0; i < length; ++i)
922                         b[i] = paintbuffer[i].sample[j] / 32768.0f;
923         }
924         qvorbis_analysis_wrote(&format->vd, length);
925
926         while(qvorbis_analysis_blockout(&format->vd, &format->vb) == 1)
927         {
928                 qvorbis_analysis(&format->vb, NULL);
929                 qvorbis_bitrate_addblock(&format->vb);
930
931                 while(qvorbis_bitrate_flushpacket(&format->vd, &pt))
932                         qogg_stream_packetin(&format->vo, &pt);
933         }
934
935         SCR_CaptureVideo_Ogg_Interleave();
936 }
937
938 void SCR_CaptureVideo_Ogg_BeginVideo(void)
939 {
940         cls.capturevideo.format = CAPTUREVIDEOFORMAT_OGG_VORBIS_THEORA;
941         cls.capturevideo.formatextension = "ogv";
942         cls.capturevideo.videofile = FS_OpenRealFile(va("%s.%s", cls.capturevideo.basename, cls.capturevideo.formatextension), "wb", false);
943         cls.capturevideo.endvideo = SCR_CaptureVideo_Ogg_EndVideo;
944         cls.capturevideo.videoframes = SCR_CaptureVideo_Ogg_VideoFrames;
945         cls.capturevideo.soundframe = SCR_CaptureVideo_Ogg_SoundFrame;
946         cls.capturevideo.formatspecific = Mem_Alloc(tempmempool, sizeof(capturevideostate_ogg_formatspecific_t));
947         {
948                 LOAD_FORMATSPECIFIC_OGG();
949                 int num, denom, i;
950                 ogg_page pg;
951                 ogg_packet pt, pt2, pt3;
952                 theora_comment tc;
953                 vorbis_comment vc;
954                 theora_info ti;
955                 int vp3compat;
956
957                 format->serial1 = rand();
958                 qogg_stream_init(&format->to, format->serial1);
959
960                 if(cls.capturevideo.soundrate)
961                 {
962                         do
963                         {
964                                 format->serial2 = rand();
965                         }
966                         while(format->serial1 == format->serial2);
967                         qogg_stream_init(&format->vo, format->serial2);
968                 }
969
970                 format->videopage.len = format->audiopage.len = 0;
971
972                 qtheora_info_init(&ti);
973                 ti.frame_width = cls.capturevideo.width;
974                 ti.frame_height = cls.capturevideo.height;
975                 ti.width = (ti.frame_width + 15) & ~15;
976                 ti.height = (ti.frame_height + 15) & ~15;
977                 //ti.offset_x = ((ti.width - ti.frame_width) / 2) & ~1;
978                 //ti.offset_y = ((ti.height - ti.frame_height) / 2) & ~1;
979
980                 for(i = 0; i < 2; ++i)
981                 {
982                         format->yuv[i].y_width = ti.width;
983                         format->yuv[i].y_height = ti.height;
984                         format->yuv[i].y_stride = ti.width;
985                         format->yuv[i].uv_width = ti.width / 2;
986                         format->yuv[i].uv_height = ti.height / 2;
987                         format->yuv[i].uv_stride = ti.width / 2;
988                         format->yuv[i].y = (unsigned char *) Mem_Alloc(tempmempool, format->yuv[i].y_stride * format->yuv[i].y_height);
989                         format->yuv[i].u = (unsigned char *) Mem_Alloc(tempmempool, format->yuv[i].uv_stride * format->yuv[i].uv_height);
990                         format->yuv[i].v = (unsigned char *) Mem_Alloc(tempmempool, format->yuv[i].uv_stride * format->yuv[i].uv_height);
991                 }
992                 format->yuvi = -1; // -1: no frame valid yet, write into 0
993
994                 FindFraction(cls.capturevideo.framerate / cls.capturevideo.framestep, &num, &denom, 1001);
995                 ti.fps_numerator = num;
996                 ti.fps_denominator = denom;
997
998                 FindFraction(1 / vid_pixelheight.value, &num, &denom, 1000);
999                 ti.aspect_numerator = num;
1000                 ti.aspect_denominator = denom;
1001
1002                 ti.colorspace = OC_CS_UNSPECIFIED;
1003                 ti.pixelformat = OC_PF_420;
1004
1005                 ti.quick_p = true; // http://mlblog.osdir.com/multimedia.ogg.theora.general/2004-07/index.shtml
1006                 ti.dropframes_p = false;
1007
1008                 ti.target_bitrate = cl_capturevideo_ogg_theora_bitrate.integer * 1000;
1009                 ti.quality = cl_capturevideo_ogg_theora_quality.integer;
1010
1011                 if(ti.target_bitrate <= 0)
1012                 {
1013                         ti.target_bitrate = -1;
1014                         ti.keyframe_data_target_bitrate = (unsigned int)-1;
1015                 }
1016                 else
1017                 {
1018                         ti.keyframe_data_target_bitrate = (int) (ti.target_bitrate * max(1, cl_capturevideo_ogg_theora_keyframe_bitrate_multiplier.value));
1019
1020                         if(ti.target_bitrate < 45000 || ti.target_bitrate > 2000000)
1021                                 Con_DPrintf("WARNING: requesting an odd bitrate for theora (sensible values range from 45 to 2000 kbps)\n");
1022                 }
1023
1024                 if(ti.quality < 0 || ti.quality > 63)
1025                 {
1026                         ti.quality = 63;
1027                         if(ti.target_bitrate <= 0)
1028                         {
1029                                 ti.target_bitrate = 0x7FFFFFFF;
1030                                 ti.keyframe_data_target_bitrate = 0x7FFFFFFF;
1031                         }
1032                 }
1033
1034                 // this -1 magic is because ti.keyframe_frequency and ti.keyframe_mindistance use different metrics
1035                 ti.keyframe_frequency = bound(1, cl_capturevideo_ogg_theora_keyframe_maxinterval.integer, 1000);
1036                 ti.keyframe_mindistance = bound(1, cl_capturevideo_ogg_theora_keyframe_mininterval.integer, (int) ti.keyframe_frequency) - 1;
1037                 ti.noise_sensitivity = bound(0, cl_capturevideo_ogg_theora_noise_sensitivity.integer, 6);
1038                 ti.sharpness = bound(0, cl_capturevideo_ogg_theora_sharpness.integer, 2);
1039                 ti.keyframe_auto_threshold = bound(0, cl_capturevideo_ogg_theora_keyframe_auto_threshold.integer, 100);
1040
1041                 ti.keyframe_frequency_force = ti.keyframe_frequency;
1042                 ti.keyframe_auto_p = (ti.keyframe_frequency != ti.keyframe_mindistance + 1);
1043
1044                 qtheora_encode_init(&format->ts, &ti);
1045                 qtheora_info_clear(&ti);
1046
1047                 if(cl_capturevideo_ogg_theora_vp3compat.integer)
1048                 {
1049                         vp3compat = 1;
1050                         qtheora_control(&format->ts, TH_ENCCTL_SET_VP3_COMPATIBLE, &vp3compat, sizeof(vp3compat));
1051                         if(!vp3compat)
1052                                 Con_DPrintf("Warning: theora stream is not fully VP3 compatible\n");
1053                 }
1054
1055                 // vorbis?
1056                 if(cls.capturevideo.soundrate)
1057                 {
1058                         qvorbis_info_init(&format->vi);
1059                         qvorbis_encode_init_vbr(&format->vi, cls.capturevideo.soundchannels, cls.capturevideo.soundrate, bound(-1, cl_capturevideo_ogg_vorbis_quality.value, 10) * 0.099);
1060                         qvorbis_comment_init(&vc);
1061                         qvorbis_analysis_init(&format->vd, &format->vi);
1062                         qvorbis_block_init(&format->vd, &format->vb);
1063                 }
1064
1065                 qtheora_comment_init(&tc);
1066
1067                 /* create the remaining theora headers */
1068                 qtheora_encode_header(&format->ts, &pt);
1069                 qogg_stream_packetin(&format->to, &pt);
1070                 if (qogg_stream_pageout (&format->to, &pg) != 1)
1071                         fprintf (stderr, "Internal Ogg library error.\n");
1072                 FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
1073                 FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
1074
1075                 qtheora_encode_comment(&tc, &pt);
1076                 qogg_stream_packetin(&format->to, &pt);
1077                 qtheora_encode_tables(&format->ts, &pt);
1078                 qogg_stream_packetin (&format->to, &pt);
1079
1080                 qtheora_comment_clear(&tc);
1081
1082                 if(cls.capturevideo.soundrate)
1083                 {
1084                         qvorbis_analysis_headerout(&format->vd, &vc, &pt, &pt2, &pt3);
1085                         qogg_stream_packetin(&format->vo, &pt);
1086                         if (qogg_stream_pageout (&format->vo, &pg) != 1)
1087                                 fprintf (stderr, "Internal Ogg library error.\n");
1088                         FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
1089                         FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
1090
1091                         qogg_stream_packetin(&format->vo, &pt2);
1092                         qogg_stream_packetin(&format->vo, &pt3);
1093
1094                         qvorbis_comment_clear(&vc);
1095                 }
1096
1097                 for(;;)
1098                 {
1099                         int result = qogg_stream_flush (&format->to, &pg);
1100                         if (result < 0)
1101                                 fprintf (stderr, "Internal Ogg library error.\n"); // TODO Host_Error
1102                         if (result <= 0)
1103                                 break;
1104                         FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
1105                         FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
1106                 }
1107
1108                 if(cls.capturevideo.soundrate)
1109                 for(;;)
1110                 {
1111                         int result = qogg_stream_flush (&format->vo, &pg);
1112                         if (result < 0)
1113                                 fprintf (stderr, "Internal Ogg library error.\n"); // TODO Host_Error
1114                         if (result <= 0)
1115                                 break;
1116                         FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
1117                         FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len);
1118                 }
1119         }
1120 }