]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_video_jamdecode.c
Fix engine not starting on Windows if linked against SDL > 2.0.5
[xonotic/darkplaces.git] / cl_video_jamdecode.c
index da65ef298e96bbb42da1da2f99c20571cb149532..04bd63489dfe7253df7ebc91b387e1474809aee8 100644 (file)
 // JAM format decoder, used by Blood Omnicide
 
+#ifdef LIBAVCODEC
+//#define JAM_USELIBAVCODECSCALE
+#endif
+
 typedef struct jamdecodestream_s
 {
-       int error;
+       qfile_t       *file;
+       double         info_framerate;
+       unsigned int   info_frames;
+       unsigned int   info_imagewidth;
+       unsigned int   info_imageheight;
+       double         info_aspectratio;
+       float          colorscale;
+       unsigned char  colorsub;
 
-       qfile_t *file;
-       double info_framerate;
-       unsigned int info_frames;
-       unsigned int info_imagewidth;
-       unsigned int info_imageheight;
-       int doubleres;
-       float colorscale;
-       unsigned char colorsub;
-       float stipple;
+       // info used during decoding
+       unsigned char *frame;
+       unsigned char *frame_prev;
+       unsigned char  frame_palette[768];
+       unsigned char *frame_compressed;
+       unsigned int   framesize;
+       unsigned int   framenum;
 
-       // info used durign decoding
-       unsigned char *videopixels;
-       unsigned char *compressed;
-       unsigned char *framedata;
-       unsigned char *prevframedata;
-       unsigned char colormap[768];
-       unsigned int framesize;
-       unsigned int framenum;
+       // libavcodec scaling
+#ifdef JAM_USELIBAVCODECSCALE
+       unsigned char *frame_output_buffer;
+       AVFrame       *frame_output;
+       AVFrame       *frame_output_scale;
+       unsigned int   framewidth;
+       unsigned int   frameheight;
+#endif
 
        // channel the sound file is being played on
        int sndchan;
 }
 jamdecodestream_t;
 
-#define JAMDECODEERROR_NONE                0
-#define JAMDECODEERROR_EOF                 1
-#define JAMDECODEERROR_READERROR           2
-#define JAMDECODEERROR_BAD_FRAME_HEADER    3
-#define JAMDECODEERROR_BAD_OUTPUT_SIZE     4
-#define JAMDECODEERROR_BAD_COLORMAP        5
-
 // opens a stream
 void jam_close(void *stream);
 unsigned int jam_getwidth(void *stream);
 unsigned int jam_getheight(void *stream);
 double jam_getframerate(void *stream);
+double jam_getaspectratio(void *stream);
 int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);
