diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 7d8541451..9fa03514f 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "glquake.h"//would prefer not to have this #endif #include "shader.h" +#include "gl_draw.h" //name of the current backdrop for the loading screen char levelshotname[MAX_QPATH]; @@ -237,10 +238,15 @@ cvar_t show_gameclock_y = CVAR("cl_gameclock_y", "-1"); cvar_t show_speed = CVAR("show_speed", "0"); cvar_t show_speed_x = CVAR("show_speed_x", "-1"); cvar_t show_speed_y = CVAR("show_speed_y", "-9"); -cvar_t scr_loadingrefresh = CVAR("scr_loadingrefresh", "0"); -cvar_t scr_showloading = CVAR("scr_showloading", "1"); cvar_t scr_showobituaries = CVAR("scr_showobituaries", "0"); +cvar_t scr_loadingrefresh = CVARD("scr_loadingrefresh", "0", "Force redrawing of the loading screen, in order to display EVERY resource that is loaded"); +cvar_t scr_showloading = CVAR("scr_showloading", "1"); +//things to configure the legacy loading screen +cvar_t scr_loadingscreen_picture = CVAR("scr_loadingscreen_picture", "gfx/loading"); +cvar_t scr_loadingscreen_scale = CVAR("scr_loadingscreen_scale", "1"); +cvar_t scr_loadingscreen_scale_limit = CVAR("scr_loadingscreen_scale_limit", "2"); + void *scr_curcursor; @@ -261,6 +267,9 @@ void CLSCR_Init(void) Cvar_Register(&con_stayhidden, cl_screengroup); Cvar_Register(&scr_loadingrefresh, cl_screengroup); Cvar_Register(&scr_showloading, cl_screengroup); + Cvar_Register(&scr_loadingscreen_picture, cl_screengroup); + Cvar_Register(&scr_loadingscreen_scale, cl_screengroup); + Cvar_Register(&scr_loadingscreen_scale_limit, cl_screengroup); Cvar_Register(&show_fps, cl_screengroup); Cvar_Register(&show_fps_x, cl_screengroup); Cvar_Register(&show_fps_y, cl_screengroup); @@ -1645,6 +1654,9 @@ void SCR_DrawFPS (void) switch (sfps) { + case 1: + default: + break; case 2: // lowest FPS, highest MS encountered if (lastfps > 1/frametime) { @@ -1856,8 +1868,6 @@ void SCR_DrawLoading (qboolean opaque) int sizex, x, y, w, h; mpic_t *pic; char *s; - int qdepth; - int h2depth; if (CSQC_UseGamecodeLoadingScreen()) return; //will be drawn as part of the regular screen updates @@ -1876,73 +1886,29 @@ void SCR_DrawLoading (qboolean opaque) if (*levelshotname) { pic = R2D_SafeCachePic (levelshotname); - R_GetShaderSizes(pic, NULL, NULL, true); - R2D_ImageColours(1, 1, 1, 1); - R2D_ScalePic (0, 0, vid.width, vid.height, pic); + if (!R_GetShaderSizes(pic, &w, &h, true)) + w = h = 1; + R2D_Letterbox(0, 0, vid.width, vid.height, pic, w, h); } else if (opaque) + { + R2D_ImageColours(0, 0, 0, 1); + R2D_FillBlock(0, 0, vid.width, vid.height); + R2D_ImageColours(1, 1, 1, 1); R2D_ConsoleBackground (0, vid.height, true); + } if (scr_showloading.ival) { - qdepth = COM_FDepthFile("gfx/loading.lmp", true); - h2depth = COM_FDepthFile("gfx/menu/loading.lmp", true); + char *qname = scr_loadingscreen_picture.string; - if (qdepth < h2depth || h2depth > 0xffffff) - { //quake files +#ifdef HEXEN2 + int qdepth = COM_FDepthFile(qname, true); + int h2depth = COM_FDepthFile("gfx/menu/loading.lmp", true); - pic = R2D_SafeCachePic ("gfx/loading.lmp"); - if (R_GetShaderSizes(pic, &w, &h, true)) - { - x = (vid.width - w)/2; - y = (vid.height - 48 - h)/2; - R2D_ScalePic (x, y, w, h, pic); - x = (vid.width/2) - 96; - y += h + 8; - } - else - { - x = (vid.width/2) - 96; - y = (vid.height/2) - 8; - - Draw_FunString((vid.width-7*8)/2, y-16, "Loading"); - } - - if (!total_loading_size) - total_loading_size = 1; - if (loading_stage > LS_CONNECTION) - { - sizex = current_loading_size * 192 / total_loading_size; - if (loading_stage == LS_SERVER) - { - R2D_ImageColours(1.0, 0.0, 0.0, 1.0); - R2D_FillBlock(x, y, sizex, 16); - R2D_ImageColours(0.0, 0.0, 0.0, 1.0); - R2D_FillBlock(x+sizex, y, 192-sizex, 16); - } - else - { - R2D_ImageColours(1.0, 1.0, 0.0, 1.0); - R2D_FillBlock(x, y, sizex, 16); - R2D_ImageColours(1.0, 0.0, 0.0, 1.0); - R2D_FillBlock(x+sizex, y, 192-sizex, 16); - } - - R2D_ImageColours(1, 1, 1, 1); - Draw_FunString(x+8, y+4, va("Loading %s... %i%%", - (loading_stage == LS_SERVER) ? "server" : "client", - current_loading_size * 100 / total_loading_size)); - } - y += 16; - - if (loadingfile) - { - Draw_FunString(x+8, y+4, loadingfile); - y+=8; - } - } - else - { //hexen2 files + if (!(qdepth < h2depth || h2depth > 0xffffff)) + { //hexen2 files. + //hexen2 has some fancy sliders built into its graphics in specific places. so this is messy. pic = R2D_SafeCachePic ("gfx/menu/loading.lmp"); if (R_GetShaderSizes(pic, &w, &h, true)) { @@ -1987,6 +1953,88 @@ void SCR_DrawLoading (qboolean opaque) y = 104; } } + else +#endif + { //quake files + pic = R2D_SafeCachePic (qname); + if (R_GetShaderSizes(pic, &w, &h, true)) + { + float f = 1; + w *= scr_loadingscreen_scale.value; + h *= scr_loadingscreen_scale.value; + switch(scr_loadingscreen_scale_limit.ival) + { + default: + break; + case 1: + f = max(w/vid.width,h/vid.height); + break; + case 2: + f = min(w/vid.width,h/vid.height); + break; + case 3: + f = w/vid.width; + break; + case 4: + f = h/vid.height; + break; + } + if (f > 1) + { + w /= f; + h /= f; + } + x = ((int)vid.width - w)/2; + y = ((int)vid.height - 48 - h)/2; + R2D_ScalePic (x, y, w, h, pic); + x = (vid.width/2) - 96; + y += h + 8; + + //make sure our loading crap will be displayed. + if (y > vid.height-32) + y = vid.height-32; + } + else + { + x = (vid.width/2) - 96; + y = (vid.height/2) - 8; + + Draw_FunString((vid.width-7*8)/2, y-16, "Loading"); + } + + if (!total_loading_size) + total_loading_size = 1; + if (loading_stage > LS_CONNECTION) + { + sizex = current_loading_size * 192 / total_loading_size; + if (loading_stage == LS_SERVER) + { + R2D_ImageColours(1.0, 0.0, 0.0, 1.0); + R2D_FillBlock(x, y, sizex, 16); + R2D_ImageColours(0.0, 0.0, 0.0, 1.0); + R2D_FillBlock(x+sizex, y, 192-sizex, 16); + } + else + { + R2D_ImageColours(1.0, 1.0, 0.0, 1.0); + R2D_FillBlock(x, y, sizex, 16); + R2D_ImageColours(1.0, 0.0, 0.0, 1.0); + R2D_FillBlock(x+sizex, y, 192-sizex, 16); + } + + R2D_ImageColours(1, 1, 1, 1); + Draw_FunString(x+8, y+4, va("Loading %s... %i%%", + (loading_stage == LS_SERVER) ? "server" : "client", + current_loading_size * 100 / total_loading_size)); + } + y += 16; + + if (loadingfile) + { + Draw_FunString(x+8, y+4, loadingfile); + y+=16; + } + } } R2D_ImageColours(1, 1, 1, 1); @@ -2096,32 +2144,12 @@ void SCR_ImageName (const char *mapname) return; } - if (!scr_disabled_for_loading) + scr_drawloading = true; + if (qrenderer != QR_NONE) { - scr_drawloading = true; - if (qrenderer != QR_NONE) - { - Sbar_Changed (); - SCR_UpdateScreen (); - } - scr_drawloading = false; - scr_disabled_for_loading = true; + Sbar_Changed (); + SCR_UpdateScreen (); } - else - { - scr_disabled_for_loading = false; - scr_drawloading = true; -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL) - { - SCR_DrawLoading(false); - SCR_SetUpToDrawConsole(); - if (Key_Dest_Has(kdm_console) || !*levelshotname) - SCR_DrawConsole(!!*levelshotname); - } -#endif - } - scr_drawloading = false; scr_disabled_time = Sys_DoubleTime(); //realtime tends to change... Hmmm.... scr_disabled_for_loading = true; diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index f46cc77d3..8a254402b 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -10,6 +10,7 @@ #include "../vk/vkrenderer.h" #endif #include "shader.h" +#include "gl_draw.h" #if defined(HAVE_JUKEBOX) #if defined(_WIN32) && !defined(WINRT) && !defined(NOMEDIAMENU) @@ -2569,7 +2570,7 @@ qboolean Media_ShowFilm(void) else if (cin) { int cw, ch; - float ratiox, ratioy, aspect; + float aspect; if (cin->cursormove) cin->cursormove(cin, mousecursor_x/(float)vid.width, mousecursor_y/(float)vid.height); if (cin->setsize) @@ -2584,40 +2585,7 @@ qboolean Media_ShowFilm(void) ch = 3; } - ratiox = (float)cw / vid.pixelwidth; - ratioy = (float)ch / vid.pixelheight; - - if (!ch || !cw) - { - R2D_ImageColours(0, 0, 0, 1); - R2D_FillBlock(0, 0, vid.width, vid.height); - R2D_ScalePic(0, 0, 0, 0, videoshader); - } - else if (ratiox > ratioy) - { - int h = (vid.width * ch) / cw; - int p = vid.height - h; - - //letterbox - R2D_ImageColours(0, 0, 0, 1); - R2D_FillBlock(0, 0, vid.width, p/2); - R2D_FillBlock(0, h + (p/2), vid.width, p/2); - - R2D_ImageColours(1, 1, 1, 1); - R2D_ScalePic(0, p/2, vid.width, h, videoshader); - } - else - { - int w = (vid.height * cw) / ch; - int p = vid.width - w; - //sidethingies - R2D_ImageColours(0, 0, 0, 1); - R2D_FillBlock(0, 0, (p/2), vid.height); - R2D_FillBlock(w + (p/2), 0, p/2, vid.height); - - R2D_ImageColours(1, 1, 1, 1); - R2D_ScalePic(p/2, 0, w, vid.height, videoshader); - } + R2D_Letterbox(0, 0, vid.pixelwidth, vid.pixelheight, videoshader, cw, ch); SCR_SetUpToDrawConsole(); if (scr_con_current) @@ -4892,72 +4860,75 @@ sfxcache_t *QDECL S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, extern cvar_t snd_linearresample_stream; int framesz = (dec->srcwidth/8 * dec->srcchannels); - if (dec->dststart > start) + if (length) { - /*I don't know where the compressed data is for each sample. acm doesn't have a seek. so reset to start, for music this should be the most common rewind anyway*/ - dec->dststart = 0; - dec->dstcount = 0; - dec->srcoffset = 0; - } - - if (dec->dstcount > snd_speed*6) - { - int trim = dec->dstcount - snd_speed; //retain a second of buffer in case we have multiple sound devices - if (dec->dststart + trim > start) + if (dec->dststart > start) { - trim = start - dec->dststart; - if (trim < 0) - trim = 0; - } -// if (trim < 0) -// trim = 0; -/// if (trim > dec->dstcount) -// trim = dec->dstcount; - memmove(dec->dstdata, dec->dstdata + trim*framesz, (dec->dstcount - trim)*framesz); - dec->dststart += trim; - dec->dstcount -= trim; - } - - while(start+length >= dec->dststart+dec->dstcount) - { - memset(&strhdr, 0, sizeof(strhdr)); - strhdr.cbStruct = sizeof(strhdr); - strhdr.pbSrc = dec->srcdata + dec->srcoffset; - strhdr.cbSrcLength = dec->srclen - dec->srcoffset; - if (!strhdr.cbSrcLength) - break; - strhdr.pbDst = buffer; - strhdr.cbDstLength = sizeof(buffer); - - qacmStreamPrepareHeader(dec->acm, &strhdr, 0); - qacmStreamConvert(dec->acm, &strhdr, ACM_STREAMCONVERTF_BLOCKALIGN); - qacmStreamUnprepareHeader(dec->acm, &strhdr, 0); - dec->srcoffset += strhdr.cbSrcLengthUsed; - if (!strhdr.cbDstLengthUsed) - { - if (strhdr.cbSrcLengthUsed) - continue; - break; + /*I don't know where the compressed data is for each sample. acm doesn't have a seek. so reset to start, for music this should be the most common rewind anyway*/ + dec->dststart = 0; + dec->dstcount = 0; + dec->srcoffset = 0; } - newlen = dec->dstcount + (strhdr.cbDstLengthUsed * ((float)snd_speed / dec->srcspeed))/framesz; - if (dec->dstbuffer < newlen+64) + if (dec->dstcount > snd_speed*6) { - dec->dstbuffer = newlen+64 + snd_speed; - dec->dstdata = BZ_Realloc(dec->dstdata, dec->dstbuffer*framesz); + int trim = dec->dstcount - snd_speed; //retain a second of buffer in case we have multiple sound devices + if (dec->dststart + trim > start) + { + trim = start - dec->dststart; + if (trim < 0) + trim = 0; + } + // if (trim < 0) + // trim = 0; + /// if (trim > dec->dstcount) + // trim = dec->dstcount; + memmove(dec->dstdata, dec->dstdata + trim*framesz, (dec->dstcount - trim)*framesz); + dec->dststart += trim; + dec->dstcount -= trim; } - SND_ResampleStream(strhdr.pbDst, - dec->srcspeed, - dec->srcwidth/8, - dec->srcchannels, - strhdr.cbDstLengthUsed / framesz, - dec->dstdata+dec->dstcount*framesz, - snd_speed, - dec->srcwidth/8, - dec->srcchannels, - snd_linearresample_stream.ival); - dec->dstcount = newlen; + while(start+length >= dec->dststart+dec->dstcount) + { + memset(&strhdr, 0, sizeof(strhdr)); + strhdr.cbStruct = sizeof(strhdr); + strhdr.pbSrc = dec->srcdata + dec->srcoffset; + strhdr.cbSrcLength = dec->srclen - dec->srcoffset; + if (!strhdr.cbSrcLength) + break; + strhdr.pbDst = buffer; + strhdr.cbDstLength = sizeof(buffer); + + qacmStreamPrepareHeader(dec->acm, &strhdr, 0); + qacmStreamConvert(dec->acm, &strhdr, ACM_STREAMCONVERTF_BLOCKALIGN); + qacmStreamUnprepareHeader(dec->acm, &strhdr, 0); + dec->srcoffset += strhdr.cbSrcLengthUsed; + if (!strhdr.cbDstLengthUsed) + { + if (strhdr.cbSrcLengthUsed) + continue; + break; + } + + newlen = dec->dstcount + (strhdr.cbDstLengthUsed * ((float)snd_speed / dec->srcspeed))/framesz; + if (dec->dstbuffer < newlen+64) + { + dec->dstbuffer = newlen+64 + snd_speed; + dec->dstdata = BZ_Realloc(dec->dstdata, dec->dstbuffer*framesz); + } + + SND_ResampleStream(strhdr.pbDst, + dec->srcspeed, + dec->srcwidth/8, + dec->srcchannels, + strhdr.cbDstLengthUsed / framesz, + dec->dstdata+dec->dstcount*framesz, + snd_speed, + dec->srcwidth/8, + dec->srcchannels, + snd_linearresample_stream.ival); + dec->dstcount = newlen; + } } buf->data = dec->dstdata; diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index d26fab260..9c4d2e0e4 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -2763,10 +2763,14 @@ void MP_Draw(void) ((float *)pr_globals)[OFS_PARM0+1] = vid.height; ((float *)pr_globals)[OFS_PARM0+2] = 0; ((float *)pr_globals)[OFS_PARM1+0] = vid.height; //dp compat, ish - if (mp_drawloading_function && (scr_drawloading||scr_disabled_for_loading)) - { - ((float *)pr_globals)[OFS_PARM1] = scr_disabled_for_loading; - PR_ExecuteProgram(menu_world.progs, mp_drawloading_function); + if (scr_drawloading||scr_disabled_for_loading) + { //don't draw the menu if we're meant to be drawing a loading screen + //the menu should provide a special function if it wants to draw custom loading screens. this is for compat with old/dp/lazy/crappy menus. + if (mp_drawloading_function) + { + ((float *)pr_globals)[OFS_PARM1] = scr_disabled_for_loading; + PR_ExecuteProgram(menu_world.progs, mp_drawloading_function); + } } else if (mp_draw_function) PR_ExecuteProgram(menu_world.progs, mp_draw_function); diff --git a/engine/client/pr_skelobj.c b/engine/client/pr_skelobj.c index 1fbd50149..3eb6ec4c6 100644 --- a/engine/client/pr_skelobj.c +++ b/engine/client/pr_skelobj.c @@ -84,6 +84,7 @@ typedef struct skelobject_s { int inuse; + int modelindex; //for vid_restarts model_t *model; world_t *world; /*be it ssqc or csqc*/ skeltype_t type; @@ -990,6 +991,21 @@ void skel_info_f(void) } } +/*models were purged. reset any pointers to them*/ +void skel_reload(void) +{ + int i; + for (i = 0; i < countof(skelobjects); i++) + { + if (!skelobjects[i].model) + continue; + if (skelobjects[i].modelindex && skelobjects[i].world) + skelobjects[i].model = skelobjects[i].world->Get_CModel(skelobjects[i].world, skelobjects[i].modelindex); + else + skelobjects[i].model = NULL; + } +} + /*destroys all skeletons*/ void skel_reset(world_t *world) { @@ -1069,6 +1085,7 @@ static skelobject_t *skel_create(world_t *world, int bonecount) skelobjects[skelidx].world = world; if (numskelobjectsused <= skelidx) numskelobjectsused = skelidx + 1; + skelobjects[skelidx].modelindex = 0; skelobjects[skelidx].model = NULL; skelobjects[skelidx].inuse = 1; return &skelobjects[skelidx]; @@ -1444,6 +1461,7 @@ void rag_removedeltaent(lerpents_t *le) if (skelobj) { skelobj->inuse = 2; //2 means don't reuse yet. + skelobj->modelindex = 0; skelobj->model = NULL; pendingkill = true; } @@ -1466,6 +1484,7 @@ void rag_lerpdeltaent(lerpents_t *le, unsigned int bonecount, short *newstate, f if (sko->numbones != bonecount) { //unusable, discard it and create a new one. sko->inuse = 2; //2 means don't reuse yet. + sko->modelindex = 0; sko->model = NULL; pendingkill = true; sko = NULL; @@ -1479,6 +1498,7 @@ void rag_lerpdeltaent(lerpents_t *le, unsigned int bonecount, short *newstate, f sko = skel_create(&csqc_world, bonecount); if (!sko) return; //couldn't get one, ran out of memory or something? + sko->modelindex = 0; sko->model = NULL; sko->type = SKEL_RELATIVE; le->skeletalobject = (sko - skelobjects) + 1; @@ -1541,6 +1561,7 @@ void rag_updatedeltaent(world_t *w, entity_t *ent, lerpents_t *le) sko = skel_create(w, Mod_GetNumBones(mod, false)); if (!sko) return; //couldn't get one, ran out of memory or something? + sko->modelindex = 0; sko->model = mod; sko->type = SKEL_RELATIVE; le->skeletalobject = (sko - skelobjects) + 1; @@ -1563,6 +1584,7 @@ void rag_updatedeltaent(world_t *w, entity_t *ent, lerpents_t *le) else if (sko->doll) sko->numanimated = sko->doll->numdefaultanimated; Mod_GetBoneRelations(mod, 0, skorel.numbones, &ent->framestate, skorel.bonematrix); + skorel.modelindex = sko->modelindex; skorel.model = sko->model; if (sko->numanimated || sko->doll != mod->dollinfo) { @@ -1789,6 +1811,7 @@ void QCBUILTIN PF_skel_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_g if (!skelobj) return; //couldn't get one, ran out of memory or something? + skelobj->modelindex = midx; skelobj->model = model; skelobj->type = type; @@ -2271,6 +2294,7 @@ void QCBUILTIN PF_skel_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_g if (skelobj) { skelobj->inuse = 2; //2 means don't reuse yet. + skelobj->modelindex = 0; skelobj->model = NULL; pendingkill = true; } diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index f4002600f..75f3122a2 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -758,6 +758,45 @@ void R2D_SubPic(float x, float y, float width, float height, mpic_t *pic, float R2D_Image(x, y, width, height, newsl, newtl, newsh, newth, pic); } +void R2D_Letterbox(float sx, float sy, float sw, float sh, mpic_t *pic, float pw, float ph) +{ + float ratiox = (float)pw / sw; + float ratioy = (float)ph / sh; + + if (pw<=0 || ph<=0) + { //no size info... + R2D_ImageColours(0, 0, 0, 1); + R2D_FillBlock(sx, sy, sw, sh); + R2D_ScalePic(sx, sy, 0, 0, pic); //in case its a videoshader with audio + } + else if (ratiox > ratioy) + { //x ratio is greatest + float h = (sw * ph) / pw; + float p = sh - h; + + //letterbox + R2D_ImageColours(0, 0, 0, 1); + R2D_FillBlock(sx, sy, sw, p/2); + R2D_FillBlock(sx, sy + h + (p/2), sw, p/2); + + R2D_ImageColours(1, 1, 1, 1); + R2D_ScalePic(sx, sy + p/2, sw, h, pic); + } + else + { //y ratio is greatest + float w = (sh * pw) / ph; + float p = sw - w; + + //sidethingies + R2D_ImageColours(0, 0, 0, 1); + R2D_FillBlock(sx, sy, (p/2), sh); + R2D_FillBlock(sx + w + (p/2), sy, p/2, sh); + + R2D_ImageColours(1, 1, 1, 1); + R2D_ScalePic(sx + p/2, sy, w, sh, pic); + } +} + /* this is an ugly special case drawing func that's only used for the player color selection menu */ void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, unsigned int *palette) { diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 93a84975e..fa88b5e39 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -1698,6 +1698,10 @@ TRACE(("dbg: R_ApplyRenderer: efrags\n")); #endif } +#ifdef SKELETALOBJECTS + skel_reload(); +#endif + if (newr && qrenderer != QR_NONE) { if (!r_forceheadless || newr->renderer->rtype != QR_HEADLESS) diff --git a/engine/client/snd_mix.c b/engine/client/snd_mix.c index 28c852270..454dbae73 100644 --- a/engine/client/snd_mix.c +++ b/engine/client/snd_mix.c @@ -192,23 +192,23 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime) //does it still make a sound if it cannot be heard?... //technically no... //this code is hacky. - if (!s->decoder.decodedata && s->decoder.buf) - { + if (s->decoder.decodedata) + scache = s->decoder.decodedata(s, &scachebuf, ch->pos>>PITCHSHIFT, 0); /*1 for luck - balances audio termination below*/ + else scache = s->decoder.buf; - ch->pos += (end-sc->paintedtime)*ch->rate; - if ((ch->pos>>PITCHSHIFT) > scache->length) + ch->pos += (end-sc->paintedtime)*ch->rate; + if (!scache || (ch->pos>>PITCHSHIFT) > scache->soundoffset+scache->length) + { + ch->pos = 0; + if (s->loopstart != -1) + ch->pos = s->loopstart<flags & CF_FORCELOOP)) { - ch->pos = 0; - if (s->loopstart != -1) - ch->pos = s->loopstart<flags & CF_FORCELOOP)) + ch->sfx = NULL; + if (s->decoder.ended) { - ch->sfx = NULL; - if (s->decoder.ended) - { - if (!S_IsPlayingSomewhere(s)) - s->decoder.ended(s); - } + if (!S_IsPlayingSomewhere(s)) + s->decoder.ended(s); } } } diff --git a/engine/client/snd_ov.c b/engine/client/snd_ov.c index 908ae21c1..85b99e3ec 100644 --- a/engine/client/snd_ov.c +++ b/engine/client/snd_ov.c @@ -175,128 +175,131 @@ static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf start *= 2*dec->srcchannels; length *= 2*dec->srcchannels; - if (start < dec->decodedbytestart) + if (length) { -// Con_Printf("Rewound to %i\n", start); - dec->failed = false; + if (start < dec->decodedbytestart) + { + // Con_Printf("Rewound to %i\n", start); + dec->failed = false; - //check pos - if (p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed))) == 0) - { - /*something rewound, purge clear the buffer*/ - dec->decodedbytecount = 0; - dec->decodedbytestart = start; - } - } - -/* if (start > dec->decodedbytestart + dec->decodedbytecount) - { - dec->decodedbytestart = start; - p_ov_pcm_seek(&dec->vf, (dec->decodedbytestart * dec->srcspeed) / outspeed); - } -*/ - if (dec->decodedbytecount > outspeed*8) - { - /*everything is okay, but our buffer is getting needlessly large. - keep anything after the 'new' position, but discard all before that - trim shouldn't be able to go negative - */ - int trim = start - dec->decodedbytestart; - if (trim < 0) - { - dec->decodedbytecount = 0; - dec->decodedbytestart = start; -// Con_Printf("trim < 0\n"); - } - else if (trim > dec->decodedbytecount) - { - if (0==p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed)))) + //check pos + if (p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed))) == 0) { + /*something rewound, purge clear the buffer*/ dec->decodedbytecount = 0; dec->decodedbytestart = start; } -// Con_Printf("trim > count\n"); - } - else - { -// Con_Printf("trim retain\n"); - //FIXME: retain an extra half-second for dual+ sound devices running slightly out of sync - memmove(dec->decodedbuffer, dec->decodedbuffer + trim, dec->decodedbytecount - trim); - dec->decodedbytecount -= trim; - dec->decodedbytestart += trim; - } - } - - for (;;) - { - if (dec->failed || start+length <= dec->decodedbytestart + dec->decodedbytecount) - break; - - if (dec->decodedbufferbytes < start+length - dec->decodedbytestart + 128) //expand if needed. - { - // Con_Printf("Expand buffer\n"); - dec->decodedbufferbytes = (start+length - dec->decodedbytestart) + outspeed; - dec->decodedbuffer = BZ_Realloc(dec->decodedbuffer, dec->decodedbufferbytes); } - if (outspeed == dec->srcspeed) + /* if (start > dec->decodedbytestart + dec->decodedbytecount) { - bytesread = p_ov_read(&dec->vf, dec->decodedbuffer+dec->decodedbytecount, (start+length) - (dec->decodedbytestart+dec->decodedbytecount), bigendianp, 2, 1, ¤t_section); - if (bytesread <= 0) + dec->decodedbytestart = start; + p_ov_pcm_seek(&dec->vf, (dec->decodedbytestart * dec->srcspeed) / outspeed); + } + */ + if (dec->decodedbytecount > outspeed*8) + { + /*everything is okay, but our buffer is getting needlessly large. + keep anything after the 'new' position, but discard all before that + trim shouldn't be able to go negative + */ + int trim = start - dec->decodedbytestart; + if (trim < 0) { - if (bytesread != 0) //0==eof + dec->decodedbytecount = 0; + dec->decodedbytestart = start; + // Con_Printf("trim < 0\n"); + } + else if (trim > dec->decodedbytecount) + { + if (0==p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed)))) { - dec->failed = true; - Con_Printf("ogg decoding failed %i\n", bytesread); + dec->decodedbytecount = 0; + dec->decodedbytestart = start; + } + // Con_Printf("trim > count\n"); + } + else + { + // Con_Printf("trim retain\n"); + //FIXME: retain an extra half-second for dual+ sound devices running slightly out of sync + memmove(dec->decodedbuffer, dec->decodedbuffer + trim, dec->decodedbytecount - trim); + dec->decodedbytecount -= trim; + dec->decodedbytestart += trim; + } + } + + for (;;) + { + if (dec->failed || start+length <= dec->decodedbytestart + dec->decodedbytecount) + break; + + if (dec->decodedbufferbytes < start+length - dec->decodedbytestart + 128) //expand if needed. + { + // Con_Printf("Expand buffer\n"); + dec->decodedbufferbytes = (start+length - dec->decodedbytestart) + outspeed; + dec->decodedbuffer = BZ_Realloc(dec->decodedbuffer, dec->decodedbufferbytes); + } + + if (outspeed == dec->srcspeed) + { + bytesread = p_ov_read(&dec->vf, dec->decodedbuffer+dec->decodedbytecount, (start+length) - (dec->decodedbytestart+dec->decodedbytecount), bigendianp, 2, 1, ¤t_section); + if (bytesread <= 0) + { + if (bytesread != 0) //0==eof + { + dec->failed = true; + Con_Printf("ogg decoding failed %i\n", bytesread); + break; + } + if (start >= dec->decodedbytestart+dec->decodedbytecount) + return NULL; //let the mixer know that we hit the end break; } - if (start >= dec->decodedbytestart+dec->decodedbytecount) - return NULL; //let the mixer know that we hit the end - break; } - } - else - { - double scale = dec->srcspeed / (double)outspeed; - int decodesize = ceil((dec->decodedbufferbytes-dec->decodedbytecount) * scale); - /*round down...*/ - decodesize &= ~(2 * dec->srcchannels - 1); - if (decodesize > dec->tempbufferbytes) + else { - dec->tempbuffer = BZ_Realloc(dec->tempbuffer, decodesize); - dec->tempbufferbytes = decodesize; - } - - bytesread = p_ov_read(&dec->vf, dec->tempbuffer, decodesize, bigendianp, 2, 1, ¤t_section); - - if (bytesread <= 0) - { - if (bytesread != 0) //0==eof + double scale = dec->srcspeed / (double)outspeed; + int decodesize = ceil((dec->decodedbufferbytes-dec->decodedbytecount) * scale); + /*round down...*/ + decodesize &= ~(2 * dec->srcchannels - 1); + if (decodesize > dec->tempbufferbytes) { - dec->failed = true; - Con_Printf("ogg decoding failed %i\n", bytesread); - return NULL; + dec->tempbuffer = BZ_Realloc(dec->tempbuffer, decodesize); + dec->tempbufferbytes = decodesize; } - if (start >= dec->decodedbytestart+dec->decodedbytecount) - return NULL; //let the mixer know that we hit the end - break; + + bytesread = p_ov_read(&dec->vf, dec->tempbuffer, decodesize, bigendianp, 2, 1, ¤t_section); + + if (bytesread <= 0) + { + if (bytesread != 0) //0==eof + { + dec->failed = true; + Con_Printf("ogg decoding failed %i\n", bytesread); + return NULL; + } + if (start >= dec->decodedbytestart+dec->decodedbytecount) + return NULL; //let the mixer know that we hit the end + break; + } + + SND_ResampleStream(dec->tempbuffer, + dec->srcspeed, + 2, + dec->srcchannels, + bytesread / (2 * dec->srcchannels), + dec->decodedbuffer+dec->decodedbytecount, + outspeed, + 2, + dec->srcchannels, + snd_linearresample_stream.ival); + + bytesread = (int)floor(bytesread / scale) & ~(2 * dec->srcchannels - 1); } - SND_ResampleStream(dec->tempbuffer, - dec->srcspeed, - 2, - dec->srcchannels, - bytesread / (2 * dec->srcchannels), - dec->decodedbuffer+dec->decodedbytecount, - outspeed, - 2, - dec->srcchannels, - snd_linearresample_stream.ival); - - bytesread = (int)floor(bytesread / scale) & ~(2 * dec->srcchannels - 1); + dec->decodedbytecount += bytesread; } - - dec->decodedbytecount += bytesread; } if (buf) diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 24a7757f6..3bcc1222f 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -274,6 +274,7 @@ void QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_ void skel_lookup(world_t *prinst, int skelidx, framestate_t *out); void skel_dodelete(world_t *world); void skel_reset(world_t *world); + void skel_reload(void); #endif void QCBUILTIN PF_physics_supported(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_physics_enable(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 45e708b74..e64063b1e 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -5531,6 +5531,9 @@ void GLBE_DrawLightPrePass(void) */ int oldfbo; + if (r_refdef.recurse) + return; //fixme: messy stuff... + /*do portals*/ BE_SelectMode(BEM_STANDARD); GLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL); @@ -5557,49 +5560,119 @@ void GLBE_DrawLightPrePass(void) static const char *defualtfmts[countof(shaderstate.tex_gbuf)] = //depth, normals, difflight, speclight {"depth", "rgba16f", "rgba16f", "rgba8", "", "", "", ""}; + + checkglerror(); + for (i = 0; i < countof(shaderstate.tex_gbuf); i++) { GLint ifmt = 0; GLenum dfmt = GL_RGBA; + GLenum dtype = GL_UNSIGNED_BYTE; var = Cvar_Get(va("gl_deferred_gbuffmt_%i", i), defualtfmts[i]?defualtfmts[i]:"", 0, "Deferred Rendering"); if (!var) continue; if (!strcmp(var->string, "rgba32f")) - ifmt = GL_RGBA32F_ARB; + { + if (gl_config_gles) + { //gles3 + ifmt = GL_RGBA32F_ARB; + dfmt = GL_RGBA; + dtype = GL_FLOAT; + } + else + ifmt = GL_RGBA32F_ARB; + } else if (!strcmp(var->string, "rgba16f")) - ifmt = GL_RGBA16F_ARB; + { + if (gl_config_gles) + { //gles3 + ifmt = GL_RGBA16F_ARB; + dfmt = GL_RGBA; + dtype = GL_FLOAT; + } + else + ifmt = GL_RGBA16F_ARB; + } // else if (!strcmp(var->string, "rgba8s")) // ifmt = GL_RGBA8_SNORM; else if (!strcmp(var->string, "depth")) { + dtype = GL_UNSIGNED_INT; ifmt = GL_DEPTH_COMPONENT; dfmt = GL_DEPTH_COMPONENT; } else if (!strcmp(var->string, "depth16")) { - ifmt = GL_DEPTH_COMPONENT16_ARB; + if (gl_config_gles) + { + dtype = GL_UNSIGNED_SHORT; + ifmt = GL_DEPTH_COMPONENT; + } + else + ifmt = GL_DEPTH_COMPONENT16_ARB; dfmt = GL_DEPTH_COMPONENT; } else if (!strcmp(var->string, "depth24")) { - ifmt = GL_DEPTH_COMPONENT24_ARB; + if (gl_config_gles) + { + dtype = GL_UNSIGNED_INT; + ifmt = GL_DEPTH_COMPONENT; + } + else + ifmt = GL_DEPTH_COMPONENT24_ARB; dfmt = GL_DEPTH_COMPONENT; } else if (!strcmp(var->string, "depth32")) { - ifmt = GL_DEPTH_COMPONENT32_ARB; + if (gl_config_gles) + { + dtype = GL_FLOAT; + ifmt = GL_DEPTH_COMPONENT; + } + else + ifmt = GL_DEPTH_COMPONENT32_ARB; dfmt = GL_DEPTH_COMPONENT; } + else if (!strcmp(var->string, "rgb565")) + { + dtype = GL_UNSIGNED_SHORT_5_6_5; + ifmt = GL_RGB; + dfmt = GL_RGB; + } + else if (!strcmp(var->string, "rgba4")) + { + dtype = GL_UNSIGNED_SHORT_4_4_4_4; + ifmt = GL_RGBA; + dfmt = GL_RGBA; + } + else if (!strcmp(var->string, "rgba5551")) + { + dtype = GL_UNSIGNED_SHORT_5_5_5_1; + ifmt = GL_RGBA; + dfmt = GL_RGBA; + } else if (!strcmp(var->string, "rgba8") || *var->string) - ifmt = GL_RGBA8; + { +#ifndef GLESONLY + if (!gl_config_gles) + ifmt = GL_RGBA8; + else +#endif + ifmt = GL_RGBA; + dfmt = GL_RGBA; + } else continue; shaderstate.tex_gbuf[i]->status = TEX_LOADED; GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_gbuf[i]); - qglTexImage2D(GL_TEXTURE_2D, 0, ifmt, w, h, 0, dfmt, GL_UNSIGNED_BYTE, NULL); + qglTexImage2D(GL_TEXTURE_2D, 0, ifmt, w, h, 0, dfmt, dtype, NULL); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + if (qglGetError()) + Con_Printf("unable to configure gbuffer image as '%s'\n", var->string); } } @@ -5660,7 +5733,7 @@ void GLBE_DrawLightPrePass(void) /*final reconfigure - now drawing final surface data onto true framebuffer*/ GLBE_FBO_Pop(oldfbo); - if (!oldfbo) + if (!oldfbo && qglDrawBuffer) qglDrawBuffer(GL_BACK); /*now draw the postlight passes (this includes blended stuff which will NOT be lit)*/ diff --git a/engine/gl/gl_draw.h b/engine/gl/gl_draw.h index 73f56d0db..4ac23d7ec 100644 --- a/engine/gl/gl_draw.h +++ b/engine/gl/gl_draw.h @@ -33,6 +33,7 @@ void R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, void R2D_Line(float x1, float y1, float x2, float y2, mpic_t *pic); void R2D_ScalePic (float x, float y, float width, float height, mpic_t *pic); void R2D_SubPic(float x, float y, float width, float height, mpic_t *pic, float srcx, float srcy, float srcwidth, float srcheight); +void R2D_Letterbox(float sx, float sy, float sw, float sh, mpic_t *pic, float pw, float ph); void R2D_ConsoleBackground (int firstline, int lastline, qboolean forceopaque); void R2D_EditorBackground (void); void R2D_TileClear (float x, float y, float w, float h); diff --git a/engine/gl/gl_screen.c b/engine/gl/gl_screen.c index 23d738204..b66a851cc 100644 --- a/engine/gl/gl_screen.c +++ b/engine/gl/gl_screen.c @@ -86,12 +86,12 @@ qboolean GLSCR_UpdateScreen (void) } else { - scr_drawloading = true; +// scr_drawloading = true; SCR_DrawLoading (true); SCR_SetUpToDrawConsole(); if (Key_Dest_Has(kdm_console)) SCR_DrawConsole(false); - scr_drawloading = false; +// scr_drawloading = false; if (R2D_Flush) R2D_Flush(); VID_SwapBuffers(); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 1f390669a..3259f45c0 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -1398,6 +1398,13 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip script = end; } + else if (!strncmp(script, "!!", 2)) + { + Con_DPrintf("Unknown directinve in glsl program %s\n", name); + script += 2; + while (*script == ' ' || *script == '\t') + script++; + } else if (!strncmp(script, "//", 2)) { script += 2; diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 460fee1a6..e774a3d1b 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -575,7 +575,16 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) gl_config.maxglslversion = 0; if (gl_config.gles && gl_config.glversion >= 2) - gl_config.maxglslversion = 100; + { + if (gl_config.glversion >= 3.2) + gl_config.maxglslversion = 320; + else if (gl_config.glversion >= 3.1) + gl_config.maxglslversion = 310; + else if (gl_config.glversion >= 3) + gl_config.maxglslversion = 300; + else + gl_config.maxglslversion = 100; + } else if (gl_config.glversion >= 2) { #define GL_SHADING_LANGUAGE_VERSION 0x8B8C @@ -1899,16 +1908,28 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int #endif { //known versions: + // == gl //100 == gles2 //110 == gl2.0 //120 == gl2.1 //130 == gl3.0 //140 == gl3.1 //150 [core|compatibility] == gl3.2 - //330, 400, 410, 420, 430 [core|compatibility] == gl?.?? //300 ES == gles3 + //310 ES == gles3.1 + //330, 400, 410, 420, 430 [core|compatibility] == gl?.?? + + if (gl_config_gles) + { + if (ver <= 110) //gles2 is rougly gl2 so 100(es)==110ish + ver = 100; + else if (ver <= 330) //gles3 is rougly gl3.3 so 300es==330ish + ver = 300; + } + + if (gl_config_gles && ver != 100) - Q_snprintfz(verline, sizeof(verline), "#version %u ES\n", ver); + Q_snprintfz(verline, sizeof(verline), "#version %u es\n", ver); else if (!gl_config_gles && ver >= 150 && !gl_config_nofixedfunc) //favour compatibility profile, simply because we want ftransform to work properly //note that versions 130+140 are awkward due to deprecation stuff, both assume compatibility profiles where supported. @@ -2921,7 +2942,12 @@ qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name)) vid.srgb = info->srgb && srgb; sh_config.minver = 100; - sh_config.maxver = 100; + if (gl_config.glversion >= 3.1) + sh_config.maxver = 310; + else if (gl_config.glversion >= 3.0) + sh_config.maxver = 300; + else + sh_config.maxver = 100; sh_config.blobpath = "gles/%s.blob"; sh_config.progpath = "glsl/%s.glsl"; sh_config.shadernamefmt = "%s_gles"; diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 8302c27ca..d0a390e79 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -8338,6 +8338,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND #endif #ifdef GLQUAKE {QR_OPENGL, 110, "lpp_depthnorm", +"!!ver 100 130\n" "!!permu BUMP\n" "!!permu SKELETAL\n" "!!permu FRAMEBLEND\n" diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index c67f755be..75f4a8cd7 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -284,7 +284,7 @@ static void SV_EmitDeltaEntIndex(sizebuf_t *msg, unsigned int entnum, qboolean r void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg, qbyte svcnumber) { #ifdef PEXT_CSQC - qbyte messagebuffer[1024]; + qbyte messagebuffer[MAX_DATAGRAM]; int en; int currentsequence = client->netchan.outgoing_sequence; globalvars_t *pr_globals; diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 900de66ad..4ef933835 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -61,7 +61,7 @@ cvar_t sv_gameplayfix_spawnbeforethinks = CVARD( "sv_gameplayfix_spawnbeforethin #endif cvar_t sv_sound_watersplash = CVAR( "sv_sound_watersplash", "misc/h2ohit1.wav"); cvar_t sv_sound_land = CVAR( "sv_sound_land", "demon/dland2.wav"); -cvar_t sv_stepheight = CVARAFD("pm_stepheight", "", "sv_stepheight", CVAR_SERVERINFO, "If empty, the value 18 will be used instead. This is the size of the step you can step up or down."); +cvar_t sv_stepheight = CVARAFD("pm_stepheight", "", "sv_stepheight", CVAR_SERVERINFO, "If empty, the value "STRINGIFY(PM_DEFAULTSTEPHEIGHT)" will be used instead. This is the size of the step you can step up or down."); cvar_t pm_ktjump = CVARF("pm_ktjump", "", CVAR_SERVERINFO); cvar_t pm_bunnyspeedcap = CVARFD("pm_bunnyspeedcap", "", CVAR_SERVERINFO, "0 or 1, ish. If the player is traveling faster than this speed while turning, their velocity will be gracefully reduced to match their current maxspeed. You can still rocket-jump to gain high velocity, but turning will reduce your speed back to the max. This can be used to disable bunny hopping."); diff --git a/engine/shaders/glsl/lpp_depthnorm.glsl b/engine/shaders/glsl/lpp_depthnorm.glsl index f4e3a8991..0762d9f9e 100644 --- a/engine/shaders/glsl/lpp_depthnorm.glsl +++ b/engine/shaders/glsl/lpp_depthnorm.glsl @@ -1,3 +1,4 @@ +!!ver 100 130 !!permu BUMP !!permu SKELETAL !!permu FRAMEBLEND diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index cd27ca2d1..d645cf943 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -405,7 +405,7 @@ static qboolean VK_CreateSwapChain(void) swapinfo.oldSwapchain = vk.swapchain; swapinfo.clipped = vid_isfullscreen?VK_FALSE:VK_TRUE; //allow fragment shaders to be skipped on parts that are obscured by another window. screenshots might get weird, so use proper captures if required/automagic. - swapinfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; //supposed to be guarenteed support. + swapinfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; //support is guarenteed by spec, in theory. for (i = 0, curpri = 0; i < presentmodes; i++) { uint32_t priority = 0; @@ -433,6 +433,9 @@ static qboolean VK_CreateSwapChain(void) } } + if (!vk.vsync && swapinfo.presentMode != VK_PRESENT_MODE_IMMEDIATE_KHR) + Con_Printf("Warning: vulkan graphics driver does not fully support disabling vsync.\n"); + vk.srgbcapable = false; swapinfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; swapinfo.imageFormat = (vid.srgb||vid_srgb.ival)?VK_FORMAT_B8G8R8A8_SRGB:VK_FORMAT_B8G8R8A8_UNORM; diff --git a/plugins/avplug/avencode.c b/plugins/avplug/avencode.c index e92ad10e4..b36c453b1 100644 --- a/plugins/avplug/avencode.c +++ b/plugins/avplug/avencode.c @@ -1,757 +1,757 @@ -#include "../plugin.h" -#include "../engine.h" - -#include "libavformat/avformat.h" -//#include "libavformat/avio.h" -//#include "libavcodec/avcodec.h" -#include "libswscale/swscale.h" -#include "libavutil/imgutils.h" -#include "libavutil/opt.h" -#include "libavutil/channel_layout.h" - -#define TARGET_FFMPEG (LIBAVFORMAT_VERSION_MICRO >= 100) - -#if TARGET_FFMPEG -#define ENCODERNAME "ffmpeg" -#else -#define ENCODERNAME "libav" -#endif - -#define HAVE_DECOUPLED_API (LIBAVCODEC_VERSION_MAJOR>57 || (LIBAVCODEC_VERSION_MAJOR==57&&LIBAVCODEC_VERSION_MINOR>=36)) - -/* -Most of the logic in here came from here: -http://svn.gnumonks.org/tags/21c3-video/upstream/ffmpeg-0.4.9-pre1/output_example.c -*/ - -static cvar_t *ffmpeg_format_force; -static cvar_t *ffmpeg_videocodec; -static cvar_t *ffmpeg_videobitrate; -static cvar_t *ffmpeg_videoforcewidth; -static cvar_t *ffmpeg_videoforceheight; -static cvar_t *ffmpeg_videopreset; -static cvar_t *ffmpeg_video_crf; -static cvar_t *ffmpeg_audiocodec; -static cvar_t *ffmpeg_audiobitrate; - -struct encctx -{ - char abspath[MAX_OSPATH]; - AVFormatContext *fc; - qboolean doneheaders; - - AVStream *video_st; - struct SwsContext *scale_ctx; - AVFrame *picture; - uint8_t *video_outbuf; - int video_outbuf_size; - - AVStream *audio_st; - AVFrame *audio; - uint8_t *audio_outbuf; - uint32_t audio_outcount; - int64_t audio_pts; -}; - -#define VARIABLE_AUDIO_FRAME_MIN_SIZE 512 //audio frames smaller than a certain size are just wasteful -#define VARIABLE_AUDIO_FRAME_MAX_SIZE 1024 - -#if !TARGET_FFMPEG -#define av_make_error_string qav_make_error_string -static inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum) -{ - av_strerror(errnum, errbuf, errbuf_size); - return errbuf; -} -#endif - -static void AVEnc_End (void *ctx); - -static AVFrame *alloc_frame(enum AVPixelFormat pix_fmt, int width, int height) -{ - AVFrame *picture; - uint8_t *picture_buf; - int size; - - picture = av_frame_alloc(); - if(!picture) - return NULL; -#if TARGET_FFMPEG - size = av_image_get_buffer_size(pix_fmt, width, height, 1); -#else - size = avpicture_get_size(pix_fmt, width, height); -#endif - picture_buf = (uint8_t*)(av_malloc(size)); - if (!picture_buf) - { - av_free(picture); - return NULL; - } -#if TARGET_FFMPEG - av_image_fill_arrays(picture->data, picture->linesize, picture_buf, pix_fmt, width, height, 1/*fixme: align*/); -#else - avpicture_fill((AVPicture*)picture, picture_buf, pix_fmt, width, height); -#endif - picture->width = width; - picture->height = height; - return picture; -} -static AVStream *add_video_stream(struct encctx *ctx, AVCodec *codec, int fps, int width, int height) -{ - AVCodecContext *c; - AVStream *st; - int bitrate = ffmpeg_videobitrate->value; - int forcewidth = ffmpeg_videoforcewidth->value; - int forceheight = ffmpeg_videoforceheight->value; - - st = avformat_new_stream(ctx->fc, codec); - if (!st) - return NULL; - - c = st->codec; - c->codec_id = codec->id; - c->codec_type = codec->type; - - /* put sample parameters */ - c->bit_rate = bitrate; - /* resolution must be a multiple of two */ - c->width = forcewidth?forcewidth:width; - c->height = forceheight?forceheight:height; - /* frames per second */ - c->time_base.num = 1; - c->time_base.den = fps; - //c->gop_size = 12; /* emit one intra frame every twelve frames at most */ - c->pix_fmt = AV_PIX_FMT_YUV420P; - if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) - { - /* just for testing, we also add B frames */ - c->max_b_frames = 2; - } - if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) - { - /* needed to avoid using macroblocks in which some coeffs overflow - this doesnt happen with normal video, it just happens here as the - motion of the chroma plane doesnt match the luma plane */ -// c->mb_decision=2; - } - // some formats want stream headers to be seperate - if (ctx->fc->oformat->flags & AVFMT_GLOBALHEADER) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - - if (*ffmpeg_videopreset->string) - av_opt_set(c->priv_data, "preset", ffmpeg_videopreset->string, AV_OPT_SEARCH_CHILDREN); - if (*ffmpeg_video_crf->string) - av_opt_set(c->priv_data, "crf", ffmpeg_video_crf->string, AV_OPT_SEARCH_CHILDREN); - - - return st; -} -static void close_video(struct encctx *ctx) -{ - if (!ctx->video_st) - return; - - avcodec_close(ctx->video_st->codec); - if (ctx->picture) - { - av_free(ctx->picture->data[0]); - av_free(ctx->picture); - } - av_free(ctx->video_outbuf); -} - -#if HAVE_DECOUPLED_API -//frame can be null on eof. -static void AVEnc_DoEncode(AVFormatContext *fc, AVStream *stream, AVFrame *frame) -{ - AVPacket pkt; - AVCodecContext *codec = stream->codec; - int err = avcodec_send_frame(codec, frame); - if (err) - { - char buf[512]; - Con_Printf("avcodec_send_frame: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); - } - - av_init_packet(&pkt); - while (!(err=avcodec_receive_packet(codec, &pkt))) - { - av_packet_rescale_ts(&pkt, codec->time_base, stream->time_base); - pkt.stream_index = stream->index; - err = av_interleaved_write_frame(fc, &pkt); - if (err) - { - char buf[512]; - Con_Printf("av_interleaved_write_frame: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); - } - av_packet_unref(&pkt); - } - if (err && err != AVERROR(EAGAIN) && err != AVERROR_EOF) - { - char buf[512]; - Con_Printf("avcodec_receive_packet: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); - } -} -#endif - -static void AVEnc_Video (void *vctx, int frameno, void *data, int bytestride, int width, int height, enum uploadfmt qpfmt) -{ - struct encctx *ctx = vctx; - const uint8_t *srcslices[4]; - int srcstride[4]; - int avpfmt; - - if (!ctx->video_st) - return; - - switch(qpfmt) - { - case TF_BGRA32: avpfmt = AV_PIX_FMT_BGRA; break; - case TF_RGBA32: avpfmt = AV_PIX_FMT_RGBA; break; -#if TARGET_FFMPEG - case TF_BGRX32: avpfmt = AV_PIX_FMT_BGR0; break; - case TF_RGBX32: avpfmt = AV_PIX_FMT_RGB0; break; -#endif - case TF_BGR24: avpfmt = AV_PIX_FMT_BGR24; break; - case TF_RGB24: avpfmt = AV_PIX_FMT_RGB24; break; - default: - return; - } - - //weird maths to flip it. - srcslices[0] = (uint8_t*)data; - srcstride[0] = bytestride; - srcslices[1] = NULL; - srcstride[1] = 0; - srcslices[2] = NULL; //libav's version probably needs this excess - srcstride[2] = 0; - srcslices[3] = NULL; - srcstride[3] = 0; - - //fixme: it would be nice to avoid copies here if possible... - //convert RGB to whatever the codec needs (ie: yuv...). - //also rescales, but only if the user resizes the video while recording. which is a stupid thing to do. - ctx->scale_ctx = sws_getCachedContext(ctx->scale_ctx, width, height, avpfmt, ctx->picture->width, ctx->picture->height, ctx->video_st->codec->pix_fmt, SWS_POINT, 0, 0, 0); - sws_scale(ctx->scale_ctx, srcslices, srcstride, 0, height, ctx->picture->data, ctx->picture->linesize); - - ctx->picture->pts = frameno; - ctx->picture->format = ctx->video_st->codec->pix_fmt; -#if HAVE_DECOUPLED_API - AVEnc_DoEncode(ctx->fc, ctx->video_st, ctx->picture); -#else - { - AVPacket pkt; - int success; - int err; - - av_init_packet(&pkt); - pkt.data = ctx->video_outbuf; - pkt.size = ctx->video_outbuf_size; - success = 0; - err = avcodec_encode_video2(ctx->video_st->codec, &pkt, ctx->picture, &success); - if (err) - { - char buf[512]; - Con_Printf("avcodec_encode_video2: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); - } - else if (err == 0 && success) - { - av_packet_rescale_ts(&pkt, ctx->video_st->codec->time_base, ctx->video_st->time_base); - pkt.stream_index = ctx->video_st->index; - err = av_interleaved_write_frame(ctx->fc, &pkt); - - if (err) - { - char buf[512]; - Con_Printf("av_interleaved_write_frame: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); - } - } - } -#endif -} - -static AVStream *add_audio_stream(struct encctx *ctx, AVCodec *codec, int samplerate, int *bits, int channels) -{ - AVCodecContext *c; - AVStream *st; - int bitrate = ffmpeg_audiobitrate->value; - - st = avformat_new_stream(ctx->fc, codec); - if (!st) - return NULL; - - st->id = ctx->fc->nb_streams-1; - c = st->codec; - c->codec_id = codec->id; - c->codec_type = codec->type; - - /* put sample parameters */ - c->bit_rate = bitrate; - /* frames per second */ - c->time_base.num = 1; - c->time_base.den = samplerate; - c->sample_rate = samplerate; - c->channels = channels; - c->channel_layout = av_get_default_channel_layout(c->channels); - c->sample_fmt = codec->sample_fmts[0]; - -// if (c->sample_fmt == AV_SAMPLE_FMT_FLTP || c->sample_fmt == AV_SAMPLE_FMT_FLT) -// *bits = 32; //get the engine to mix 32bit audio instead of whatever its currently set to. -// else if (c->sample_fmt == AV_SAMPLE_FMT_U8P || c->sample_fmt == AV_SAMPLE_FMT_U8) -// *bits = 8; //get the engine to mix 32bit audio instead of whatever its currently set to. -// else if (c->sample_fmt == AV_SAMPLE_FMT_S16P || c->sample_fmt == AV_SAMPLE_FMT_S16) -// *bits = 16; -// else - *bits = 32; - - c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; - - // some formats want stream headers to be seperate - if (ctx->fc->oformat->flags & AVFMT_GLOBALHEADER) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - -// avcodec_parameters_from_context(st->codecpar, c); - - return st; -} -static void close_audio(struct encctx *ctx) -{ - if (!ctx->audio_st) - return; - - avcodec_close(ctx->audio_st->codec); -} -static void AVEnc_Audio (void *vctx, void *data, int bytes) -{ - struct encctx *ctx = vctx; - - if (!ctx->audio_st) - return; - - while (bytes) - { - int i, p, chans = ctx->audio_st->codec->channels; - int blocksize = sizeof(float)*chans; - int count = bytes / blocksize; - int planesize = ctx->audio_st->codec->frame_size; - float *in; - int offset; - - if (!planesize) //variable-sized frames. yay - { - planesize = count; - if (count > VARIABLE_AUDIO_FRAME_MAX_SIZE - ctx->audio_outcount) - count = VARIABLE_AUDIO_FRAME_MAX_SIZE - ctx->audio_outcount; - } - else if (count > ctx->audio_st->codec->frame_size - ctx->audio_outcount) - count = ctx->audio_st->codec->frame_size - ctx->audio_outcount; - - in = (float*)data; - offset = ctx->audio_outcount; - ctx->audio_outcount += count; - data = (qbyte*)data + count * blocksize; - bytes -= count * blocksize; - - //input is always float audio, because I'm lazy. - //output is whatever the codec needs (may be packed or planar, gah). - //the engine's mixer will do all rate scaling for us, as well as channel selection - switch(ctx->audio_st->codec->sample_fmt) - { - case AV_SAMPLE_FMT_DBL: - offset *= chans; - count *= chans; - planesize *= chans; - chans = 1; - case AV_SAMPLE_FMT_DBLP: - for (p = 0; p < chans; p++) - { - double *f = (double*)ctx->audio_outbuf + p*planesize + offset; - for (i = 0; i < count*chans; i+=chans) - *f++ = in[i]; - in++; - } - break; - case AV_SAMPLE_FMT_FLT: - offset *= chans; - count *= chans; - planesize *= chans; - chans = 1; - case AV_SAMPLE_FMT_FLTP: - for (p = 0; p < chans; p++) - { - float *f = (float *)ctx->audio_outbuf + p*planesize + offset; - for (i = 0; i < count*chans; i+=chans) - *f++ = in[i]; - in++; - } - break; - case AV_SAMPLE_FMT_S32: - offset *= chans; - count *= chans; - planesize *= chans; - chans = 1; - case AV_SAMPLE_FMT_S32P: - for (p = 0; p < chans; p++) - { - int32_t *f = (int32_t *)ctx->audio_outbuf + p*planesize + offset; - for (i = 0; i < count*chans; i+=chans) - *f++ = bound(0x80000000, (in[i] * 0x7fffffff), 0x7fffffff); - in++; - } - break; - case AV_SAMPLE_FMT_S16: - offset *= chans; - count *= chans; - planesize *= chans; - chans = 1; - case AV_SAMPLE_FMT_S16P: - for (p = 0; p < chans; p++) - { - int16_t *f = (int16_t *)ctx->audio_outbuf + p*planesize + offset; - for (i = 0; i < count*chans; i+=chans) - *f++ = bound(-32768, (int)(in[i] * 32767), 32767); - - //sin((ctx->audio_pts+ctx->audio_outcount-count+i/chans)*0.1) * 32767;// - in++; - } - break; - case AV_SAMPLE_FMT_U8: - offset *= chans; - count *= chans; - planesize *= chans; - chans = 1; - case AV_SAMPLE_FMT_U8P: - for (p = 0; p < chans; p++) - { - uint8_t *f = (uint8_t*)ctx->audio_outbuf + p*planesize + offset; - for (i = 0; i < count*chans; i+=chans) - *f++ = bound(0, 128+(int)(in[i] * 127), 255); - in++; - } - break; - default: - return; - } - - if (ctx->audio_st->codec->frame_size) - { - if (ctx->audio_outcount < ctx->audio_st->codec->frame_size) - break; //not enough data yet. - } - else - { - if (ctx->audio_outcount < VARIABLE_AUDIO_FRAME_MIN_SIZE) - break; //not enough data yet. - } - - ctx->audio->nb_samples = ctx->audio_outcount; - avcodec_fill_audio_frame(ctx->audio, ctx->audio_st->codec->channels, ctx->audio_st->codec->sample_fmt, ctx->audio_outbuf, av_get_bytes_per_sample(ctx->audio_st->codec->sample_fmt)*ctx->audio_outcount*ctx->audio_st->codec->channels, 1); - ctx->audio->pts = ctx->audio_pts; - ctx->audio_pts += ctx->audio_outcount; - ctx->audio_outcount = 0; - -#if HAVE_DECOUPLED_API - AVEnc_DoEncode(ctx->fc, ctx->audio_st, ctx->audio); -#else - { - AVPacket pkt; - int success; - int err; - av_init_packet(&pkt); - pkt.data = NULL; - pkt.size = 0; - success = 0; - err = avcodec_encode_audio2(ctx->audio_st->codec, &pkt, ctx->audio, &success); - - if (err) - { - char buf[512]; - Con_Printf("avcodec_encode_audio2: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); - } - else if (success) - { - // pkt.pts = ctx->audio_st->codec->coded_frame->pts; - // if(ctx->audio_st->codec->coded_frame->key_frame) - // pkt.flags |= AV_PKT_FLAG_KEY; - - av_packet_rescale_ts(&pkt, ctx->audio_st->codec->time_base, ctx->audio_st->time_base); - pkt.stream_index = ctx->audio_st->index; - err = av_interleaved_write_frame(ctx->fc, &pkt); - if (err) - { - char buf[512]; - Con_Printf("av_interleaved_write_frame: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); - } - } - } -#endif - } -} - -static void *AVEnc_Begin (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits) -{ - struct encctx *ctx; - AVOutputFormat *fmt = NULL; - AVCodec *videocodec = NULL; - AVCodec *audiocodec = NULL; - int err; - - if (ffmpeg_format_force->string) - { - fmt = av_guess_format(ffmpeg_format_force->string, NULL, NULL); - if (!fmt) - { - Con_Printf("Unknown format specified.\n"); - return NULL; - } - } - if (!fmt) - fmt = av_guess_format(NULL, streamname, NULL); - if (!fmt) - { - Con_DPrintf("Could not deduce output format from file extension: using MPEG.\n"); - fmt = av_guess_format("mpeg", NULL, NULL); - } - if (!fmt) - { - Con_Printf("Format not known\n"); - return NULL; - } - - if (videorate) - { - if (strcmp(ffmpeg_videocodec->string, "none")) - { - if (ffmpeg_videocodec->string[0]) - { - videocodec = avcodec_find_encoder_by_name(ffmpeg_videocodec->string); - if (!videocodec) - { - Con_Printf("Unsupported %s \"%s\"\n", ffmpeg_videocodec->name, ffmpeg_videocodec->string); - return NULL; - } - } - if (!videocodec && fmt->video_codec != AV_CODEC_ID_NONE) - videocodec = avcodec_find_encoder(fmt->video_codec); - } - } - if (*sndkhz) - { - if (strcmp(ffmpeg_audiocodec->string, "none")) - { - if (ffmpeg_audiocodec->string[0]) - { - audiocodec = avcodec_find_encoder_by_name(ffmpeg_audiocodec->string); - if (!audiocodec) - { - Con_Printf(ENCODERNAME": Unsupported %s \"%s\"\n", ffmpeg_audiocodec->name, ffmpeg_audiocodec->string); - return NULL; - } - } - if (!audiocodec && fmt->audio_codec != AV_CODEC_ID_NONE) - audiocodec = avcodec_find_encoder(fmt->audio_codec); - } - } - - Con_DPrintf(ENCODERNAME": Using format \"%s\"\n", fmt->name); - if (videocodec) - Con_DPrintf(ENCODERNAME": Using Video Codec \"%s\"\n", videocodec->name); - else - Con_DPrintf(ENCODERNAME": Not encoding video\n"); - if (audiocodec) - Con_DPrintf(ENCODERNAME": Using Audio Codec \"%s\"\n", audiocodec->name); - else - Con_DPrintf(ENCODERNAME": Not encoding audio\n"); - - if (!videocodec && !audiocodec) - { - Con_DPrintf(ENCODERNAME": Nothing to encode!\n"); - return NULL; - } - - if (!audiocodec) - *sndkhz = 0; - - ctx = malloc(sizeof(*ctx)); - if (!ctx) - return NULL; - memset(ctx, 0, sizeof(*ctx)); - - ctx->fc = avformat_alloc_context(); - ctx->fc->oformat = fmt; - Q_snprintf(ctx->fc->filename, sizeof(ctx->fc->filename), "%s", streamname); - - - //pick default codecs - ctx->video_st = NULL; - if (videocodec) - ctx->video_st = add_video_stream(ctx, videocodec, videorate, width, height); - if (audiocodec) - ctx->audio_st = add_audio_stream(ctx, audiocodec, *sndkhz, sndbits, *sndchannels); - - if (ctx->video_st) - { - AVCodecContext *c = ctx->video_st->codec; - err = avcodec_open2(c, videocodec, NULL); - if (err < 0) - { - char buf[512]; - Con_Printf(ENCODERNAME": Could not init codec instance \"%s\" - %s\nMaybe try a different framerate/resolution/bitrate\n", videocodec->name, av_make_error_string(buf, sizeof(buf), err)); - AVEnc_End(ctx); - return NULL; - } - - ctx->picture = alloc_frame(c->pix_fmt, c->width, c->height); - - ctx->video_outbuf_size = 200000; - ctx->video_outbuf = av_malloc(ctx->video_outbuf_size); - if (!ctx->video_outbuf) - ctx->video_outbuf_size = 0; - } - if (ctx->audio_st) - { - int sz; - AVCodecContext *c = ctx->audio_st->codec; - err = avcodec_open2(c, audiocodec, NULL); - if (err < 0) - { - char buf[512]; - Con_Printf(ENCODERNAME": Could not init codec instance \"%s\" - %s\n", audiocodec->name, av_make_error_string(buf, sizeof(buf), err)); - AVEnc_End(ctx); - return NULL; - } - - ctx->audio = av_frame_alloc(); - sz = ctx->audio_st->codec->frame_size; - if (!sz) - sz = VARIABLE_AUDIO_FRAME_MAX_SIZE; - sz *= av_get_bytes_per_sample(ctx->audio_st->codec->sample_fmt) * ctx->audio_st->codec->channels; - ctx->audio_outbuf = av_malloc(sz); - } - - av_dump_format(ctx->fc, 0, streamname, 1); - - if (!(fmt->flags & AVFMT_NOFILE)) - { - //okay, this is annoying, but I'm too lazy to figure out the issue I was having with avio stuff. - if (!pFS_NativePath(streamname, FS_GAMEONLY, ctx->abspath, sizeof(ctx->abspath)) || avio_open(&ctx->fc->pb, ctx->abspath, AVIO_FLAG_WRITE) < 0) - { - Con_Printf("Could not open '%s'\n", streamname); - AVEnc_End(ctx); - return NULL; - } - } - - //nearly complete, can make the file dirty now. - err = avformat_write_header(ctx->fc, NULL); - if (err < 0) - { - char buf[512]; - Con_Printf("avformat_write_header: failed %s\n", av_make_error_string(buf, sizeof(buf), err)); - AVEnc_End(ctx); - return NULL; - } - ctx->doneheaders = true; - return ctx; -} -static void AVEnc_End (void *vctx) -{ - struct encctx *ctx = vctx; - unsigned int i; - -#if HAVE_DECOUPLED_API - if (ctx->doneheaders) - { - //terminate the codecs properly, flushing all unwritten packets - if (ctx->video_st) - AVEnc_DoEncode(ctx->fc, ctx->video_st, NULL); - if (ctx->audio_st) - AVEnc_DoEncode(ctx->fc, ctx->audio_st, NULL); - } -#endif - - close_video(ctx); - close_audio(ctx); - - //don't write trailers if this is an error case and we never even wrote the headers. - if (ctx->doneheaders) - { - av_write_trailer(ctx->fc); - if (*ctx->abspath) - Con_Printf("Finished writing %s\n", ctx->abspath); - } - - for(i = 0; i < ctx->fc->nb_streams; i++) - av_freep(&ctx->fc->streams[i]); -// if (!(fmt->flags & AVFMT_NOFILE)) - avio_close(ctx->fc->pb); - av_free(ctx->audio_outbuf); - av_free(ctx->fc); - free(ctx); -} -static media_encoder_funcs_t encoderfuncs = -{ - sizeof(media_encoder_funcs_t), - "ffmpeg", - "Use ffmpeg's various codecs. Various settings are configured with the "ENCODERNAME"_* cvars.", - ".mp4", - AVEnc_Begin, - AVEnc_Video, - AVEnc_Audio, - AVEnc_End -}; - -/* +#include "../plugin.h" +#include "../engine.h" + +#include "libavformat/avformat.h" +//#include "libavformat/avio.h" +//#include "libavcodec/avcodec.h" +#include "libswscale/swscale.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/channel_layout.h" + +#define TARGET_FFMPEG (LIBAVFORMAT_VERSION_MICRO >= 100) + +#if TARGET_FFMPEG +#define ENCODERNAME "ffmpeg" +#else +#define ENCODERNAME "libav" +#endif + +#define HAVE_DECOUPLED_API (LIBAVCODEC_VERSION_MAJOR>57 || (LIBAVCODEC_VERSION_MAJOR==57&&LIBAVCODEC_VERSION_MINOR>=36)) + +/* +Most of the logic in here came from here: +http://svn.gnumonks.org/tags/21c3-video/upstream/ffmpeg-0.4.9-pre1/output_example.c +*/ + +static cvar_t *ffmpeg_format_force; +static cvar_t *ffmpeg_videocodec; +static cvar_t *ffmpeg_videobitrate; +static cvar_t *ffmpeg_videoforcewidth; +static cvar_t *ffmpeg_videoforceheight; +static cvar_t *ffmpeg_videopreset; +static cvar_t *ffmpeg_video_crf; +static cvar_t *ffmpeg_audiocodec; +static cvar_t *ffmpeg_audiobitrate; + +struct encctx +{ + char abspath[MAX_OSPATH]; + AVFormatContext *fc; + qboolean doneheaders; + + AVStream *video_st; + struct SwsContext *scale_ctx; + AVFrame *picture; + uint8_t *video_outbuf; + int video_outbuf_size; + + AVStream *audio_st; + AVFrame *audio; + uint8_t *audio_outbuf; + uint32_t audio_outcount; + int64_t audio_pts; +}; + +#define VARIABLE_AUDIO_FRAME_MIN_SIZE 512 //audio frames smaller than a certain size are just wasteful +#define VARIABLE_AUDIO_FRAME_MAX_SIZE 1024 + +#if !TARGET_FFMPEG +#define av_make_error_string qav_make_error_string +static inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum) +{ + av_strerror(errnum, errbuf, errbuf_size); + return errbuf; +} +#endif + +static void AVEnc_End (void *ctx); + +static AVFrame *alloc_frame(enum AVPixelFormat pix_fmt, int width, int height) +{ + AVFrame *picture; + uint8_t *picture_buf; + int size; + + picture = av_frame_alloc(); + if(!picture) + return NULL; +#if TARGET_FFMPEG + size = av_image_get_buffer_size(pix_fmt, width, height, 1); +#else + size = avpicture_get_size(pix_fmt, width, height); +#endif + picture_buf = (uint8_t*)(av_malloc(size)); + if (!picture_buf) + { + av_free(picture); + return NULL; + } +#if TARGET_FFMPEG + av_image_fill_arrays(picture->data, picture->linesize, picture_buf, pix_fmt, width, height, 1/*fixme: align*/); +#else + avpicture_fill((AVPicture*)picture, picture_buf, pix_fmt, width, height); +#endif + picture->width = width; + picture->height = height; + return picture; +} +static AVStream *add_video_stream(struct encctx *ctx, AVCodec *codec, int fps, int width, int height) +{ + AVCodecContext *c; + AVStream *st; + int bitrate = ffmpeg_videobitrate->value; + int forcewidth = ffmpeg_videoforcewidth->value; + int forceheight = ffmpeg_videoforceheight->value; + + st = avformat_new_stream(ctx->fc, codec); + if (!st) + return NULL; + + c = st->codec; + c->codec_id = codec->id; + c->codec_type = codec->type; + + /* put sample parameters */ + c->bit_rate = bitrate; + /* resolution must be a multiple of two */ + c->width = forcewidth?forcewidth:width; + c->height = forceheight?forceheight:height; + /* frames per second */ + c->time_base.num = 1; + c->time_base.den = fps; + //c->gop_size = 12; /* emit one intra frame every twelve frames at most */ + c->pix_fmt = AV_PIX_FMT_YUV420P; + if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) + { + /* just for testing, we also add B frames */ + c->max_b_frames = 2; + } + if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) + { + /* needed to avoid using macroblocks in which some coeffs overflow + this doesnt happen with normal video, it just happens here as the + motion of the chroma plane doesnt match the luma plane */ +// c->mb_decision=2; + } + // some formats want stream headers to be seperate + if (ctx->fc->oformat->flags & AVFMT_GLOBALHEADER) + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + + if (*ffmpeg_videopreset->string) + av_opt_set(c->priv_data, "preset", ffmpeg_videopreset->string, AV_OPT_SEARCH_CHILDREN); + if (*ffmpeg_video_crf->string) + av_opt_set(c->priv_data, "crf", ffmpeg_video_crf->string, AV_OPT_SEARCH_CHILDREN); + + + return st; +} +static void close_video(struct encctx *ctx) +{ + if (!ctx->video_st) + return; + + avcodec_close(ctx->video_st->codec); + if (ctx->picture) + { + av_free(ctx->picture->data[0]); + av_free(ctx->picture); + } + av_free(ctx->video_outbuf); +} + +#if HAVE_DECOUPLED_API +//frame can be null on eof. +static void AVEnc_DoEncode(AVFormatContext *fc, AVStream *stream, AVFrame *frame) +{ + AVPacket pkt; + AVCodecContext *codec = stream->codec; + int err = avcodec_send_frame(codec, frame); + if (err) + { + char buf[512]; + Con_Printf("avcodec_send_frame: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); + } + + av_init_packet(&pkt); + while (!(err=avcodec_receive_packet(codec, &pkt))) + { + av_packet_rescale_ts(&pkt, codec->time_base, stream->time_base); + pkt.stream_index = stream->index; + err = av_interleaved_write_frame(fc, &pkt); + if (err) + { + char buf[512]; + Con_Printf("av_interleaved_write_frame: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); + } + av_packet_unref(&pkt); + } + if (err && err != AVERROR(EAGAIN) && err != AVERROR_EOF) + { + char buf[512]; + Con_Printf("avcodec_receive_packet: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); + } +} +#endif + +static void AVEnc_Video (void *vctx, int frameno, void *data, int bytestride, int width, int height, enum uploadfmt qpfmt) +{ + struct encctx *ctx = vctx; + const uint8_t *srcslices[4]; + int srcstride[4]; + int avpfmt; + + if (!ctx->video_st) + return; + + switch(qpfmt) + { + case TF_BGRA32: avpfmt = AV_PIX_FMT_BGRA; break; + case TF_RGBA32: avpfmt = AV_PIX_FMT_RGBA; break; +#if TARGET_FFMPEG + case TF_BGRX32: avpfmt = AV_PIX_FMT_BGR0; break; + case TF_RGBX32: avpfmt = AV_PIX_FMT_RGB0; break; +#endif + case TF_BGR24: avpfmt = AV_PIX_FMT_BGR24; break; + case TF_RGB24: avpfmt = AV_PIX_FMT_RGB24; break; + default: + return; + } + + //weird maths to flip it. + srcslices[0] = (uint8_t*)data; + srcstride[0] = bytestride; + srcslices[1] = NULL; + srcstride[1] = 0; + srcslices[2] = NULL; //libav's version probably needs this excess + srcstride[2] = 0; + srcslices[3] = NULL; + srcstride[3] = 0; + + //fixme: it would be nice to avoid copies here if possible... + //convert RGB to whatever the codec needs (ie: yuv...). + //also rescales, but only if the user resizes the video while recording. which is a stupid thing to do. + ctx->scale_ctx = sws_getCachedContext(ctx->scale_ctx, width, height, avpfmt, ctx->picture->width, ctx->picture->height, ctx->video_st->codec->pix_fmt, SWS_POINT, 0, 0, 0); + sws_scale(ctx->scale_ctx, srcslices, srcstride, 0, height, ctx->picture->data, ctx->picture->linesize); + + ctx->picture->pts = frameno; + ctx->picture->format = ctx->video_st->codec->pix_fmt; +#if HAVE_DECOUPLED_API + AVEnc_DoEncode(ctx->fc, ctx->video_st, ctx->picture); +#else + { + AVPacket pkt; + int success; + int err; + + av_init_packet(&pkt); + pkt.data = ctx->video_outbuf; + pkt.size = ctx->video_outbuf_size; + success = 0; + err = avcodec_encode_video2(ctx->video_st->codec, &pkt, ctx->picture, &success); + if (err) + { + char buf[512]; + Con_Printf("avcodec_encode_video2: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); + } + else if (err == 0 && success) + { + av_packet_rescale_ts(&pkt, ctx->video_st->codec->time_base, ctx->video_st->time_base); + pkt.stream_index = ctx->video_st->index; + err = av_interleaved_write_frame(ctx->fc, &pkt); + + if (err) + { + char buf[512]; + Con_Printf("av_interleaved_write_frame: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); + } + } + } +#endif +} + +static AVStream *add_audio_stream(struct encctx *ctx, AVCodec *codec, int samplerate, int *bits, int channels) +{ + AVCodecContext *c; + AVStream *st; + int bitrate = ffmpeg_audiobitrate->value; + + st = avformat_new_stream(ctx->fc, codec); + if (!st) + return NULL; + + st->id = ctx->fc->nb_streams-1; + c = st->codec; + c->codec_id = codec->id; + c->codec_type = codec->type; + + /* put sample parameters */ + c->bit_rate = bitrate; + /* frames per second */ + c->time_base.num = 1; + c->time_base.den = samplerate; + c->sample_rate = samplerate; + c->channels = channels; + c->channel_layout = av_get_default_channel_layout(c->channels); + c->sample_fmt = codec->sample_fmts[0]; + +// if (c->sample_fmt == AV_SAMPLE_FMT_FLTP || c->sample_fmt == AV_SAMPLE_FMT_FLT) +// *bits = 32; //get the engine to mix 32bit audio instead of whatever its currently set to. +// else if (c->sample_fmt == AV_SAMPLE_FMT_U8P || c->sample_fmt == AV_SAMPLE_FMT_U8) +// *bits = 8; //get the engine to mix 32bit audio instead of whatever its currently set to. +// else if (c->sample_fmt == AV_SAMPLE_FMT_S16P || c->sample_fmt == AV_SAMPLE_FMT_S16) +// *bits = 16; +// else + *bits = 32; + + c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; + + // some formats want stream headers to be seperate + if (ctx->fc->oformat->flags & AVFMT_GLOBALHEADER) + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + +// avcodec_parameters_from_context(st->codecpar, c); + + return st; +} +static void close_audio(struct encctx *ctx) +{ + if (!ctx->audio_st) + return; + + avcodec_close(ctx->audio_st->codec); +} +static void AVEnc_Audio (void *vctx, void *data, int bytes) +{ + struct encctx *ctx = vctx; + + if (!ctx->audio_st) + return; + + while (bytes) + { + int i, p, chans = ctx->audio_st->codec->channels; + int blocksize = sizeof(float)*chans; + int count = bytes / blocksize; + int planesize = ctx->audio_st->codec->frame_size; + float *in; + int offset; + + if (!planesize) //variable-sized frames. yay + { + planesize = count; + if (count > VARIABLE_AUDIO_FRAME_MAX_SIZE - ctx->audio_outcount) + count = VARIABLE_AUDIO_FRAME_MAX_SIZE - ctx->audio_outcount; + } + else if (count > ctx->audio_st->codec->frame_size - ctx->audio_outcount) + count = ctx->audio_st->codec->frame_size - ctx->audio_outcount; + + in = (float*)data; + offset = ctx->audio_outcount; + ctx->audio_outcount += count; + data = (qbyte*)data + count * blocksize; + bytes -= count * blocksize; + + //input is always float audio, because I'm lazy. + //output is whatever the codec needs (may be packed or planar, gah). + //the engine's mixer will do all rate scaling for us, as well as channel selection + switch(ctx->audio_st->codec->sample_fmt) + { + case AV_SAMPLE_FMT_DBL: + offset *= chans; + count *= chans; + planesize *= chans; + chans = 1; + case AV_SAMPLE_FMT_DBLP: + for (p = 0; p < chans; p++) + { + double *f = (double*)ctx->audio_outbuf + p*planesize + offset; + for (i = 0; i < count*chans; i+=chans) + *f++ = in[i]; + in++; + } + break; + case AV_SAMPLE_FMT_FLT: + offset *= chans; + count *= chans; + planesize *= chans; + chans = 1; + case AV_SAMPLE_FMT_FLTP: + for (p = 0; p < chans; p++) + { + float *f = (float *)ctx->audio_outbuf + p*planesize + offset; + for (i = 0; i < count*chans; i+=chans) + *f++ = in[i]; + in++; + } + break; + case AV_SAMPLE_FMT_S32: + offset *= chans; + count *= chans; + planesize *= chans; + chans = 1; + case AV_SAMPLE_FMT_S32P: + for (p = 0; p < chans; p++) + { + int32_t *f = (int32_t *)ctx->audio_outbuf + p*planesize + offset; + for (i = 0; i < count*chans; i+=chans) + *f++ = bound(0x80000000, (in[i] * 0x7fffffff), 0x7fffffff); + in++; + } + break; + case AV_SAMPLE_FMT_S16: + offset *= chans; + count *= chans; + planesize *= chans; + chans = 1; + case AV_SAMPLE_FMT_S16P: + for (p = 0; p < chans; p++) + { + int16_t *f = (int16_t *)ctx->audio_outbuf + p*planesize + offset; + for (i = 0; i < count*chans; i+=chans) + *f++ = bound(-32768, (int)(in[i] * 32767), 32767); + + //sin((ctx->audio_pts+ctx->audio_outcount-count+i/chans)*0.1) * 32767;// + in++; + } + break; + case AV_SAMPLE_FMT_U8: + offset *= chans; + count *= chans; + planesize *= chans; + chans = 1; + case AV_SAMPLE_FMT_U8P: + for (p = 0; p < chans; p++) + { + uint8_t *f = (uint8_t*)ctx->audio_outbuf + p*planesize + offset; + for (i = 0; i < count*chans; i+=chans) + *f++ = bound(0, 128+(int)(in[i] * 127), 255); + in++; + } + break; + default: + return; + } + + if (ctx->audio_st->codec->frame_size) + { + if (ctx->audio_outcount < ctx->audio_st->codec->frame_size) + break; //not enough data yet. + } + else + { + if (ctx->audio_outcount < VARIABLE_AUDIO_FRAME_MIN_SIZE) + break; //not enough data yet. + } + + ctx->audio->nb_samples = ctx->audio_outcount; + avcodec_fill_audio_frame(ctx->audio, ctx->audio_st->codec->channels, ctx->audio_st->codec->sample_fmt, ctx->audio_outbuf, av_get_bytes_per_sample(ctx->audio_st->codec->sample_fmt)*ctx->audio_outcount*ctx->audio_st->codec->channels, 1); + ctx->audio->pts = ctx->audio_pts; + ctx->audio_pts += ctx->audio_outcount; + ctx->audio_outcount = 0; + +#if HAVE_DECOUPLED_API + AVEnc_DoEncode(ctx->fc, ctx->audio_st, ctx->audio); +#else + { + AVPacket pkt; + int success; + int err; + av_init_packet(&pkt); + pkt.data = NULL; + pkt.size = 0; + success = 0; + err = avcodec_encode_audio2(ctx->audio_st->codec, &pkt, ctx->audio, &success); + + if (err) + { + char buf[512]; + Con_Printf("avcodec_encode_audio2: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); + } + else if (success) + { + // pkt.pts = ctx->audio_st->codec->coded_frame->pts; + // if(ctx->audio_st->codec->coded_frame->key_frame) + // pkt.flags |= AV_PKT_FLAG_KEY; + + av_packet_rescale_ts(&pkt, ctx->audio_st->codec->time_base, ctx->audio_st->time_base); + pkt.stream_index = ctx->audio_st->index; + err = av_interleaved_write_frame(ctx->fc, &pkt); + if (err) + { + char buf[512]; + Con_Printf("av_interleaved_write_frame: error: %s\n", av_make_error_string(buf, sizeof(buf), err)); + } + } + } +#endif + } +} + +static void *AVEnc_Begin (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits) +{ + struct encctx *ctx; + AVOutputFormat *fmt = NULL; + AVCodec *videocodec = NULL; + AVCodec *audiocodec = NULL; + int err; + + if (ffmpeg_format_force->string) + { + fmt = av_guess_format(ffmpeg_format_force->string, NULL, NULL); + if (!fmt) + { + Con_Printf("Unknown format specified.\n"); + return NULL; + } + } + if (!fmt) + fmt = av_guess_format(NULL, streamname, NULL); + if (!fmt) + { + Con_DPrintf("Could not deduce output format from file extension: using MPEG.\n"); + fmt = av_guess_format("mpeg", NULL, NULL); + } + if (!fmt) + { + Con_Printf("Format not known\n"); + return NULL; + } + + if (videorate) + { + if (strcmp(ffmpeg_videocodec->string, "none")) + { + if (ffmpeg_videocodec->string[0]) + { + videocodec = avcodec_find_encoder_by_name(ffmpeg_videocodec->string); + if (!videocodec) + { + Con_Printf("Unsupported %s \"%s\"\n", ffmpeg_videocodec->name, ffmpeg_videocodec->string); + return NULL; + } + } + if (!videocodec && fmt->video_codec != AV_CODEC_ID_NONE) + videocodec = avcodec_find_encoder(fmt->video_codec); + } + } + if (*sndkhz) + { + if (strcmp(ffmpeg_audiocodec->string, "none")) + { + if (ffmpeg_audiocodec->string[0]) + { + audiocodec = avcodec_find_encoder_by_name(ffmpeg_audiocodec->string); + if (!audiocodec) + { + Con_Printf(ENCODERNAME": Unsupported %s \"%s\"\n", ffmpeg_audiocodec->name, ffmpeg_audiocodec->string); + return NULL; + } + } + if (!audiocodec && fmt->audio_codec != AV_CODEC_ID_NONE) + audiocodec = avcodec_find_encoder(fmt->audio_codec); + } + } + + Con_DPrintf(ENCODERNAME": Using format \"%s\"\n", fmt->name); + if (videocodec) + Con_DPrintf(ENCODERNAME": Using Video Codec \"%s\"\n", videocodec->name); + else + Con_DPrintf(ENCODERNAME": Not encoding video\n"); + if (audiocodec) + Con_DPrintf(ENCODERNAME": Using Audio Codec \"%s\"\n", audiocodec->name); + else + Con_DPrintf(ENCODERNAME": Not encoding audio\n"); + + if (!videocodec && !audiocodec) + { + Con_DPrintf(ENCODERNAME": Nothing to encode!\n"); + return NULL; + } + + if (!audiocodec) + *sndkhz = 0; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + memset(ctx, 0, sizeof(*ctx)); + + ctx->fc = avformat_alloc_context(); + ctx->fc->oformat = fmt; + Q_snprintf(ctx->fc->filename, sizeof(ctx->fc->filename), "%s", streamname); + + + //pick default codecs + ctx->video_st = NULL; + if (videocodec) + ctx->video_st = add_video_stream(ctx, videocodec, videorate, width, height); + if (audiocodec) + ctx->audio_st = add_audio_stream(ctx, audiocodec, *sndkhz, sndbits, *sndchannels); + + if (ctx->video_st) + { + AVCodecContext *c = ctx->video_st->codec; + err = avcodec_open2(c, videocodec, NULL); + if (err < 0) + { + char buf[512]; + Con_Printf(ENCODERNAME": Could not init codec instance \"%s\" - %s\nMaybe try a different framerate/resolution/bitrate\n", videocodec->name, av_make_error_string(buf, sizeof(buf), err)); + AVEnc_End(ctx); + return NULL; + } + + ctx->picture = alloc_frame(c->pix_fmt, c->width, c->height); + + ctx->video_outbuf_size = 200000; + ctx->video_outbuf = av_malloc(ctx->video_outbuf_size); + if (!ctx->video_outbuf) + ctx->video_outbuf_size = 0; + } + if (ctx->audio_st) + { + int sz; + AVCodecContext *c = ctx->audio_st->codec; + err = avcodec_open2(c, audiocodec, NULL); + if (err < 0) + { + char buf[512]; + Con_Printf(ENCODERNAME": Could not init codec instance \"%s\" - %s\n", audiocodec->name, av_make_error_string(buf, sizeof(buf), err)); + AVEnc_End(ctx); + return NULL; + } + + ctx->audio = av_frame_alloc(); + sz = ctx->audio_st->codec->frame_size; + if (!sz) + sz = VARIABLE_AUDIO_FRAME_MAX_SIZE; + sz *= av_get_bytes_per_sample(ctx->audio_st->codec->sample_fmt) * ctx->audio_st->codec->channels; + ctx->audio_outbuf = av_malloc(sz); + } + + av_dump_format(ctx->fc, 0, streamname, 1); + + if (!(fmt->flags & AVFMT_NOFILE)) + { + //okay, this is annoying, but I'm too lazy to figure out the issue I was having with avio stuff. + if (!pFS_NativePath(streamname, FS_GAMEONLY, ctx->abspath, sizeof(ctx->abspath)) || avio_open(&ctx->fc->pb, ctx->abspath, AVIO_FLAG_WRITE) < 0) + { + Con_Printf("Could not open '%s'\n", streamname); + AVEnc_End(ctx); + return NULL; + } + } + + //nearly complete, can make the file dirty now. + err = avformat_write_header(ctx->fc, NULL); + if (err < 0) + { + char buf[512]; + Con_Printf("avformat_write_header: failed %s\n", av_make_error_string(buf, sizeof(buf), err)); + AVEnc_End(ctx); + return NULL; + } + ctx->doneheaders = true; + return ctx; +} +static void AVEnc_End (void *vctx) +{ + struct encctx *ctx = vctx; + unsigned int i; + +#if HAVE_DECOUPLED_API + if (ctx->doneheaders) + { + //terminate the codecs properly, flushing all unwritten packets + if (ctx->video_st) + AVEnc_DoEncode(ctx->fc, ctx->video_st, NULL); + if (ctx->audio_st) + AVEnc_DoEncode(ctx->fc, ctx->audio_st, NULL); + } +#endif + + close_video(ctx); + close_audio(ctx); + + //don't write trailers if this is an error case and we never even wrote the headers. + if (ctx->doneheaders) + { + av_write_trailer(ctx->fc); + if (*ctx->abspath) + Con_Printf("Finished writing %s\n", ctx->abspath); + } + + for(i = 0; i < ctx->fc->nb_streams; i++) + av_freep(&ctx->fc->streams[i]); +// if (!(fmt->flags & AVFMT_NOFILE)) + avio_close(ctx->fc->pb); + av_free(ctx->audio_outbuf); + av_free(ctx->fc); + free(ctx); +} +static media_encoder_funcs_t encoderfuncs = +{ + sizeof(media_encoder_funcs_t), + "ffmpeg", + "Use ffmpeg's various codecs. Various settings are configured with the "ENCODERNAME"_* cvars.", + ".mp4", + AVEnc_Begin, + AVEnc_Video, + AVEnc_Audio, + AVEnc_End +}; + +/* qintptr_t AVEnc_ExecuteCommand(qintptr_t *args) { char cmd[256]; Cmd_Argv(0, cmd, sizeof(cmd)); if (!strcmp(cmd, "avcapture")) - { -menuclear -menualias menucallback - -menubox 0 0 320 8 -menutext 0 0 "GO GO GO!!!" "radio21" -menutext 0 8 "Fall back" "radio22" -menutext 0 8 "Stick together" "radio23" -menutext 0 16 "Get in position" "radio24" -menutext 0 24 "Storm the front" "radio25" -menutext 0 24 "Report in" "radio26" + { +menuclear +menualias menucallback + +menubox 0 0 320 8 +menutext 0 0 "GO GO GO!!!" "radio21" +menutext 0 8 "Fall back" "radio22" +menutext 0 8 "Stick together" "radio23" +menutext 0 16 "Get in position" "radio24" +menutext 0 24 "Storm the front" "radio25" +menutext 0 24 "Report in" "radio26" menutext 0 24 "Cancel" return true; } return false; -} -*/ - -qboolean AVEnc_Init(void) -{ +} +*/ + +qboolean AVEnc_Init(void) +{ CHECKBUILTIN(FS_NativePath); - if (!BUILTINISVALID(FS_NativePath)) - { - Con_Printf(ENCODERNAME": Engine too old\n"); - return false; - } - if (!pPlug_ExportNative("Media_VideoEncoder", &encoderfuncs)) - { - Con_Printf(ENCODERNAME": Engine doesn't support media encoder plugins\n"); - return false; - } - - ffmpeg_format_force = pCvar_GetNVFDG(ENCODERNAME"_format_force", "", 0, "Forces the output container format. If blank, will guess based upon filename extension.", ENCODERNAME); - ffmpeg_videocodec = pCvar_GetNVFDG(ENCODERNAME"_videocodec", "", 0, "Forces which video encoder to use. If blank, guesses based upon container defaults.", ENCODERNAME); - ffmpeg_videobitrate = pCvar_GetNVFDG(ENCODERNAME"_videobitrate", "4000000", 0, "Specifies the target video bitrate", ENCODERNAME); - ffmpeg_videoforcewidth = pCvar_GetNVFDG(ENCODERNAME"_videoforcewidth", "", 0, "Rescales the input video width. Best to leave blank in order to record the video at the native resolution.", ENCODERNAME); - ffmpeg_videoforceheight = pCvar_GetNVFDG(ENCODERNAME"_videoforceheight", "", 0, "Rescales the input video height. Best to leave blank in order to record the video at the native resolution.", ENCODERNAME); - ffmpeg_videopreset = pCvar_GetNVFDG(ENCODERNAME"_videopreset", "veryfast", 0, "Specifies which codec preset to use, for codecs that support such presets.", ENCODERNAME); - ffmpeg_video_crf = pCvar_GetNVFDG(ENCODERNAME"_video_crf", "", 0, "Specifies the 'Constant Rate Factor' codec setting.", ENCODERNAME); - ffmpeg_audiocodec = pCvar_GetNVFDG(ENCODERNAME"_audiocodec", "", 0, "Forces which audio encoder to use. If blank, guesses based upon container defaults.", ENCODERNAME); - ffmpeg_audiobitrate = pCvar_GetNVFDG(ENCODERNAME"_audiobitrate", "64000", 0, "Specifies the target audio bitrate", ENCODERNAME); - -// if (Plug_Export("ExecuteCommand", AVEnc_ExecuteCommand)) -// Cmd_AddCommand("avcapture"); - - return true; -} - + if (!BUILTINISVALID(FS_NativePath)) + { + Con_Printf(ENCODERNAME": Engine too old\n"); + return false; + } + if (!pPlug_ExportNative("Media_VideoEncoder", &encoderfuncs)) + { + Con_Printf(ENCODERNAME": Engine doesn't support media encoder plugins\n"); + return false; + } + + ffmpeg_format_force = pCvar_GetNVFDG(ENCODERNAME"_format_force", "", 0, "Forces the output container format. If blank, will guess based upon filename extension.", ENCODERNAME); + ffmpeg_videocodec = pCvar_GetNVFDG(ENCODERNAME"_videocodec", "", 0, "Forces which video encoder to use. If blank, guesses based upon container defaults.", ENCODERNAME); + ffmpeg_videobitrate = pCvar_GetNVFDG(ENCODERNAME"_videobitrate", "4000000", 0, "Specifies the target video bitrate", ENCODERNAME); + ffmpeg_videoforcewidth = pCvar_GetNVFDG(ENCODERNAME"_videoforcewidth", "", 0, "Rescales the input video width. Best to leave blank in order to record the video at the native resolution.", ENCODERNAME); + ffmpeg_videoforceheight = pCvar_GetNVFDG(ENCODERNAME"_videoforceheight", "", 0, "Rescales the input video height. Best to leave blank in order to record the video at the native resolution.", ENCODERNAME); + ffmpeg_videopreset = pCvar_GetNVFDG(ENCODERNAME"_videopreset", "veryfast", 0, "Specifies which codec preset to use, for codecs that support such presets.", ENCODERNAME); + ffmpeg_video_crf = pCvar_GetNVFDG(ENCODERNAME"_video_crf", "", 0, "Specifies the 'Constant Rate Factor' codec setting.", ENCODERNAME); + ffmpeg_audiocodec = pCvar_GetNVFDG(ENCODERNAME"_audiocodec", "", 0, "Forces which audio encoder to use. If blank, guesses based upon container defaults.", ENCODERNAME); + ffmpeg_audiobitrate = pCvar_GetNVFDG(ENCODERNAME"_audiobitrate", "64000", 0, "Specifies the target audio bitrate", ENCODERNAME); + +// if (Plug_Export("ExecuteCommand", AVEnc_ExecuteCommand)) +// Cmd_AddCommand("avcapture"); + + return true; +} +