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