-static void *jam_open(clvideo_t *video, char *filename, const char **errorstring)
+void *jam_open(clvideo_t *video, char *filename, const char **errorstring)
 {
-       unsigned char jamHead[16];
-       char *wavename;
+       char jamHead[16];
        jamdecodestream_t *s;
-       qfile_t *file;
+       char *wavename;
 
+       // allocate stream structure
        s = (jamdecodestream_t *)Z_Malloc(sizeof(jamdecodestream_t));
-       if (s != NULL)
+       memset(s, 0, sizeof(jamdecodestream_t));
+       if (s == NULL)
        {
-               if ((file = FS_OpenVirtualFile(filename, false)))
-               {
-                       s->file = file;
-                       if (FS_Read(s->file, &jamHead, 16))
-                       {
-                               if (!memcmp(jamHead, "JAM", 3))
-                               {
-                                       s->info_imagewidth = LittleLong(*(jamHead + 4));
-                                       s->info_imageheight = LittleLong(*(jamHead + 8));
-                                       s->info_frames = LittleLong(*(jamHead + 12));
-                                       s->info_framerate = 15;
-                                       s->doubleres = 0;
-                                       s->colorscale = 0.70;
-                                       s->colorsub = 8;
-                                       s->stipple = 0.4;
-                                       s->framesize = s->info_imagewidth * s->info_imageheight;
-                                       if (s->framesize > 0)
-                                       {
-                                               s->compressed = (unsigned char *)Z_Malloc(s->framesize);
-                                               s->framedata = (unsigned char *)Z_Malloc(s->framesize * 2);
-                                               s->prevframedata = (unsigned char *)Z_Malloc(s->framesize * 2);
-                                               s->videopixels = (unsigned char *)Z_Malloc(s->framesize * 4); // bgra, doubleres
-                                               if (s->compressed != NULL && s->framedata != NULL && s->prevframedata != NULL && s->videopixels != NULL)
-                                               {
-                                                       size_t namelen;
+               *errorstring = "unable to allocate memory for stream info structure";
+               return NULL;
+       }
+       s->sndchan = -1;
 
-                                                       namelen = strlen(filename) + 10;
-                                                       wavename = (char *)Z_Malloc(namelen);
-                                                       if (wavename)
-                                                       {
-                                                               sfx_t* sfx;
+       // open file
+       s->file = FS_OpenVirtualFile(filename, true);
+       if (!s->file)
+       {
+               *errorstring = "unable to open videofile";
+               jam_close(s);
+               return NULL;
+       }
 
-                                                               FS_StripExtension(filename, wavename, namelen);
-                                                               strlcat(wavename, ".wav", namelen);
-                                                               sfx = S_PrecacheSound(wavename, false, false);
-                                                               if (sfx != NULL)
-                                                                       s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);
-                                                               else
-                                                                       s->sndchan = -1;
-                                                               Z_Free(wavename);
-                                                       }
-                                                       // all is well...
-                                                       // set the module functions
-                                                       s->framenum = 0;
-                                                       video->close = jam_close;
-                                                       video->getwidth = jam_getwidth;
-                                                       video->getheight = jam_getheight;
-                                                       video->getframerate = jam_getframerate;
-                                                       video->decodeframe = jam_video;
-                                                       return s;
-                                               }
-                                               else if (errorstring != NULL)
-                                                       *errorstring = "unable to allocate memory for stream info structure";
-                                               if (s->compressed != NULL)
-                                                       Z_Free(s->compressed);
-                                               if (s->framedata != NULL)
-                                                       Z_Free(s->framedata);
-                                               if (s->prevframedata != NULL)
-                                                       Z_Free(s->prevframedata);
-                                               if (s->videopixels != NULL)
-                                                       Z_Free(s->videopixels);
-                                       }
-                                       else if (errorstring != NULL)
-                                               *errorstring = "bad framesize";
-                               }
-                               else if (errorstring != NULL)
-                                       *errorstring = "not JAM videofile";
-                       }
-                       else if (errorstring != NULL)
-                               *errorstring = "unexpected EOF";
-                       FS_Close(file);
-               }
-               else if (errorstring != NULL)
-                       *errorstring = "unable to open videofile";
+       // read header
+       if (!FS_Read(s->file, jamHead, 16))
+       {
+               *errorstring = "JamDecoder: unexpected EOF reading header";
+               jam_close(s);
+               return NULL;
+       }
+       if (memcmp(jamHead, "JAM", 4))
+       {
+               *errorstring = "JamDecoder: not a JAM file";
+               jam_close(s);
+               return NULL;
+       }
+
+       s->info_imagewidth = LittleLong(*(jamHead + 4));
+       s->info_imageheight = LittleLong(*(jamHead + 8));
+       s->info_frames = LittleLong(*(jamHead + 12)) - 1;
+       s->info_framerate = 15;
+       s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight;
+       s->colorscale = 0.90;
+       s->colorsub = 4;
+       s->framesize = s->info_imagewidth * s->info_imageheight;
+
+       // allocate frame input/output
+       if (s->framesize < 0)
+       {
+               *errorstring = "JamDecoder: bad framesize";
+               jam_close(s);
+               return NULL;
+       }
+       s->frame = (unsigned char *)Z_Malloc(s->framesize * 2);
+       s->frame_prev = (unsigned char *)Z_Malloc(s->framesize * 2);
+       s->frame_compressed = (unsigned char *)Z_Malloc(s->framesize);
+       if (s->frame_compressed == NULL || s->frame == NULL || s->frame_prev == NULL)
+       {
+               *errorstring = "JamDecoder: unable to allocate memory for video decoder";
+               jam_close(s);
+               return NULL;
+       }
+
+       // scale support provided by libavcodec
+#ifdef JAM_USELIBAVCODECSCALE
+       s->framewidth = s->info_imagewidth;
+       s->frameheight = s->info_imageheight;
+
+       // min size
+       if (cl_video_libavcodec_minwidth.integer > 0)
+               s->info_imagewidth = max(s->info_imagewidth, (unsigned int)cl_video_libavcodec_minwidth.integer);
+       if (cl_video_libavcodec_minheight.integer > 0)
+               s->info_imageheight = max(s->info_imageheight, (unsigned int)cl_video_libavcodec_minheight.integer);
+
+       // allocate output
+       s->frame_output_buffer = (unsigned char *)Z_Malloc(s->framesize * 4);
+       s->frame_output = AvCodec_AllocFrame();
+       s->frame_output_scale = AvCodec_AllocFrame();
+       if (!s->frame_output_buffer || !s->frame_output || !s->frame_output_scale)
+    {
+               *errorstring = "JamDecoder: failed to allocate LibAvcodec frame";
+               jam_close(s);
                Z_Free(s);
+        return NULL;
        }
