/* =========================================================================== Copyright (C) 2008 Stefan Langer This file is part of Spearmint Source Code. Spearmint Source Code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Spearmint Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Spearmint Source Code. If not, see . In addition, Spearmint Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ /* This is a "ogm"-decoder to use a "better"(smaller files,higher resolutions) Cinematic-Format than roq In this code "ogm" is only: ogg wrapper, vorbis audio, xvid video (or theora video) (ogm(Ogg Media) in general is ogg wrapper with all kind of audio/video/subtitle/...) ... infos used for this src: xvid: * examples/xvid_decraw.c * xvid.h ogg/vobis: * decoder_example.c (libvorbis src) * libogg Documentation ( http://www.xiph.org/ogg/doc/libogg/ ) * VLC ogg demux ( http://trac.videolan.org/vlc/browser/trunk/modules/demux/ogg.c ) theora: * theora doxygen docs (1.0beta1) */ #if defined(USE_CODEC_VORBIS) && (defined(USE_CIN_XVID) || defined(USE_CIN_THEORA)) #include #include #ifdef USE_CIN_XVID #include #endif #ifdef USE_CIN_THEORA #include #endif #include "client.h" #include "snd_local.h" #define OGG_BUFFER_SIZE 8*1024 //4096 typedef struct { fileHandle_t ogmFile; ogg_sync_state oy; /* sync and verify incoming physical bitstream */ //ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */ ogg_stream_state os_audio; ogg_stream_state os_video; vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ vorbis_comment vc; /* struct that stores all the bitstream user comments */ qboolean videoStreamIsXvid; //FIXME: atm there isn't realy a check for this (all "video" streams are handelt as xvid, because xvid support more than one "subtype") #ifdef USE_CIN_XVID xvid_dec_stats_t xvid_dec_stats; void *xvid_dec_handle; #endif qboolean videoStreamIsTheora; #ifdef USE_CIN_THEORA theora_info th_info; // dump_video.c(example decoder): ti theora_comment th_comment; // dump_video.c(example decoder): tc theora_state th_state; // dump_video.c(example decoder): td yuv_buffer th_yuvbuffer; #endif unsigned char *outputBuffer; int outputWidht; int outputHeight; int outputBufferSize; // in Pixel (so "real Bytesize" = outputBufferSize*4) int VFrameCount; // output video-stream ogg_int64_t Vtime_unit; int currentTime; // input from Run-function } cin_ogm_t; static cin_ogm_t g_ogm; int nextNeededVFrame(void); /* ####################### ####################### XVID */ #ifdef USE_CIN_XVID #define BPP 4 static int init_xvid(void) { int ret; xvid_gbl_init_t xvid_gbl_init; xvid_dec_create_t xvid_dec_create; /* Reset the structure with zeros */ memset(&xvid_gbl_init, 0, sizeof(xvid_gbl_init_t)); memset(&xvid_dec_create, 0, sizeof(xvid_dec_create_t)); /* Version */ xvid_gbl_init.version = XVID_VERSION; xvid_gbl_init.cpu_flags = 0; xvid_gbl_init.debug = 0; xvid_global(NULL, 0, &xvid_gbl_init, NULL); /* Version */ xvid_dec_create.version = XVID_VERSION; /* * Image dimensions -- set to 0, xvidcore will resize when ever it is * needed */ xvid_dec_create.width = 0; xvid_dec_create.height = 0; ret = xvid_decore(NULL, XVID_DEC_CREATE, &xvid_dec_create, NULL); g_ogm.xvid_dec_handle = xvid_dec_create.handle; return (ret); } static int dec_xvid(unsigned char *input, int input_size) { int ret; xvid_dec_frame_t xvid_dec_frame; /* Reset all structures */ memset(&xvid_dec_frame, 0, sizeof(xvid_dec_frame_t)); memset(&g_ogm.xvid_dec_stats, 0, sizeof(xvid_dec_stats_t)); /* Set version */ xvid_dec_frame.version = XVID_VERSION; g_ogm.xvid_dec_stats.version = XVID_VERSION; /* No general flags to set */ xvid_dec_frame.general = XVID_LOWDELAY; //0; /* Input stream */ xvid_dec_frame.bitstream = input; xvid_dec_frame.length = input_size; /* Output frame structure */ xvid_dec_frame.output.plane[0] = g_ogm.outputBuffer; xvid_dec_frame.output.stride[0] = g_ogm.outputWidht * BPP; if(g_ogm.outputBuffer == NULL) xvid_dec_frame.output.csp = XVID_CSP_NULL; else xvid_dec_frame.output.csp = XVID_CSP_RGBA; // example was with XVID_CSP_I420 ret = xvid_decore(g_ogm.xvid_dec_handle, XVID_DEC_DECODE, &xvid_dec_frame, &g_ogm.xvid_dec_stats); return (ret); } static int shutdown_xvid(void) { int ret = 0; if(g_ogm.xvid_dec_handle) ret = xvid_decore(g_ogm.xvid_dec_handle, XVID_DEC_DESTROY, NULL, NULL); return (ret); } #endif /* ####################### ####################### OGG/OGM ... also calls to vorbis/theora-libs */ /* loadBlockToSync return: !0 -> no data transferred */ static int loadBlockToSync(void) { int r = -1; char *buffer; int bytes; if(g_ogm.ogmFile) { buffer = ogg_sync_buffer(&g_ogm.oy, OGG_BUFFER_SIZE); bytes = FS_Read(buffer, OGG_BUFFER_SIZE, g_ogm.ogmFile); ogg_sync_wrote(&g_ogm.oy, bytes); r = (bytes == 0); } return r; } /* loadPagesToStreams return: !0 -> no data transferred (or not for all Streams) */ static int loadPagesToStreams(void) { int r = -1; int AudioPages = 0; int VideoPages = 0; ogg_stream_state *osptr = NULL; ogg_page og; while(!AudioPages || !VideoPages) { if(ogg_sync_pageout(&g_ogm.oy, &og) != 1) break; if(g_ogm.os_audio.serialno == ogg_page_serialno(&og)) { osptr = &g_ogm.os_audio; ++AudioPages; } if(g_ogm.os_video.serialno == ogg_page_serialno(&og)) { osptr = &g_ogm.os_video; ++VideoPages; } if(osptr != NULL) { ogg_stream_pagein(osptr, &og); } } if(AudioPages && VideoPages) r = 0; return r; } #define SIZEOF_RAWBUFF 4*1024 static byte rawBuffer[SIZEOF_RAWBUFF]; #define MIN_AUDIO_PRELOAD 400 // in ms #define MAX_AUDIO_PRELOAD 500 // in ms /* return: audio wants more packets */ static qboolean loadAudio(void) { qboolean anyDataTransferred = qtrue; float **pcm; float *right, *left; int samples, samplesNeeded; int i; short *ptr; ogg_packet op; vorbis_block vb; memset(&op, 0, sizeof(op)); memset(&vb, 0, sizeof(vb)); vorbis_block_init(&g_ogm.vd, &vb); while(anyDataTransferred && g_ogm.currentTime + MAX_AUDIO_PRELOAD > (int)(g_ogm.vd.granulepos * 1000 / g_ogm.vi.rate)) { anyDataTransferred = qfalse; if((samples = vorbis_synthesis_pcmout(&g_ogm.vd, &pcm)) > 0) { // vorbis -> raw ptr = (short *)rawBuffer; samplesNeeded = (SIZEOF_RAWBUFF) / (2 * 2); // (width*channel) if(samples < samplesNeeded) samplesNeeded = samples; left = pcm[0]; right = (g_ogm.vi.channels > 1) ? pcm[1] : pcm[0]; for(i = 0; i < samplesNeeded; ++i) { ptr[0] = (left[i] >= -1.0f && left[i] <= 1.0f) ? left[i] * 32767.f : 32767 * ((left[i] > 0.0f) - (left[i] < 0.0f)); ptr[1] = (right[i] >= -1.0f && right[i] <= 1.0f) ? right[i] * 32767.f : 32767 * ((right[i] > 0.0f) - (right[i] < 0.0f)); ptr += 2; //numChans; } if(i > 0) { // tell libvorbis how many samples we actually consumed vorbis_synthesis_read(&g_ogm.vd, i); // S_RawSamples(ssize, 22050, 2, 2, (byte *)sbuf, 1.0f, -1); S_RawSamples(0, i, g_ogm.vi.rate, 2, 2, rawBuffer, 1.0f, -1); anyDataTransferred = qtrue; } } if(!anyDataTransferred) { // op -> vorbis if(ogg_stream_packetout(&g_ogm.os_audio, &op)) { if(vorbis_synthesis(&vb, &op) == 0) vorbis_synthesis_blockin(&g_ogm.vd, &vb); anyDataTransferred = qtrue; } } } vorbis_block_clear(&vb); if(g_ogm.currentTime + MIN_AUDIO_PRELOAD > (int)(g_ogm.vd.granulepos * 1000 / g_ogm.vi.rate)) return qtrue; else return qfalse; } /* return: 1 -> loaded a new Frame ( g_ogm.outputBuffer points to the actual frame ) 0 -> no new Frame <0 -> error */ #ifdef USE_CIN_XVID static int loadVideoFrameXvid(void) { int r = 0; ogg_packet op; int used_bytes = 0; memset(&op, 0, sizeof(op)); while(!r && (ogg_stream_packetout(&g_ogm.os_video, &op))) { used_bytes = dec_xvid(op.packet, op.bytes); if(g_ogm.xvid_dec_stats.type == XVID_TYPE_VOL) { if(g_ogm.outputWidht != g_ogm.xvid_dec_stats.data.vol.width || g_ogm.outputHeight != g_ogm.xvid_dec_stats.data.vol.height) { g_ogm.outputWidht = g_ogm.xvid_dec_stats.data.vol.width; g_ogm.outputHeight = g_ogm.xvid_dec_stats.data.vol.height; Com_DPrintf("[XVID]new resolution %dx%d\n", g_ogm.outputWidht, g_ogm.outputHeight); } if(g_ogm.outputBufferSize < g_ogm.xvid_dec_stats.data.vol.width * g_ogm.xvid_dec_stats.data.vol.height) { g_ogm.outputBufferSize = g_ogm.xvid_dec_stats.data.vol.width * g_ogm.xvid_dec_stats.data.vol.height; /* Free old output buffer */ if(g_ogm.outputBuffer) free(g_ogm.outputBuffer); /* Allocate the new buffer */ g_ogm.outputBuffer = (unsigned char *)malloc(g_ogm.outputBufferSize * 4); //FIXME? should the 4 stay for BPP? if(g_ogm.outputBuffer == NULL) { g_ogm.outputBufferSize = 0; r = -2; break; } } // use the rest of this packet used_bytes += dec_xvid(op.packet + used_bytes, op.bytes - used_bytes); } // we got a real output frame ... if(g_ogm.xvid_dec_stats.type > 0) { r = 1; ++g_ogm.VFrameCount; // Com_Printf("frame infos: %d %d %d\n", xvid_dec_stats.data.vop.general, xvid_dec_stats.data.vop.time_base, xvid_dec_stats.data.vop.time_increment); // Com_Printf("frame info time: %d (Frame# %d, %d)\n", xvid_dec_stats.data.vop.time_base, VFrameCount, (int)(VFrameCount*Vtime_unit/10000000)); } // if((op.bytes-used_bytes)>0) // Com_Printf("unused: %d(firstChar: %X)\n",(op.bytes-used_bytes),(int)(op.packet[used_bytes])); } return r; } #endif /* return: 1 -> loaded a new Frame ( g_ogm.outputBuffer points to the actual frame ) 0 -> no new Frame <0 -> error */ #ifdef USE_CIN_THEORA /* how many >> are needed to make y==x (shifting y>>i) return: -1 -> no match >=0 -> number of shifts */ static int findSizeShift(int x, int y) { int i; for(i = 0; (y >> i); ++i) if(x == (y >> i)) return i; return -1; } static int loadVideoFrameTheora(void) { int r = 0; ogg_packet op; memset(&op, 0, sizeof(op)); while(!r && (ogg_stream_packetout(&g_ogm.os_video, &op))) { ogg_int64_t th_frame; theora_decode_packetin(&g_ogm.th_state, &op); th_frame = theora_granule_frame(&g_ogm.th_state, g_ogm.th_state.granulepos); if((g_ogm.VFrameCount < th_frame && th_frame >= nextNeededVFrame()) || !g_ogm.outputBuffer) { // int i,j; int yWShift, uvWShift; int yHShift, uvHShift; if(theora_decode_YUVout(&g_ogm.th_state, &g_ogm.th_yuvbuffer)) continue; if(g_ogm.outputWidht != g_ogm.th_info.width || g_ogm.outputHeight != g_ogm.th_info.height) { g_ogm.outputWidht = g_ogm.th_info.width; g_ogm.outputHeight = g_ogm.th_info.height; Com_DPrintf("[Theora(ogg)]new resolution %dx%d\n", g_ogm.outputWidht, g_ogm.outputHeight); } if(g_ogm.outputBufferSize < g_ogm.th_info.width * g_ogm.th_info.height) { g_ogm.outputBufferSize = g_ogm.th_info.width * g_ogm.th_info.height; /* Free old output buffer */ if(g_ogm.outputBuffer) free(g_ogm.outputBuffer); /* Allocate the new buffer */ g_ogm.outputBuffer = (unsigned char *)malloc(g_ogm.outputBufferSize * 4); if(g_ogm.outputBuffer == NULL) { g_ogm.outputBufferSize = 0; r = -2; break; } } yWShift = findSizeShift(g_ogm.th_yuvbuffer.y_width, g_ogm.th_info.width); uvWShift = findSizeShift(g_ogm.th_yuvbuffer.uv_width, g_ogm.th_info.width); yHShift = findSizeShift(g_ogm.th_yuvbuffer.y_height, g_ogm.th_info.height); uvHShift = findSizeShift(g_ogm.th_yuvbuffer.uv_height, g_ogm.th_info.height); if(yWShift < 0 || uvWShift < 0 || yHShift < 0 || uvHShift < 0) { Com_Printf("[Theora] unexpected resolution in a yuv-Frame\n"); r = -1; } else { Frame_yuv_to_rgb24(g_ogm.th_yuvbuffer.y, g_ogm.th_yuvbuffer.u, g_ogm.th_yuvbuffer.v, g_ogm.th_info.width, g_ogm.th_info.height, g_ogm.th_yuvbuffer.y_stride, g_ogm.th_yuvbuffer.uv_stride, yWShift, uvWShift, yHShift, uvHShift, (unsigned int *)g_ogm.outputBuffer); /* unsigned char* pixelPtr = g_ogm.outputBuffer; unsigned int* pixPtr; pixPtr = (unsigned int*)g_ogm.outputBuffer; //TODO: use one yuv->rgb funktion for the hole frame (the big amout of stack movement(yuv->rgb calls) couldn't be good ;) ) for(j=0;jrgb code *pixPtr++ = yuv_to_rgb24( g_ogm.th_yuvbuffer.y[(i>>yWShift)+(j>>yHShift)*g_ogm.th_yuvbuffer.y_stride], g_ogm.th_yuvbuffer.u[(i>>uvWShift)+(j>>uvHShift)*g_ogm.th_yuvbuffer.uv_stride], g_ogm.th_yuvbuffer.v[(i>>uvWShift)+(j>>uvHShift)*g_ogm.th_yuvbuffer.uv_stride]); #endif } } */ r = 1; g_ogm.VFrameCount = th_frame; } } } return r; } #endif /* return: 1 -> loaded a new Frame ( g_ogm.outputBuffer points to the actual frame ) 0 -> no new Frame <0 -> error */ static int loadVideoFrame(void) { #ifdef USE_CIN_XVID if(g_ogm.videoStreamIsXvid) return loadVideoFrameXvid(); #endif #ifdef USE_CIN_THEORA if(g_ogm.videoStreamIsTheora) return loadVideoFrameTheora(); #endif // if we come to this point, there will be no codec that use the stream content ... if(g_ogm.os_video.serialno) { ogg_packet op; while(ogg_stream_packetout(&g_ogm.os_video, &op)); } return 1; } /* return: qtrue => noDataTransferred */ static qboolean loadFrame(void) { qboolean anyDataTransferred = qtrue; qboolean needVOutputData = qtrue; // qboolean audioSDone = qfalse; // qboolean videoSDone = qfalse; qboolean audioWantsMoreData = qfalse; int status; while(anyDataTransferred && (needVOutputData || audioWantsMoreData)) { anyDataTransferred = qfalse; // xvid -> "gl" ? videoDone : needPacket // vorbis -> raw sound ? audioDone : needPacket // anyDataTransferred = videoDone && audioDone; // needVOutputData = videoDone && audioDone; // if needPacket { // videoStream -> xvid ? videoStreamDone : needPage // audioSteam -> vorbis ? audioStreamDone : needPage // anyDataTransferred = audioStreamDone && audioStreamDone; if(needVOutputData && (status = loadVideoFrame())) { needVOutputData = qfalse; if(status > 0) anyDataTransferred = qtrue; else anyDataTransferred = qfalse; // error (we don't need any videodata and we had no transferred) } // if needPage if(needVOutputData || audioWantsMoreData) { // try to transfer Pages to the audio- and video-Stream if(loadPagesToStreams()) { // try to load a datablock from file anyDataTransferred |= !loadBlockToSync(); } else anyDataTransferred = qtrue; // successful loadPagesToStreams() } // load all Audio after loading new pages ... if(g_ogm.VFrameCount > 1) // wait some videoframes (it's better to have some delay, than a lagy sound) audioWantsMoreData = loadAudio(); } } // ogg_packet_clear(&op); return !anyDataTransferred; } //from VLC ogg.c ( http://trac.videolan.org/vlc/browser/trunk/modules/demux/ogg.c ) typedef struct { char streamtype[8]; char subtype[4]; ogg_int32_t size; /* size of the structure */ ogg_int64_t time_unit; /* in reference time */// in 10^-7 seconds (dT between frames) ogg_int64_t samples_per_unit; ogg_int32_t default_len; /* in media time */ ogg_int32_t buffersize; ogg_int16_t bits_per_sample; union { struct { ogg_int32_t width; ogg_int32_t height; } stream_header_video; struct { ogg_int16_t channels; ogg_int16_t blockalign; ogg_int32_t avgbytespersec; } stream_header_audio; } sh; } stream_header_t; qboolean isPowerOf2(int x) { int bitsSet = 0; int i; for(i = 0; i < sizeof(int) * 8; ++i) if(x & (1 << i)) ++bitsSet; return (bitsSet <= 1); } /* return: 0 -> no problem */ //TODO: vorbis/theora-header&init in sub-functions //TODO: "clean" error-returns ... int Cin_OGM_Init(const char *filename) { int status; ogg_page og; ogg_packet op; int i; if(g_ogm.ogmFile) { Com_Printf(S_COLOR_YELLOW "WARNING: it seams there was already a ogm running, it will be killed to start %s\n", filename); Cin_OGM_Shutdown(); } memset(&g_ogm, 0, sizeof(cin_ogm_t)); FS_FOpenFileRead(filename, &g_ogm.ogmFile, qtrue); if(!g_ogm.ogmFile) { Com_Printf(S_COLOR_YELLOW "WARNING: Can't open ogm-file for reading (%s)\n", filename); return -1; } ogg_sync_init(&g_ogm.oy); /* Now we can read pages */ //FIXME? can serialno be 0 in ogg? (better way to check inited?) //TODO: support for more than one audio stream? / detect files with one stream(or without correct ones) while(!g_ogm.os_audio.serialno || !g_ogm.os_video.serialno) { if(ogg_sync_pageout(&g_ogm.oy, &og) == 1) { if(strstr((char *)(og.body + 1), "vorbis")) { //FIXME? better way to find audio stream if(g_ogm.os_audio.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: more than one audio stream, in ogm-file(%s) ... we will stay at the first one\n", filename); } else { ogg_stream_init(&g_ogm.os_audio, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm.os_audio, &og); } } #ifdef USE_CIN_THEORA if(strstr((char *)(og.body + 1), "theora")) { if(g_ogm.os_video.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: more than one video stream, in ogm-file(%s) ... we will stay at the first one\n", filename); } else { g_ogm.videoStreamIsTheora = qtrue; ogg_stream_init(&g_ogm.os_video, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm.os_video, &og); } } #endif #ifdef USE_CIN_XVID if(strstr((char *)(og.body + 1), "video")) { //FIXME? better way to find video stream if(g_ogm.os_video.serialno) { Com_Printf("more than one video stream, in ogm-file(%s) ... we will stay at the first one\n", filename); } else { stream_header_t *sh; g_ogm.videoStreamIsXvid = qtrue; sh = (stream_header_t *) (og.body + 1); //TODO: one solution for checking xvid and theora if(!isPowerOf2(sh->sh.stream_header_video.width)) { Com_Printf("VideoWidth of the ogm-file isn't a power of 2 value (%s)\n", filename); return -5; } if(!isPowerOf2(sh->sh.stream_header_video.height)) { Com_Printf("VideoHeight of the ogm-file isn't a power of 2 value (%s)\n", filename); return -6; } g_ogm.Vtime_unit = sh->time_unit; ogg_stream_init(&g_ogm.os_video, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm.os_video, &og); } } #endif } else if(loadBlockToSync()) break; } if(g_ogm.videoStreamIsXvid && g_ogm.videoStreamIsTheora) { Com_Printf(S_COLOR_YELLOW "WARNING: Found \"video\"- and \"theora\"-stream ,ogm-file (%s)\n", filename); return -2; } #if 1 if(!g_ogm.os_audio.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: Haven't found a audio(vorbis) stream in ogm-file (%s)\n", filename); return -2; } #endif if(!g_ogm.os_video.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: Haven't found a video stream in ogm-file (%s)\n", filename); return -3; } //load vorbis header vorbis_info_init(&g_ogm.vi); vorbis_comment_init(&g_ogm.vc); i = 0; while(i < 3) { status = ogg_stream_packetout(&g_ogm.os_audio, &op); if(status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: Corrupt ogg packet while loading vorbis-headers, ogm-file(%s)\n", filename); return -8; } if(status > 0) { status = vorbis_synthesis_headerin(&g_ogm.vi, &g_ogm.vc, &op); if(i == 0 && status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: This Ogg bitstream does not contain Vorbis audio data, ogm-file(%s)\n", filename); return -9; } ++i; } else if(loadPagesToStreams()) { if(loadBlockToSync()) { Com_Printf(S_COLOR_YELLOW "WARNING: Couldn't find all vorbis headers before end of ogm-file (%s)\n", filename); return -10; } } } vorbis_synthesis_init(&g_ogm.vd, &g_ogm.vi); #ifdef USE_CIN_XVID status = init_xvid(); if(status) { Com_Printf("[Xvid]Decore INIT problem, return value %d(ogm-file: %s)\n", status, filename); return -4; } #endif #ifdef USE_CIN_THEORA if(g_ogm.videoStreamIsTheora) { ROQ_GenYUVTables(); theora_info_init(&g_ogm.th_info); theora_comment_init(&g_ogm.th_comment); i = 0; while(i < 3) { status = ogg_stream_packetout(&g_ogm.os_video, &op); if(status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: Corrupt ogg packet while loading theora-headers, ogm-file(%s)\n", filename); return -8; } if(status > 0) { status = theora_decode_header(&g_ogm.th_info, &g_ogm.th_comment, &op); if(i == 0 && status != 0) { Com_Printf(S_COLOR_YELLOW "WARNING: This Ogg bitstream does not contain theora data, ogm-file(%s)\n", filename); return -9; } ++i; } else if(loadPagesToStreams()) { if(loadBlockToSync()) { Com_Printf(S_COLOR_YELLOW "WARNING: Couldn't find all theora headers before end of ogm-file (%s)\n", filename); return -10; } } } theora_decode_init(&g_ogm.th_state, &g_ogm.th_info); if(!isPowerOf2(g_ogm.th_info.width)) { Com_Printf(S_COLOR_YELLOW "WARNING: VideoWidth of the ogm-file isn't a power of 2 value (%s)\n", filename); return -5; } if(!isPowerOf2(g_ogm.th_info.height)) { Com_Printf(S_COLOR_YELLOW "WARNING: VideoHeight of the ogm-file isn't a power of 2 value (%s)\n", filename); return -6; } g_ogm.Vtime_unit = ((ogg_int64_t) g_ogm.th_info.fps_denominator * 1000 * 10000 / g_ogm.th_info.fps_numerator); } #endif Com_DPrintf("OGM-Init done (%s)\n", filename); return 0; } int nextNeededVFrame(void) { return (int)(g_ogm.currentTime * (ogg_int64_t) 10000 / g_ogm.Vtime_unit); } /* time ~> time in ms to which the movie should run return: 0 => nothing special 1 => eof */ int Cin_OGM_Run(int time) { g_ogm.currentTime = time; while(!g_ogm.VFrameCount || time + 20 >= (int)(g_ogm.VFrameCount * g_ogm.Vtime_unit / 10000)) { if(loadFrame()) return 1; } return 0; } /* Gives a Pointer to the current Output-Buffer and the Resolution */ unsigned char *Cin_OGM_GetOutput(int *outWidth, int *outHeight) { if(outWidth != NULL) *outWidth = g_ogm.outputWidht; if(outHeight != NULL) *outHeight = g_ogm.outputHeight; return g_ogm.outputBuffer; } void Cin_OGM_Shutdown() { #ifdef USE_CIN_XVID int status; status = shutdown_xvid(); if(status) Com_Printf("[Xvid]Decore RELEASE problem, return value %d\n", status); #endif #ifdef USE_CIN_THEORA theora_clear(&g_ogm.th_state); theora_comment_clear(&g_ogm.th_comment); theora_info_clear(&g_ogm.th_info); #endif if(g_ogm.outputBuffer) free(g_ogm.outputBuffer); g_ogm.outputBuffer = NULL; vorbis_dsp_clear(&g_ogm.vd); vorbis_comment_clear(&g_ogm.vc); vorbis_info_clear(&g_ogm.vi); /* must be called last (comment from vorbis example code) */ ogg_stream_clear(&g_ogm.os_audio); ogg_stream_clear(&g_ogm.os_video); ogg_sync_clear(&g_ogm.oy); FS_FCloseFile(g_ogm.ogmFile); g_ogm.ogmFile = 0; } #else int Cin_OGM_Init(const char *filename) { return 1; } int Cin_OGM_Run(int time) { return 1; } unsigned char *Cin_OGM_GetOutput(int *outWidth, int *outHeight) { return 0; } void Cin_OGM_Shutdown(void) { return; } #endif