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