Fix engine not starting on Windows if linked against SDL > 2.0.5
[xonotic/darkplaces.git] / cl_video_jamdecode.c
1 // JAM format decoder, used by Blood Omnicide
2
3 #ifdef LIBAVCODEC
4 //#define JAM_USELIBAVCODECSCALE
5 #endif
6
7 typedef struct jamdecodestream_s
8 {
9         qfile_t       *file;
10         double         info_framerate;
11         unsigned int   info_frames;
12         unsigned int   info_imagewidth;
13         unsigned int   info_imageheight;
14         double         info_aspectratio;
15         float          colorscale;
16         unsigned char  colorsub;
17
18         // info used during decoding
19         unsigned char *frame;
20         unsigned char *frame_prev;
21         unsigned char  frame_palette[768];
22         unsigned char *frame_compressed;
23         unsigned int   framesize;
24         unsigned int   framenum;
25
26         // libavcodec scaling
27 #ifdef JAM_USELIBAVCODECSCALE
28         unsigned char *frame_output_buffer;
29         AVFrame       *frame_output;
30         AVFrame       *frame_output_scale;
31         unsigned int   framewidth;
32         unsigned int   frameheight;
33 #endif
34
35         // channel the sound file is being played on
36         int sndchan;
37 }
38 jamdecodestream_t;
39
40 // opens a stream
41 void jam_close(void *stream);
42 unsigned int jam_getwidth(void *stream);
43 unsigned int jam_getheight(void *stream);
44 double jam_getframerate(void *stream);
45 double jam_getaspectratio(void *stream);
46 int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);
47 void *jam_open(clvideo_t *video, char *filename, const char **errorstring)
48 {
49         char jamHead[16];
50         jamdecodestream_t *s;
51         char *wavename;
52
53         // allocate stream structure
54         s = (jamdecodestream_t *)Z_Malloc(sizeof(jamdecodestream_t));
55         memset(s, 0, sizeof(jamdecodestream_t));
56         if (s == NULL)
57         {
58                 *errorstring = "unable to allocate memory for stream info structure";
59                 return NULL;
60         }
61         s->sndchan = -1;
62
63         // open file
64         s->file = FS_OpenVirtualFile(filename, true);
65         if (!s->file)
66         {
67                 *errorstring = "unable to open videofile";
68                 jam_close(s);
69                 return NULL;
70         }
71
72         // read header
73         if (!FS_Read(s->file, jamHead, 16))
74         {
75                 *errorstring = "JamDecoder: unexpected EOF reading header";
76                 jam_close(s);
77                 return NULL;
78         }
79         if (memcmp(jamHead, "JAM", 4))
80         {
81                 *errorstring = "JamDecoder: not a JAM file";
82                 jam_close(s);
83                 return NULL;
84         }
85
86         s->info_imagewidth = LittleLong(*(jamHead + 4));
87         s->info_imageheight = LittleLong(*(jamHead + 8));
88         s->info_frames = LittleLong(*(jamHead + 12)) - 1;
89         s->info_framerate = 15;
90         s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight;
91         s->colorscale = 0.90;
92         s->colorsub = 4;
93         s->framesize = s->info_imagewidth * s->info_imageheight;
94
95         // allocate frame input/output
96         if (s->framesize < 0)
97         {
98                 *errorstring = "JamDecoder: bad framesize";
99                 jam_close(s);
100                 return NULL;
101         }
102         s->frame = (unsigned char *)Z_Malloc(s->framesize * 2);
103         s->frame_prev = (unsigned char *)Z_Malloc(s->framesize * 2);
104         s->frame_compressed = (unsigned char *)Z_Malloc(s->framesize);
105         if (s->frame_compressed == NULL || s->frame == NULL || s->frame_prev == NULL)
106         {
107                 *errorstring = "JamDecoder: unable to allocate memory for video decoder";
108                 jam_close(s);
109                 return NULL;
110         }
111
112         // scale support provided by libavcodec
113 #ifdef JAM_USELIBAVCODECSCALE
114         s->framewidth = s->info_imagewidth;
115         s->frameheight = s->info_imageheight;
116
117         // min size
118         if (cl_video_libavcodec_minwidth.integer > 0)
119                 s->info_imagewidth = max(s->info_imagewidth, (unsigned int)cl_video_libavcodec_minwidth.integer);
120         if (cl_video_libavcodec_minheight.integer > 0)
121                 s->info_imageheight = max(s->info_imageheight, (unsigned int)cl_video_libavcodec_minheight.integer);
122
123         // allocate output
124         s->frame_output_buffer = (unsigned char *)Z_Malloc(s->framesize * 4);
125         s->frame_output = AvCodec_AllocFrame();
126         s->frame_output_scale = AvCodec_AllocFrame();
127         if (!s->frame_output_buffer || !s->frame_output || !s->frame_output_scale)
128     {
129                 *errorstring = "JamDecoder: failed to allocate LibAvcodec frame";
130                 jam_close(s);
131                 Z_Free(s);
132         return NULL;
133         }
134 #endif
135
136         // everything is ok
137         // set the module functions
138         s->framenum = 0;
139         video->close = jam_close;
140         video->getwidth = jam_getwidth;
141         video->getheight = jam_getheight;
142         video->getframerate = jam_getframerate;
143         video->decodeframe = jam_video;
144         video->getaspectratio = jam_getaspectratio;
145
146         // set sound
147         size_t namelen;
148         namelen = strlen(filename) + 10;
149         wavename = (char *)Z_Malloc(namelen);
150         if (wavename)
151         {
152                 sfx_t* sfx;
153                 FS_StripExtension(filename, wavename, namelen);
154                 strlcat(wavename, ".wav", namelen);
155                 sfx = S_PrecacheSound(wavename, false, false);
156                 if (sfx != NULL)
157                         s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);
158                 else
159                         s->sndchan = -1;
160                 Z_Free(wavename);
161         }
162
163         return s;
164 }
165
166 // closes a stream
167 void jam_close(void *stream)
168 {
169         jamdecodestream_t *s = (jamdecodestream_t *)stream;
170         if (s == NULL)
171                 return;
172         if (s->frame_compressed)
173                 Z_Free(s->frame_compressed);
174         s->frame_compressed = NULL;
175         if (s->frame)
176                 Z_Free(s->frame);
177         s->frame = NULL;
178         if (s->frame_prev)
179                 Z_Free(s->frame_prev);
180         s->frame_prev = NULL;
181         if (s->sndchan != -1)
182                 S_StopChannel(s->sndchan, true, true);
183         s->sndchan = -1;
184         if (s->file)
185                 FS_Close(s->file);
186         s->file = NULL;
187 #ifdef JAM_USELIBAVCODECSCALE
188         if (s->frame_output_buffer)
189                 Z_Free(s->frame_output_buffer);
190         s->frame_output_buffer = NULL;
191         if (s->frame_output)
192                 AvUtil_Free(s->frame_output);
193         s->frame_output = NULL;
194         if (s->frame_output_scale)
195                 AvUtil_Free(s->frame_output_scale);
196         s->frame_output_scale = NULL;
197 #endif
198         Z_Free(s);
199 }
200
201 // returns the width of the image data
202 unsigned int jam_getwidth(void *stream)
203 {
204         jamdecodestream_t *s = (jamdecodestream_t *)stream;
205         return s->info_imagewidth;
206 }
207
208 // returns the height of the image data
209 unsigned int jam_getheight(void *stream)
210 {
211         jamdecodestream_t *s = (jamdecodestream_t *)stream;
212         return s->info_imageheight;
213 }
214
215 // returns the framerate of the stream
216 double jam_getframerate(void *stream)
217 {
218         jamdecodestream_t *s = (jamdecodestream_t *)stream;
219         return s->info_framerate;
220 }
221
222 // returns aspect ration of the stream
223 double jam_getaspectratio(void *stream)
224 {
225         jamdecodestream_t *s = (jamdecodestream_t *)stream;
226         return s->info_aspectratio;
227 }
228
229 // decode JAM frame
230 static void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigned char *prevbuf, int outsize, int frametype)
231 {
232         unsigned char *srcptr, *destptr, *prevptr;
233         int bytesleft;
234         unsigned int mark;
235         unsigned short int bits;
236         int rep;
237         int backoffs;
238         unsigned char *back;
239         int i;
240
241         srcptr = inbuf;
242         destptr = outbuf;
243         prevptr = prevbuf;
244         bytesleft = outsize;
245
246         if (frametype == 2)
247         {
248                 memcpy(outbuf, inbuf, outsize);
249                 return;
250         }
251         while(bytesleft > 0)
252         {
253                 memcpy(&mark, srcptr, 4);
254                 srcptr += 4;
255                 for(i=0; i<32 && bytesleft > 0; i++,mark=mark>>1)
256                 {
257                         if(mark & 1)
258                         {
259                                 *destptr = *srcptr;
260                                 destptr ++;
261                                 prevptr ++;
262                                 srcptr ++;
263                                 bytesleft --;
264                         }
265                         else
266                         {
267                                 bits = srcptr[0] + 256*srcptr[1];
268                                 rep = (bits >> 11) + 3;
269                                 if(frametype == 1)
270                                 {
271                                         backoffs = 0x821 - (bits & 0x7ff);
272                                         back = destptr - backoffs;
273                                 }
274                                 else
275                                 {
276                                         backoffs = 0x400 - (bits & 0x7ff);
277                                         back = prevptr - backoffs;
278                                 }
279                                 srcptr += 2;
280                                 memcpy(destptr, back, rep);
281                                 destptr += rep;
282                                 prevptr += rep;
283                                 bytesleft -= rep;
284                         }
285                 }
286         }
287 }
288
289 // decodes a video frame to the supplied output pixels
290 int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)
291 {
292         unsigned char frameHead[16], *b;
293         unsigned int compsize, outsize, i;
294         jamdecodestream_t *s = (jamdecodestream_t *)stream;
295
296         // EOF
297         if (s->framenum >= s->info_frames)
298                 return 1;
299         s->framenum++;
300
301 readframe:
302         // read frame header
303         if (!FS_Read(s->file, &frameHead, 16))
304         {
305                 Con_Printf("JamDecoder: unexpected EOF on frame %i\n", s->framenum);
306                 return 1;
307         }
308         compsize = LittleLong(*(frameHead + 8)) - 16;
309         outsize = LittleLong(*(frameHead + 12));
310         if (compsize > s->framesize || outsize > s->framesize)
311         {
312                 Con_Printf("JamDecoder: got bogus header on frame %i\n", s->framenum);
313                 return 1;
314         }
315
316         // read frame contents
317         if (!FS_Read(s->file, s->frame_compressed, compsize))
318         {
319                 Con_Printf("JamDecoder: unexpected EOF on frame %i\n", s->framenum);
320                 return 1;
321         }
322
323         // palette goes interleaved with special flag
324         if (frameHead[0] == 2)
325         {
326                 if (compsize == 768)
327                 {
328                         memcpy(s->frame_palette, s->frame_compressed, 768);
329                         for(i = 0; i < 768; i++)
330                                 s->frame_palette[i] = (unsigned char)(bound(0, (s->frame_palette[i] * s->colorscale) - s->colorsub, 255));
331                         goto readframe;
332                 }
333         }
334         else
335         {
336                 // decode frame
337                 // shift buffers to provide current and previous one, decode
338                 b = s->frame_prev;
339                 s->frame_prev = s->frame;
340                 s->frame = b;
341                 jam_decodeframe(s->frame_compressed, s->frame, s->frame_prev, outsize, frameHead[4]);
342 #ifdef JAM_USELIBAVCODECSCALE
343                 // make BGRA imagepixels from 8bit palettized frame
344                 b = (unsigned char *)s->frame_output_buffer;
345                 for(i = 0; i < s->framesize; i++)
346                 {
347                         *b++ = s->frame_palette[s->frame[i]*3 + 2];
348                         *b++ = s->frame_palette[s->frame[i]*3 + 1];
349                         *b++ = s->frame_palette[s->frame[i]*3];
350                         *b++ = 255;
351                 }
352                 // scale
353                 AvCodec_FillPicture((AVPicture *)s->frame_output, (uint8_t *)s->frame_output_buffer, PIX_FMT_BGRA, s->framewidth, s->frameheight);
354                 AvCodec_FillPicture((AVPicture *)s->frame_output_scale, (uint8_t *)imagedata, PIX_FMT_BGRA, s->info_imagewidth, s->info_imageheight);
355                 SwsContext *scale_context = SwScale_GetCachedContext(NULL, s->framewidth, s->frameheight, PIX_FMT_BGRA, s->info_imagewidth, s->info_imageheight, PIX_FMT_BGRA, libavcodec_scalers[max(0, min(LIBAVCODEC_SCALERS, cl_video_libavcodec_scaler.integer))], NULL, NULL, NULL); 
356                 if (!scale_context)
357                 {
358                         Con_Printf("JamDecoder: LibAvcodec: error creating scale context frame %i\n", s->framenum);
359                         return 1;
360                 }
361                 if (!SwScale_Scale(scale_context, s->frame_output->data, s->frame_output->linesize, 0, s->frameheight, s->frame_output_scale->data, s->frame_output_scale->linesize))
362                         Con_Printf("JamDecoder: LibAvcodec : error scaling frame\n", s->framenum);
363                 SwScale_FreeContext(scale_context); 
364 #else
365                 // make BGRA imagepixels from 8bit palettized frame
366                 b = (unsigned char *)imagedata;
367                 for(i = 0; i < s->framesize; i++)
368                 {
369                         // bgra
370                         *b++ = s->frame_palette[s->frame[i]*3 + 2];
371                         *b++ = s->frame_palette[s->frame[i]*3 + 1];
372                         *b++ = s->frame_palette[s->frame[i]*3];
373                         *b++ = 255;
374                 }
375 #endif
376         }
377         return 0;
378 }