From: divverent Date: Wed, 18 Feb 2009 10:16:51 +0000 (+0000) Subject: forgot these two files X-Git-Tag: xonotic-v0.1.0preview~1872 X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=commitdiff_plain;h=624b2a6616c0e07574000dba59e0f46c8f12d8f9;ds=sidebyside forgot these two files git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8723 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cap_ogg.c b/cap_ogg.c new file mode 100644 index 00000000..410298d0 --- /dev/null +++ b/cap_ogg.c @@ -0,0 +1,902 @@ +#include + +#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 + * examples/encoder_example.c 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 index 00000000..287e96a0 --- /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();