-       else if (errorstring != NULL)
-               *errorstring = "unable to allocate memory for stream info structure";
-       return NULL;
+#endif
+
+       // everything is ok
+       // set the module functions
+       s->framenum = 0;
+       video->close = jam_close;
+       video->getwidth = jam_getwidth;
+       video->getheight = jam_getheight;
+       video->getframerate = jam_getframerate;
+       video->decodeframe = jam_video;
+       video->getaspectratio = jam_getaspectratio;
+
+       // set sound
+       size_t namelen;
+       namelen = strlen(filename) + 10;
+       wavename = (char *)Z_Malloc(namelen);
+       if (wavename)
+       {
+               sfx_t* sfx;
+               FS_StripExtension(filename, wavename, namelen);
+               strlcat(wavename, ".wav", namelen);
+               sfx = S_PrecacheSound(wavename, false, false);
+               if (sfx != NULL)
+                       s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);
+               else
+                       s->sndchan = -1;
+               Z_Free(wavename);
+       }
+
+       return s;
 }
 
 // closes a stream
@@ -138,14 +169,32 @@ void jam_close(void *stream)
        jamdecodestream_t *s = (jamdecodestream_t *)stream;
        if (s == NULL)
                return;
-       Z_Free(s->compressed);
-       Z_Free(s->framedata);
-       Z_Free(s->prevframedata);
-       Z_Free(s->videopixels);
+       if (s->frame_compressed)
+               Z_Free(s->frame_compressed);
+       s->frame_compressed = NULL;
+       if (s->frame)
+               Z_Free(s->frame);
+       s->frame = NULL;
+       if (s->frame_prev)
+               Z_Free(s->frame_prev);
+       s->frame_prev = NULL;
        if (s->sndchan != -1)
                S_StopChannel(s->sndchan, true, true);
+       s->sndchan = -1;
        if (s->file)
                FS_Close(s->file);
+       s->file = NULL;
+#ifdef JAM_USELIBAVCODECSCALE
+       if (s->frame_output_buffer)
+               Z_Free(s->frame_output_buffer);
+       s->frame_output_buffer = NULL;
+       if (s->frame_output)
+               AvUtil_Free(s->frame_output);
+       s->frame_output = NULL;
+       if (s->frame_output_scale)
+               AvUtil_Free(s->frame_output_scale);
+       s->frame_output_scale = NULL;
+#endif
        Z_Free(s);
 }
 
@@ -153,8 +202,6 @@ void jam_close(void *stream)
 unsigned int jam_getwidth(void *stream)
 {
        jamdecodestream_t *s = (jamdecodestream_t *)stream;
-       if (s->doubleres)
-               return s->info_imagewidth * 2;
        return s->info_imagewidth;
 }
 
