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 
libavw_decodeframe(void * stream,void * imagedata,unsigned int Rmask,unsigned int Gmask,unsigned int Bmask,unsigned int bytesperpixel,int imagebytesperrow)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
libavw_getwidth(void * stream)198 unsigned int libavw_getwidth(void *stream)
199 {
200 	return ((libavwstream_t *)stream)->info_imagewidth;
201 }
202 
libavw_getheight(void * stream)203 unsigned int libavw_getheight(void *stream)
204 {
205 	return ((libavwstream_t *)stream)->info_imageheight;
206 }
207 
libavw_getframerate(void * stream)208 double libavw_getframerate(void *stream)
209 {
210 	return ((libavwstream_t *)stream)->info_framerate;
211 }
212 
libavw_getaspectratio(void * stream)213 double libavw_getaspectratio(void *stream)
214 {
215 	return ((libavwstream_t *)stream)->info_aspectratio;
216 }
217 
218 // close stream
libavw_close(void * 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
LibAvW_FS_Read(void * opaque,unsigned char * buf,int buf_size)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 }
LibAvW_FS_Seek(void * opaque,fs_offset_t pos,int whence)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 }
LibAvW_FS_SeekSize(void * opaque)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
LibAvW_OpenVideo(clvideo_t * video,char * filename,const char ** errorstring)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 
libavw_message(int level,const char * message)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 
LibAvW_OpenLibrary(void)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 
LibAvW_CloseLibrary(void)382 static void LibAvW_CloseLibrary(void)
383 {
384 	Sys_UnloadLibrary(&libavw_dll);
385 }
386 
387