/* Libavcodec integration for Darkplaces by Timofeyev Pavel This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ // LordHavoc: for some reason this is being #include'd rather than treated as its own file... // LordHavoc: adapted to not require stdint.h as this is not available on MSVC++, using unsigned char instead of uint8_t and fs_offset_t instead of int64_t. // scaler type #define LIBAVW_SCALER_BILINEAR 0 #define LIBAVW_SCALER_BICUBIC 1 #define LIBAVW_SCALER_X 2 #define LIBAVW_SCALER_POINT 3 #define LIBAVW_SCALER_AREA 4 #define LIBAVW_SCALER_BICUBLIN 5 #define LIBAVW_SCALER_GAUSS 6 #define LIBAVW_SCALER_SINC 7 #define LIBAVW_SCALER_LANCZOS 8 #define LIBAVW_SCALER_SPLINE 9 // output format #define LIBAVW_PIXEL_FORMAT_BGR 0 #define LIBAVW_PIXEL_FORMAT_BGRA 1 // print levels #define LIBAVW_PRINT_WARNING 1 #define LIBAVW_PRINT_ERROR 2 #define LIBAVW_PRINT_FATAL 3 #define LIBAVW_PRINT_PANIC 4 // exported callback functions: typedef void avwCallbackPrint(int, const char *); typedef int avwCallbackIoRead(void *, unsigned char *, int); typedef fs_offset_t avwCallbackIoSeek(void *, fs_offset_t, int); typedef fs_offset_t avwCallbackIoSeekSize(void *); // exported functions: int (*qLibAvW_Init)(avwCallbackPrint *printfunction); // init library, returns error code const char *(*qLibAvW_ErrorString)(int errorcode); // get string for error code const char *(*qLibAvW_AvcVersion)(void); // get a string containing libavcodec version wrapper was built for float (*qLibAvW_Version)(void); // get wrapper version int (*qLibAvW_CreateStream)(void **stream); // create stream, returns error code void (*qLibAvW_RemoveStream)(void *stream); // flush and remove stream int (*qLibAvW_StreamGetVideoWidth)(void *stream); // get video parameters of stream int (*qLibAvW_StreamGetVideoHeight)(void *stream); double (*qLibAvW_StreamGetFramerate)(void *stream); int (*qLibAvW_StreamGetError)(void *stream); // get last function errorcode from stream // simple API to play video int (*qLibAvW_PlayVideo)(void *stream, void *file, avwCallbackIoRead *IoRead, avwCallbackIoSeek *IoSeek, avwCallbackIoSeekSize *IoSeekSize); int (*qLibAvW_PlaySeekNextFrame)(void *stream); int (*qLibAvW_PlayGetFrameImage)(void *stream, int pixel_format, void *imagedata, int imagewidth, int imageheight, int scaler); static dllfunction_t libavwfuncs[] = { {"LibAvW_Init", (void **) &qLibAvW_Init }, {"LibAvW_ErrorString", (void **) &qLibAvW_ErrorString }, {"LibAvW_AvcVersion", (void **) &qLibAvW_AvcVersion }, {"LibAvW_Version", (void **) &qLibAvW_Version }, {"LibAvW_CreateStream", (void **) &qLibAvW_CreateStream }, {"LibAvW_RemoveStream", (void **) &qLibAvW_RemoveStream }, {"LibAvW_StreamGetVideoWidth", (void **) &qLibAvW_StreamGetVideoWidth }, {"LibAvW_StreamGetVideoHeight",(void **) &qLibAvW_StreamGetVideoHeight }, {"LibAvW_StreamGetFramerate", (void **) &qLibAvW_StreamGetFramerate }, {"LibAvW_StreamGetError", (void **) &qLibAvW_StreamGetError }, {"LibAvW_PlayVideo", (void **) &qLibAvW_PlayVideo }, {"LibAvW_PlaySeekNextFrame", (void **) &qLibAvW_PlaySeekNextFrame }, {"LibAvW_PlayGetFrameImage", (void **) &qLibAvW_PlayGetFrameImage }, {NULL, NULL} }; const char* dllnames_libavw[] = { #if defined(WIN32) "libavw.dll", #elif defined(MACOSX) "libavw.dylib", #else "libavw.so.1", "libavw.so", #endif NULL }; static dllhandle_t libavw_dll = NULL; // DP videostream typedef struct libavwstream_s { qfile_t *file; double info_framerate; unsigned int info_imagewidth; unsigned int info_imageheight; double info_aspectratio; void *stream; // channel the sound file is being played on sfx_t *sfx; int sndchan; int sndstarted; } libavwstream_t; cvar_t cl_video_libavw_minwidth = {CVAR_SAVE, "cl_video_libavw_minwidth", "0", "if videos width is lesser than minimal, thay will be upscaled"}; cvar_t cl_video_libavw_minheight = {CVAR_SAVE, "cl_video_libavw_minheight", "0", "if videos height is lesser than minimal, thay will be upscaled"}; cvar_t cl_video_libavw_scaler = {CVAR_SAVE, "cl_video_libavw_scaler", "1", "selects a scaler for libavcode played videos. Scalers are: 0 - bilinear, 1 - bicubic, 2 - x, 3 - point, 4 - area, 5 - bicublin, 6 - gauss, 7 - sinc, 8 - lanczos, 9 - spline."}; // video extensions const char* libavw_extensions[] = { "ogv", "avi", "mpg", "mp4", "mkv", "webm", "bik", "roq", "flv", "wmv", "mpeg", "mjpeg", "mpeg4", NULL }; /* ================================================================= Video decoding a features that is not supported yet and likely to be done - streaming audio from videofiles - streaming subtitles ================================================================= */ unsigned int libavw_getwidth(void *stream); unsigned int libavw_getheight(void *stream); double libavw_getframerate(void *stream); double libavw_getaspectratio(void *stream); void libavw_close(void *stream); static int libavw_decodeframe(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow) { int pixel_format = LIBAVW_PIXEL_FORMAT_BGR; int errorcode; libavwstream_t *s = (libavwstream_t *)stream; // start sound if (!s->sndstarted) { if (s->sfx != NULL) s->sndchan = S_StartSound(-1, 0, s->sfx, vec3_origin, 1.0f, 0); s->sndstarted = 1; } // read frame if (!qLibAvW_PlaySeekNextFrame(s->stream)) { // got error or file end errorcode = qLibAvW_StreamGetError(s->stream); if (errorcode) Con_Printf("LibAvW: %s\n", qLibAvW_ErrorString(errorcode)); return 1; } // decode into bgr texture if (bytesperpixel == 4) pixel_format = LIBAVW_PIXEL_FORMAT_BGRA; else if (bytesperpixel == 3) pixel_format = LIBAVW_PIXEL_FORMAT_BGR; else { Con_Printf("LibAvW: cannot determine pixel format for bpp %i\n", bytesperpixel); return 1; } if (!qLibAvW_PlayGetFrameImage(s->stream, pixel_format, imagedata, s->info_imagewidth, s->info_imageheight, min(9, max(0, cl_video_libavw_scaler.integer)))) Con_Printf("LibAvW: %s\n", qLibAvW_ErrorString(qLibAvW_StreamGetError(s->stream))); return 0; } // get stream info unsigned int libavw_getwidth(void *stream) { return ((libavwstream_t *)stream)->info_imagewidth; } unsigned int libavw_getheight(void *stream) { return ((libavwstream_t *)stream)->info_imageheight; } double libavw_getframerate(void *stream) { return ((libavwstream_t *)stream)->info_framerate; } double libavw_getaspectratio(void *stream) { return ((libavwstream_t *)stream)->info_aspectratio; } // close stream void libavw_close(void *stream) { libavwstream_t *s = (libavwstream_t *)stream; if (s->stream) qLibAvW_RemoveStream(s->stream); s->stream = NULL; if (s->file) FS_Close(s->file); s->file = NULL; if (s->sndchan >= 0) S_StopChannel(s->sndchan, true, true); s->sndchan = -1; } // IO wrapper static int LibAvW_FS_Read(void *opaque, unsigned char *buf, int buf_size) { return FS_Read((qfile_t *)opaque, buf, buf_size); } static fs_offset_t LibAvW_FS_Seek(void *opaque, fs_offset_t pos, int whence) { return (fs_offset_t)FS_Seek((qfile_t *)opaque, pos, whence); } static fs_offset_t LibAvW_FS_SeekSize(void *opaque) { return (fs_offset_t)FS_FileSize((qfile_t *)opaque); } // open as DP video stream static void *LibAvW_OpenVideo(clvideo_t *video, char *filename, const char **errorstring) { libavwstream_t *s; char filebase[MAX_OSPATH], check[MAX_OSPATH]; unsigned int i; int errorcode; char *wavename; size_t len; if (!libavw_dll) return NULL; // allocate stream s = (libavwstream_t *)Z_Malloc(sizeof(libavwstream_t)); if (s == NULL) { *errorstring = "unable to allocate memory for stream info structure"; return NULL; } memset(s, 0, sizeof(libavwstream_t)); s->sndchan = -1; // open file s->file = FS_OpenVirtualFile(filename, true); if (!s->file) { FS_StripExtension(filename, filebase, sizeof(filebase)); // we tried .dpv, try another extensions for (i = 0; libavw_extensions[i] != NULL; i++) { dpsnprintf(check, sizeof(check), "%s.%s", filebase, libavw_extensions[i]); s->file = FS_OpenVirtualFile(check, true); if (s->file) break; } if (!s->file) { *errorstring = "unable to open videofile"; libavw_close(s); Z_Free(s); return NULL; } } // allocate libavw stream if ((errorcode = qLibAvW_CreateStream(&s->stream))) { *errorstring = qLibAvW_ErrorString(errorcode); libavw_close(s); Z_Free(s); return NULL; } // open video for playing if (!qLibAvW_PlayVideo(s->stream, s->file, &LibAvW_FS_Read, &LibAvW_FS_Seek, &LibAvW_FS_SeekSize)) { *errorstring = qLibAvW_ErrorString(qLibAvW_StreamGetError(s->stream)); libavw_close(s); Z_Free(s); return NULL; } // all right, start codec s->info_imagewidth = qLibAvW_StreamGetVideoWidth(s->stream); s->info_imageheight = qLibAvW_StreamGetVideoHeight(s->stream); s->info_framerate = qLibAvW_StreamGetFramerate(s->stream); s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight; video->close = libavw_close; video->getwidth = libavw_getwidth; video->getheight = libavw_getheight; video->getframerate = libavw_getframerate; video->decodeframe = libavw_decodeframe; video->getaspectratio = libavw_getaspectratio; // apply min-width, min-height, keep aspect rate if (cl_video_libavw_minwidth.integer > 0) s->info_imagewidth = max(s->info_imagewidth, (unsigned int)cl_video_libavw_minwidth.integer); if (cl_video_libavw_minheight.integer > 0) s->info_imageheight = max(s->info_imageheight, (unsigned int)cl_video_libavw_minheight.integer); // provide sound in separate .wav len = strlen(filename) + 10; wavename = (char *)Z_Malloc(len); if (wavename) { FS_StripExtension(filename, wavename, len-1); strlcat(wavename, ".wav", len); s->sfx = S_PrecacheSound(wavename, false, false); s->sndchan = -1; Z_Free(wavename); } return s; } static void libavw_message(int level, const char *message) { if (level == LIBAVW_PRINT_WARNING) Con_Printf("LibAvcodec warning: %s\n", message); else if (level == LIBAVW_PRINT_ERROR) Con_Printf("LibAvcodec error: %s\n", message); else if (level == LIBAVW_PRINT_FATAL) Con_Printf("LibAvcodec fatal error: %s\n", message); else Con_Printf("LibAvcodec panic: %s\n", message); } static qboolean LibAvW_OpenLibrary(void) { int errorcode; // COMMANDLINEOPTION: Video: -nolibavw disables libavcodec wrapper support if (COM_CheckParm("-nolibavw")) return false; // load DLL's Sys_LoadLibrary(dllnames_libavw, &libavw_dll, libavwfuncs); if (!libavw_dll) return false; // initialize libav wrapper if ((errorcode = qLibAvW_Init(&libavw_message))) { Con_Printf("LibAvW failed to initialize: %s\n", qLibAvW_ErrorString(errorcode)); Sys_UnloadLibrary(&libavw_dll); } Cvar_RegisterVariable(&cl_video_libavw_minwidth); Cvar_RegisterVariable(&cl_video_libavw_minheight); Cvar_RegisterVariable(&cl_video_libavw_scaler); return true; } static void LibAvW_CloseLibrary(void) { Sys_UnloadLibrary(&libavw_dll); }