@@ -162,8 +209,6 @@ unsigned int jam_getwidth(void *stream)
 unsigned int jam_getheight(void *stream)
 {
        jamdecodestream_t *s = (jamdecodestream_t *)stream;
-       if (s->doubleres)
-               return s->info_imageheight * 2;
        return s->info_imageheight;
 }
 
@@ -174,6 +219,12 @@ double jam_getframerate(void *stream)
        return s->info_framerate;
 }
 
+// returns aspect ration of the stream
+double jam_getaspectratio(void *stream)
+{
+       jamdecodestream_t *s = (jamdecodestream_t *)stream;
+       return s->info_aspectratio;
+}
 
 // decode JAM frame
 static void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigned char *prevbuf, int outsize, int frametype)
@@ -239,117 +290,89 @@ static void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigne
 int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)
 {
        unsigned char frameHead[16], *b;
-       unsigned int compsize, outsize, i, j;
+       unsigned int compsize, outsize, i;
        jamdecodestream_t *s = (jamdecodestream_t *)stream;
 
-       s->error = DPVSIMPLEDECODEERROR_NONE;
-       if (s->framenum < s->info_frames)
-       {
+       // EOF
+       if (s->framenum >= s->info_frames)
+               return 1;
+       s->framenum++;
+
 readframe:
-               if (FS_Read(s->file, &frameHead, 16))
-               {
-                       compsize = LittleLong(*(frameHead + 8)) - 16;
-                       outsize = LittleLong(*(frameHead + 12));
-                       if (compsize > s->framesize || outsize > s->framesize)
-                               s->error = JAMDECODEERROR_BAD_FRAME_HEADER;
-                       else if (FS_Read(s->file, s->compressed, compsize))
-                       {
-                               // palette goes interleaved with special flag
-                               if (frameHead[0] == 2)
-                               {
-                                       if (compsize == 768)
-                                       {
-                                               memcpy(s->colormap, s->compressed, 768);
-                                               for(i = 0; i < 768; i++)
-                                                       s->colormap[i] = (unsigned char)(bound(0, (s->colormap[i] * s->colorscale) - s->colorsub, 255));
-                                               goto readframe;
-                                       }
-                                       //else
-                                       //      s->error = JAMDECODEERROR_BAD_COLORMAP;
-                               }
-                               else
-                               {
-                                       // decode frame
-                                       // shift buffers to provide current and previous one, decode
-                                       b = s->prevframedata;
-                                       s->prevframedata = s->framedata;
-                                       s->framedata = b;
-                                       jam_decodeframe(s->compressed, s->framedata, s->prevframedata, outsize, frameHead[4]);
-                                       // make 32bit imagepixels from 8bit palettized frame
-                                       if (s->doubleres)
-                                               b = s->videopixels;
-                                       else
-                                               b = (unsigned char *)imagedata;
-                                       for(i = 0; i < s->framesize; i++)
-                                       {
-                                               // bgra
-                                               *b++ = s->colormap[s->framedata[i]*3 + 2];
-                                               *b++ = s->colormap[s->framedata[i]*3 + 1];
-                                               *b++ = s->colormap[s->framedata[i]*3];
-                                               *b++ = 255;
-                                       }
-                                       // nearest 2x
-                                       if (s->doubleres)
-                                       {
-                                               for (i = 0; i < s->info_imageheight; i++)
-                                               {
-                                                       b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2);
-                                                       for (j = 0; j < s->info_imagewidth; j++)
-                                                       {
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];
-                                                               //
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];
-                                                       }
-                                                       b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2 + 1);
-                                                       for (j = 0; j < s->info_imagewidth; j++)
-                                                       {
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];
-                                                               //
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];
-                                                       }
-                                               }
-                                               // do stippling
-                                               if (s->stipple)
-                                               {
-                                                       for (i = 0; i < s->info_imageheight; i++)
-                                                       {
-                                                               b = (unsigned char *)imagedata + (s->info_imagewidth * 4 * 2 * 2 * i);
-                                                               for (j = 0; j < s->info_imagewidth; j++)
-                                                               {
-                                                                       b[0] = b[0] * s->stipple;
-                                                                       b[1] = b[1] * s->stipple;
-                                                                       b[2] = b[2] * s->stipple;
-                                                                       b += 4;
-                                                                       b[0] = b[0] * s->stipple;
-                                                                       b[1] = b[1] * s->stipple;
-                                                                       b[2] = b[2] * s->stipple;
-                                                                       b += 4;
-                                                               }
-                                                       }
-                                               }
-                                       }
+       // read frame header
+       if (!FS_Read(s->file, &frameHead, 16))
+       {
+               Con_Printf("JamDecoder: unexpected EOF on frame %i\n", s->framenum);
+               return 1;
+       }
+       compsize = LittleLong(*(frameHead + 8)) - 16;
+       outsize = LittleLong(*(frameHead + 12));
+       if (compsize > s->framesize || outsize > s->framesize)
+       {
+               Con_Printf("JamDecoder: got bogus header on frame %i\n", s->framenum);
+               return 1;
+       }
 
