view libmpdemux/demux_gif.c @ 18150:710d4bc5f8c9

Using channel count, samplerate and input bps values from the container instead of the decoder breaks some DTS samples where the container says the audio has 6 channels but the decoder gives 2. In this case take the number of channels from the decoder instead, the output will almost certainly be badly garbled anyway if the number of channels is wrong. patch by Uoti Urpala, uoti <<.>> urpala <<@>> pp1 <<.>> inet <<.>> fi
author diego
date Wed, 19 Apr 2006 20:12:01 +0000
parents 6b86089c2edd
children d2d9d011203f
line wrap: on
line source

/*
	GIF file parser for MPlayer
	by Joey Parrish
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "config.h"

#ifdef HAVE_GIF

#include "mp_msg.h"
#include "help_mp.h"

#include "stream.h"
#include "demuxer.h"
#include "stheader.h"

#include <gif_lib.h>
static int current_pts = 0;
static unsigned char *pallete = NULL;

#define GIF_SIGNATURE (('G' << 16) | ('I' << 8) | 'F')

#ifndef HAVE_GIF_TVT_HACK
// not supported by certain versions of the library
int my_read_gif(GifFileType *gif, uint8_t *buf, int len) {
  return stream_read(gif->UserData, buf, len);
}
#endif
  
static int gif_check_file(demuxer_t *demuxer)
{
  if (stream_read_int24(demuxer->stream) == GIF_SIGNATURE)
    return DEMUXER_TYPE_GIF;
  return 0;
}

static int demux_gif_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds)
{
  GifFileType *gif = (GifFileType *)demuxer->priv;
  sh_video_t *sh_video = (sh_video_t *)demuxer->video->sh;
  GifRecordType type = UNDEFINED_RECORD_TYPE;
  int len = 0;
  demux_packet_t *dp = NULL;
  ColorMapObject *effective_map = NULL;
  char *buf = NULL;

  while (type != IMAGE_DESC_RECORD_TYPE) {
    if (DGifGetRecordType(gif, &type) == GIF_ERROR) {
      PrintGifError();
      return 0; // oops
    }
    if (type == TERMINATE_RECORD_TYPE)
      return 0; // eof
    if (type == SCREEN_DESC_RECORD_TYPE) {
      if (DGifGetScreenDesc(gif) == GIF_ERROR) {
        PrintGifError();
        return 0; // oops
      }
    }
    if (type == EXTENSION_RECORD_TYPE) {
      int code;
      unsigned char *p = NULL;
      if (DGifGetExtension(gif, &code, &p) == GIF_ERROR) {
        PrintGifError();
        return 0; // oops
      }
      if (code == 0xF9) {
        int frametime = 0;
        if (p[0] == 4) // is the length correct?
          frametime = (p[1] << 8) | p[2]; // set the time, centiseconds
        current_pts += frametime;
      } else if ((code == 0xFE) && (verbose)) { // comment extension
	// print iff verbose
	printf("GIF comment: ");
        while (p != NULL) {
          int length = p[0];
	  char *comments = p + 1;
	  comments[length] = 0;
	  printf("%s", comments);
          if (DGifGetExtensionNext(gif, &p) == GIF_ERROR) {
            PrintGifError();
            return 0; // oops
          }
	}
	printf("\n");
      }
      while (p != NULL) {
        if (DGifGetExtensionNext(gif, &p) == GIF_ERROR) {
          PrintGifError();
          return 0; // oops
        }
      }
    }
  }
  
  if (DGifGetImageDesc(gif) == GIF_ERROR) {
    PrintGifError();
    return 0; // oops
  }

  len = gif->Image.Width * gif->Image.Height;
  dp = new_demux_packet(len);
  buf = malloc(len);
  memset(buf, 0, len);
  memset(dp->buffer, 0, len);
  
  if (DGifGetLine(gif, buf, len) == GIF_ERROR) {
    PrintGifError();
    return 0; // oops
  }

  effective_map = gif->Image.ColorMap;
  if (effective_map == NULL) effective_map = gif->SColorMap;

  {
    int y;

    // copy the pallete
    for (y = 0; y < 256; y++) {
	pallete[(y * 4) + 0] = effective_map->Colors[y].Blue;
	pallete[(y * 4) + 1] = effective_map->Colors[y].Green;
	pallete[(y * 4) + 2] = effective_map->Colors[y].Red;
	pallete[(y * 4) + 3] = 0;
    }

    for (y = 0; y < gif->Image.Height; y++) {
      unsigned char *drow = dp->buffer;
      unsigned char *gbuf = buf + (y * gif->Image.Width);

      drow += gif->Image.Width * (y + gif->Image.Top);
      drow += gif->Image.Left;

      memcpy(drow, gbuf, gif->Image.Width);
    }
  }

  free(buf);

  demuxer->video->dpos++;
  dp->pts = ((float)current_pts) / 100;
  dp->pos = stream_tell(demuxer->stream);
  ds_add_packet(demuxer->video, dp);

  return 1;
}

static demuxer_t* demux_open_gif(demuxer_t* demuxer)
{
  sh_video_t *sh_video = NULL;
  GifFileType *gif = NULL;

  current_pts = 0;
  demuxer->seekable = 0; // FIXME

  // go back to the beginning
  stream_seek(demuxer->stream,demuxer->stream->start_pos);

#ifdef HAVE_GIF_TVT_HACK
  // without the TVT functionality of libungif, a hard seek must be
  // done to the beginning of the file.  this is because libgif is
  // unable to use mplayer's cache, and without this lseek libgif will
  // not read from the beginning of the file and the command will fail.
  // with this hack enabled, you will lose the ability to stream a GIF.
  lseek(demuxer->stream->fd, 0, SEEK_SET);
  gif = DGifOpenFileHandle(demuxer->stream->fd);
#else
  gif = DGifOpen(demuxer->stream, my_read_gif);
#endif
  if (!gif) {
    PrintGifError();
    return NULL;
  }

  // create a new video stream header
  sh_video = new_sh_video(demuxer, 0);

  // make sure the demuxer knows about the new video stream header
  // (even though new_sh_video() ought to take care of it)
  demuxer->video->sh = sh_video;

  // make sure that the video demuxer stream header knows about its
  // parent video demuxer stream (this is getting wacky), or else
  // video_read_properties() will choke
  sh_video->ds = demuxer->video;

  sh_video->disp_w = gif->SWidth;
  sh_video->disp_h = gif->SHeight;

  sh_video->format = mmioFOURCC(8, 'R', 'G', 'B');
  
  sh_video->fps = 5.0f;
  sh_video->frametime = 1.0f / sh_video->fps;
  
  sh_video->bih = malloc(sizeof(BITMAPINFOHEADER) + (256 * 4));
  sh_video->bih->biCompression = sh_video->format;
  sh_video->bih->biBitCount = 8;
  sh_video->bih->biPlanes = 2;
  pallete = (unsigned char *)(sh_video->bih + 1);
  
  demuxer->priv = gif;

  return demuxer;
}

static void demux_close_gif(demuxer_t* demuxer)
{
  GifFileType *gif = (GifFileType *)demuxer->priv;

  if(!gif)
    return;

  if (DGifCloseFile(gif) == GIF_ERROR)
    PrintGifError();
  
  demuxer->stream->fd = 0;
  demuxer->priv = NULL;
}


demuxer_desc_t demuxer_desc_gif = {
  "GIF demuxer",
  "gif",
  "GIF",
  "Joey Parrish",
  "",
  DEMUXER_TYPE_GIF,
  0, // unsafe autodetect
  gif_check_file,
  demux_gif_fill_buffer,
  demux_open_gif,
  demux_close_gif,
  NULL,
  NULL
};

#endif /* HAVE_GIF */