]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_video_libavw.c
hush more warnings
[xonotic/darkplaces.git] / cl_video_libavw.c
1 /*\r
2         Libavcodec integration for Darkplaces by Timofeyev Pavel\r
3 \r
4         This program is free software; you can redistribute it and/or\r
5         modify it under the terms of the GNU General Public License\r
6         as published by the Free Software Foundation; either version 2\r
7         of the License, or (at your option) any later version.\r
8 \r
9         This program is distributed in the hope that it will be useful,\r
10         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r
12 \r
13         See the GNU General Public License for more details.\r
14 \r
15         You should have received a copy of the GNU General Public License\r
16         along with this program; if not, write to:\r
17 \r
18                 Free Software Foundation, Inc.\r
19                 59 Temple Place - Suite 330\r
20                 Boston, MA  02111-1307, USA\r
21 \r
22 */\r
23 \r
24 // LordHavoc: for some reason this is being #include'd rather than treated as its own file...\r
25 // 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.\r
26 \r
27 // scaler type\r
28 #define LIBAVW_SCALER_BILINEAR  0\r
29 #define LIBAVW_SCALER_BICUBIC   1\r
30 #define LIBAVW_SCALER_X         2\r
31 #define LIBAVW_SCALER_POINT     3\r
32 #define LIBAVW_SCALER_AREA      4\r
33 #define LIBAVW_SCALER_BICUBLIN  5\r
34 #define LIBAVW_SCALER_GAUSS     6\r
35 #define LIBAVW_SCALER_SINC      7\r
36 #define LIBAVW_SCALER_LANCZOS   8\r
37 #define LIBAVW_SCALER_SPLINE    9\r
38 // output format\r
39 #define LIBAVW_PIXEL_FORMAT_BGR   0\r
40 #define LIBAVW_PIXEL_FORMAT_BGRA  1\r
41 // print levels\r
42 #define LIBAVW_PRINT_WARNING 1\r
43 #define LIBAVW_PRINT_ERROR   2\r
44 #define LIBAVW_PRINT_FATAL   3\r
45 #define LIBAVW_PRINT_PANIC   4\r
46 // exported callback functions:\r
47 typedef void    avwCallbackPrint(int, const char *);\r
48 typedef int     avwCallbackIoRead(void *, unsigned char *, int);\r
49 typedef fs_offset_t avwCallbackIoSeek(void *, fs_offset_t, int);\r
50 typedef fs_offset_t avwCallbackIoSeekSize(void *);\r
51 // exported functions:\r
52 int         (*qLibAvW_Init)(avwCallbackPrint *printfunction); // init library, returns error code\r
53 const char *(*qLibAvW_ErrorString)(int errorcode); // get string for error code\r
54 const char *(*qLibAvW_AvcVersion)(void); // get a string containing libavcodec version wrapper was built for\r
55 float       (*qLibAvW_Version)(void); // get wrapper version\r
56 int         (*qLibAvW_CreateStream)(void **stream); // create stream, returns error code\r
57 void        (*qLibAvW_RemoveStream)(void *stream); // flush and remove stream\r
58 int         (*qLibAvW_StreamGetVideoWidth)(void *stream); // get video parameters of stream\r
59 int         (*qLibAvW_StreamGetVideoHeight)(void *stream);\r
60 double      (*qLibAvW_StreamGetFramerate)(void *stream);\r
61 int         (*qLibAvW_StreamGetError)(void *stream); // get last function errorcode from stream\r
62 // simple API to play video\r
63 int (*qLibAvW_PlayVideo)(void *stream, void *file, avwCallbackIoRead *IoRead, avwCallbackIoSeek *IoSeek, avwCallbackIoSeekSize *IoSeekSize);\r
64 int (*qLibAvW_PlaySeekNextFrame)(void *stream);\r
65 int (*qLibAvW_PlayGetFrameImage)(void *stream, int pixel_format, void *imagedata, int imagewidth, int imageheight, int scaler);\r
66 \r
67 static dllfunction_t libavwfuncs[] =\r
68 {\r
69         {"LibAvW_Init",                (void **) &qLibAvW_Init },\r
70         {"LibAvW_ErrorString",         (void **) &qLibAvW_ErrorString },\r
71         {"LibAvW_AvcVersion",          (void **) &qLibAvW_AvcVersion },\r
72         {"LibAvW_Version",             (void **) &qLibAvW_Version },\r
73         {"LibAvW_CreateStream",        (void **) &qLibAvW_CreateStream },\r
74         {"LibAvW_RemoveStream",        (void **) &qLibAvW_RemoveStream },\r
75         {"LibAvW_StreamGetVideoWidth", (void **) &qLibAvW_StreamGetVideoWidth },\r
76         {"LibAvW_StreamGetVideoHeight",(void **) &qLibAvW_StreamGetVideoHeight },\r
77         {"LibAvW_StreamGetFramerate",  (void **) &qLibAvW_StreamGetFramerate },\r
78         {"LibAvW_StreamGetError",      (void **) &qLibAvW_StreamGetError },\r
79         {"LibAvW_PlayVideo",           (void **) &qLibAvW_PlayVideo },\r
80         {"LibAvW_PlaySeekNextFrame",   (void **) &qLibAvW_PlaySeekNextFrame },\r
81         {"LibAvW_PlayGetFrameImage",   (void **) &qLibAvW_PlayGetFrameImage },\r
82         {NULL, NULL}\r
83 };\r
84 \r
85 const char* dllnames_libavw[] =\r
86 {\r
87 #if defined(WIN32)\r
88                 "libavw.dll",\r
89 #elif defined(MACOSX)\r
90                 "libavw.dylib",\r
91 #else\r
92                 "libavw.so.1",\r
93                 "libavw.so",\r
94 #endif\r
95                 NULL\r
96 };\r
97 \r
98 static dllhandle_t libavw_dll = NULL;\r
99 \r
100 // DP videostream\r
101 typedef struct libavwstream_s\r
102 {\r
103         qfile_t     *file;\r
104         double       info_framerate;\r
105         unsigned int info_imagewidth;\r
106         unsigned int info_imageheight;\r
107         double       info_aspectratio;\r
108         void        *stream;\r
109 \r
110         // channel the sound file is being played on\r
111         sfx_t *sfx;\r
112         int    sndchan;\r
113         int    sndstarted;\r
114 }\r
115 libavwstream_t;\r
116 \r
117 cvar_t cl_video_libavw_minwidth  = {CVAR_SAVE, "cl_video_libavw_minwidth", "0", "if videos width is lesser than minimal, thay will be upscaled"};\r
118 cvar_t cl_video_libavw_minheight = {CVAR_SAVE, "cl_video_libavw_minheight", "0", "if videos height is lesser than minimal, thay will be upscaled"};\r
119 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."};\r
120 \r
121 // video extensions\r
122 const char* libavw_extensions[] =\r
123 {\r
124         "ogv",\r
125         "avi",\r
126         "mpg",\r
127         "mp4",\r
128         "mkv",\r
129         "webm",\r
130         "bik",\r
131         "roq",\r
132         "flv",\r
133         "wmv",\r
134         "mpeg",\r
135         "mjpeg",\r
136         "mpeg4",\r
137         NULL\r
138 };\r
139 \r
140 /*\r
141 =================================================================\r
142 \r
143   Video decoding\r
144   a features that is not supported yet and likely to be done\r
145   - streaming audio from videofiles\r
146   - streaming subtitles\r
147 \r
148 =================================================================\r
149 */\r
150 \r
151 unsigned int libavw_getwidth(void *stream);\r
152 unsigned int libavw_getheight(void *stream);\r
153 double libavw_getframerate(void *stream);\r
154 double libavw_getaspectratio(void *stream);\r
155 void libavw_close(void *stream);\r
156 \r
157 static int libavw_decodeframe(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)\r
158 {\r
159         int pixel_format = LIBAVW_PIXEL_FORMAT_BGR;\r
160         int errorcode;\r
161 \r
162         libavwstream_t *s = (libavwstream_t *)stream;\r
163 \r
164         // start sound\r
165         if (!s->sndstarted)\r
166         {\r
167                 if (s->sfx != NULL)\r
168                         s->sndchan = S_StartSound(-1, 0, s->sfx, vec3_origin, 1.0f, 0);\r
169                 s->sndstarted = 1;\r
170         }\r
171 \r
172         // read frame\r
173         if (!qLibAvW_PlaySeekNextFrame(s->stream))\r
174         {\r
175                 // got error or file end\r
176                 errorcode = qLibAvW_StreamGetError(s->stream);\r
177                 if (errorcode)\r
178                         Con_Printf("LibAvW: %s\n", qLibAvW_ErrorString(errorcode));\r
179                 return 1;\r
180         }\r
181 \r
182         // decode into bgr texture\r
183         if (bytesperpixel == 4)\r
184                 pixel_format = LIBAVW_PIXEL_FORMAT_BGRA;\r
185         else if (bytesperpixel == 3)\r
186                 pixel_format = LIBAVW_PIXEL_FORMAT_BGR;\r
187         else\r
188         {\r
189                 Con_Printf("LibAvW: cannot determine pixel format for bpp %i\n", bytesperpixel);\r
190                 return 1;\r
191         }\r
192         if (!qLibAvW_PlayGetFrameImage(s->stream, pixel_format, imagedata, s->info_imagewidth, s->info_imageheight, min(9, max(0, cl_video_libavw_scaler.integer))))\r
193                 Con_Printf("LibAvW: %s\n", qLibAvW_ErrorString(qLibAvW_StreamGetError(s->stream)));\r
194         return 0;\r
195 }\r
196 \r
197 // get stream info\r
198 unsigned int libavw_getwidth(void *stream)\r
199 {\r
200         return ((libavwstream_t *)stream)->info_imagewidth;\r
201 }\r
202 \r
203 unsigned int libavw_getheight(void *stream)\r
204 {\r
205         return ((libavwstream_t *)stream)->info_imageheight;\r
206 }\r
207 \r
208 double libavw_getframerate(void *stream)\r
209 {\r
210         return ((libavwstream_t *)stream)->info_framerate;\r
211 }\r
212 \r
213 double libavw_getaspectratio(void *stream)\r
214 {\r
215         return ((libavwstream_t *)stream)->info_aspectratio;\r
216 }\r
217 \r
218 // close stream\r
219 void libavw_close(void *stream)\r
220 {\r
221         libavwstream_t *s = (libavwstream_t *)stream;\r
222 \r
223         if (s->stream)\r
224                 qLibAvW_RemoveStream(s->stream);\r
225         s->stream = NULL;\r
226         if (s->file)\r
227                 FS_Close(s->file);\r
228         s->file = NULL;\r
229         if (s->sndchan >= 0)\r
230                 S_StopChannel(s->sndchan, true, true);\r
231         s->sndchan = -1;\r
232 }\r
233 \r
234 // IO wrapper\r
235 static int LibAvW_FS_Read(void *opaque, unsigned char *buf, int buf_size)\r
236 {\r
237         return FS_Read((qfile_t *)opaque, buf, buf_size);\r
238 }\r
239 static fs_offset_t LibAvW_FS_Seek(void *opaque, fs_offset_t pos, int whence)\r
240 {\r
241         return (fs_offset_t)FS_Seek((qfile_t *)opaque, pos, whence);\r
242 }\r
243 static fs_offset_t LibAvW_FS_SeekSize(void *opaque)\r
244 {\r
245         return (fs_offset_t)FS_FileSize((qfile_t *)opaque);\r
246 }\r
247 \r
248 // open as DP video stream\r
249 static void *LibAvW_OpenVideo(clvideo_t *video, char *filename, const char **errorstring)\r
250 {\r
251         libavwstream_t *s;\r
252         char filebase[MAX_OSPATH], check[MAX_OSPATH];\r
253         unsigned int i;\r
254         int errorcode;\r
255         char *wavename;\r
256         size_t len;\r
257 \r
258         if (!libavw_dll)\r
259                 return NULL;\r
260 \r
261         // allocate stream\r
262         s = (libavwstream_t *)Z_Malloc(sizeof(libavwstream_t));\r
263         s->sndchan = -1;\r
264         memset(s, 0, sizeof(libavwstream_t));\r
265         if (s == NULL)\r
266         {\r
267                 *errorstring = "unable to allocate memory for stream info structure";\r
268                 return NULL;\r
269         }\r
270 \r
271         // open file\r
272         s->file = FS_OpenVirtualFile(filename, true);\r
273         if (!s->file)\r
274         {\r
275                 FS_StripExtension(filename, filebase, sizeof(filebase));\r
276                 // we tried .dpv, try another extensions\r
277                 for (i = 0; libavw_extensions[i] != NULL; i++)\r
278                 {\r
279                         dpsnprintf(check, sizeof(check), "%s.%s", filebase, libavw_extensions[i]);\r
280                         s->file = FS_OpenVirtualFile(check, true);\r
281                         if (s->file)\r
282                                 break;\r
283                 }\r
284                 if (!s->file)\r
285                 {\r
286                         *errorstring = "unable to open videofile";\r
287                         libavw_close(s);\r
288                         Z_Free(s);\r
289                          return NULL;\r
290                 }\r
291         }\r
292 \r
293         // allocate libavw stream\r
294         if ((errorcode = qLibAvW_CreateStream(&s->stream)))\r
295         {\r
296                 *errorstring = qLibAvW_ErrorString(errorcode);\r
297                 libavw_close(s);\r
298                 Z_Free(s);\r
299         return NULL;\r
300         }\r
301 \r
302         // open video for playing\r
303         if (!qLibAvW_PlayVideo(s->stream, s->file, &LibAvW_FS_Read, &LibAvW_FS_Seek, &LibAvW_FS_SeekSize))\r
304         {\r
305                 *errorstring = qLibAvW_ErrorString(qLibAvW_StreamGetError(s->stream));\r
306                 libavw_close(s);\r
307                 Z_Free(s);\r
308         return NULL;\r
309         }\r
310 \r
311         // all right, start codec\r
312         s->info_imagewidth = qLibAvW_StreamGetVideoWidth(s->stream);\r
313         s->info_imageheight = qLibAvW_StreamGetVideoHeight(s->stream);\r
314         s->info_framerate = qLibAvW_StreamGetFramerate(s->stream);\r
315         s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight;\r
316         video->close = libavw_close;\r
317         video->getwidth = libavw_getwidth;\r
318         video->getheight = libavw_getheight;\r
319         video->getframerate = libavw_getframerate;\r
320         video->decodeframe = libavw_decodeframe;\r
321         video->getaspectratio = libavw_getaspectratio;\r
322 \r
323         // apply min-width, min-height, keep aspect rate\r
324         if (cl_video_libavw_minwidth.integer > 0)\r
325                 s->info_imagewidth = max(s->info_imagewidth, (unsigned int)cl_video_libavw_minwidth.integer);\r
326         if (cl_video_libavw_minheight.integer > 0)\r
327                 s->info_imageheight = max(s->info_imageheight, (unsigned int)cl_video_libavw_minheight.integer);\r
328         \r
329         // provide sound in separate .wav\r
330         len = strlen(filename) + 10;\r
331         wavename = (char *)Z_Malloc(len);\r
332         if (wavename)\r
333         {\r
334                 FS_StripExtension(filename, wavename, len-1);\r
335                 strlcat(wavename, ".wav", len);\r
336                 s->sfx = S_PrecacheSound(wavename, false, false);\r
337                 s->sndchan = -1;\r
338                 Z_Free(wavename);\r
339         }\r
340         return s;\r
341 }\r
342 \r
343 static void libavw_message(int level, const char *message)\r
344 {\r
345         if (level == LIBAVW_PRINT_WARNING)\r
346                 Con_Printf("LibAvcodec warning: %s\n", message);\r
347         else if (level == LIBAVW_PRINT_ERROR)\r
348                 Con_Printf("LibAvcodec error: %s\n", message);\r
349         else if (level == LIBAVW_PRINT_FATAL)\r
350                 Con_Printf("LibAvcodec fatal error: %s\n", message);\r
351         else\r
352                 Con_Printf("LibAvcodec panic: %s\n", message);\r
353 }\r
354 \r
355 static qboolean LibAvW_OpenLibrary(void)\r
356 {\r
357         int errorcode;\r
358 \r
359         // COMMANDLINEOPTION: Video: -nolibavw disables libavcodec wrapper support\r
360         if (COM_CheckParm("-nolibavw"))\r
361                 return false;\r
362 \r
363         // load DLL's\r
364         Sys_LoadLibrary(dllnames_libavw, &libavw_dll, libavwfuncs);\r
365         if (!libavw_dll)\r
366                 return false;\r
367 \r
368         // initialize libav wrapper\r
369         if ((errorcode = qLibAvW_Init(&libavw_message)))\r
370         {\r
371                 Con_Printf("LibAvW failed to initialize: %s\n", qLibAvW_ErrorString(errorcode));\r
372                 Sys_UnloadLibrary(&libavw_dll);\r
373         }\r
374 \r
375         Cvar_RegisterVariable(&cl_video_libavw_minwidth);\r
376         Cvar_RegisterVariable(&cl_video_libavw_minheight);\r
377         Cvar_RegisterVariable(&cl_video_libavw_scaler);\r
378 \r
379         return true;\r
380 }\r
381 \r
382 static void LibAvW_CloseLibrary(void)\r
383 {\r
384         Sys_UnloadLibrary(&libavw_dll);\r
385 }\r
386 \r