-                               }
-                       }
-                       else
-                               s->error = JAMDECODEERROR_READERROR;
+       // read frame contents
+       if (!FS_Read(s->file, s->frame_compressed, compsize))
+       {
+               Con_Printf("JamDecoder: unexpected EOF on frame %i\n", s->framenum);
+               return 1;
+       }
+
+       // palette goes interleaved with special flag
+       if (frameHead[0] == 2)
+       {
+               if (compsize == 768)
+               {
+                       memcpy(s->frame_palette, s->frame_compressed, 768);
+                       for(i = 0; i < 768; i++)
+                               s->frame_palette[i] = (unsigned char)(bound(0, (s->frame_palette[i] * s->colorscale) - s->colorsub, 255));
+                       goto readframe;
                }
-               else
-                       s->error = JAMDECODEERROR_READERROR;
        }
        else
-               s->error = DPVSIMPLEDECODEERROR_EOF;
-       return s->error;
+       {
+               // decode frame
+               // shift buffers to provide current and previous one, decode
+               b = s->frame_prev;
+               s->frame_prev = s->frame;
+               s->frame = b;
+               jam_decodeframe(s->frame_compressed, s->frame, s->frame_prev, outsize, frameHead[4]);
+#ifdef JAM_USELIBAVCODECSCALE
+               // make BGRA imagepixels from 8bit palettized frame
+               b = (unsigned char *)s->frame_output_buffer;
+               for(i = 0; i < s->framesize; i++)
+               {
+                       *b++ = s->frame_palette[s->frame[i]*3 + 2];
+                       *b++ = s->frame_palette[s->frame[i]*3 + 1];
+                       *b++ = s->frame_palette[s->frame[i]*3];
+                       *b++ = 255;
+               }
+               // scale
+               AvCodec_FillPicture((AVPicture *)s->frame_output, (uint8_t *)s->frame_output_buffer, PIX_FMT_BGRA, s->framewidth, s->frameheight);
+               AvCodec_FillPicture((AVPicture *)s->frame_output_scale, (uint8_t *)imagedata, PIX_FMT_BGRA, s->info_imagewidth, s->info_imageheight);
+               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); 
+               if (!scale_context)
+               {
+                       Con_Printf("JamDecoder: LibAvcodec: error creating scale context frame %i\n", s->framenum);
+                       return 1;
+               }
+               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))
+                       Con_Printf("JamDecoder: LibAvcodec : error scaling frame\n", s->framenum);
+               SwScale_FreeContext(scale_context); 
+#else
+               // make BGRA imagepixels from 8bit palettized frame
+               b = (unsigned char *)imagedata;
+               for(i = 0; i < s->framesize; i++)
+               {
+                       // bgra
+                       *b++ = s->frame_palette[s->frame[i]*3 + 2];
+                       *b++ = s->frame_palette[s->frame[i]*3 + 1];
+                       *b++ = s->frame_palette[s->frame[i]*3];
+                       *b++ = 255;
+               }
+#endif
+       }
+       return 0;
 }