changeset 4628:1504901deed8

Fixed FILM demuxer so that it now plays (my) FILM files correctly; modified Cinepak decoder in order to deal with the deviant CVID data from FILM files
author melanson
date Sun, 10 Feb 2002 01:23:36 +0000
parents 3db7ee2ac7b0
children 745a40983b36
files cinepak.c dec_video.c libmpdemux/demux_film.c
diffstat 3 files changed, 134 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/cinepak.c	Sun Feb 10 01:07:58 2002 +0000
+++ b/cinepak.c	Sun Feb 10 01:23:36 2002 +0000
@@ -36,7 +36,7 @@
 typedef struct {
 	cvid_codebook *v4_codebook[MAX_STRIPS];
 	cvid_codebook *v1_codebook[MAX_STRIPS];
-	int strip_num;
+	unsigned long strip_num;
 } cinepak_info;
 
 
@@ -392,6 +392,7 @@
 long len, top_size, chunk_size;
 unsigned char *frm_ptr, *frm_end;
 int i, cur_strip, d0, d1, d2, d3, frm_stride, bpp = 3;
+int modulo;
 void (*read_codebook)(cvid_codebook *c, int mode) = read_codebook_24;
 void (*cvid_v1)(unsigned char *frm, unsigned char *end, int stride, cvid_codebook *cb) = cvid_v1_24;
 void (*cvid_v4)(unsigned char *frm, unsigned char *end, int stride, cvid_codebook *cb0,
@@ -498,7 +499,7 @@
 		x = 0;
 		if(x1 != width) 
 			printf("CVID: Warning x1 (%ld) != width (%d)\n", x1, width);
-
+x1 = width;
 #if DBUG
 	printf("   %d) %04lx %04ld <%ld,%ld> <%ld,%ld> yt %ld  %d\n",
 		cur_strip, strip_id, top_size, x0, y0, x1, y1, y_bottom);
@@ -520,9 +521,12 @@
 					/* -------------------- Codebook Entries -------------------- */
 				case 0x2000:
 				case 0x2200:
+					modulo = chunk_size % 6;
 					codebook = (chunk_id == 0x2200 ? v1_codebook : v4_codebook);
-					cnum = chunk_size/6;
+					cnum = (chunk_size - modulo) / 6;
 					for(i = 0; i < cnum; i++) read_codebook(codebook+i, 0);
+					while (modulo--)
+						get_byte();
 					break;
 
 				case 0x2400:
@@ -537,7 +541,7 @@
 					codebook = (chunk_id == 0x2300 ? v1_codebook : v4_codebook);
 
 					ci = 0;
-					while(chunk_size > 0)
+					while(chunk_size > 3)
 						{
 						flag = get_long();
 						chunk_size -= 4;
--- a/dec_video.c	Sun Feb 10 01:07:58 2002 +0000
+++ b/dec_video.c	Sun Feb 10 01:23:36 2002 +0000
@@ -824,9 +824,14 @@
   //--------------------  Decode a frame: -----------------------
 switch(sh_video->codec->driver){
  case VFM_CINEPAK:
-   decode_cinepak(sh_video->context, start, in_size, sh_video->our_out_buffer,
-      sh_video->disp_w, sh_video->disp_h, (out_fmt==IMGFMT_YUY2)?16:(out_fmt&255));
-   blit_frame = 3;
+   if (in_size == 0)
+     blit_frame = 0;
+   else
+   {
+     decode_cinepak(sh_video->context, start, in_size, sh_video->our_out_buffer,
+       sh_video->disp_w, sh_video->disp_h, (out_fmt==IMGFMT_YUY2)?16:(out_fmt&255));
+     blit_frame = 3;
+   }
    break;
 #ifdef USE_XANIM
   case VFM_XANIM: {
--- a/libmpdemux/demux_film.c	Sun Feb 10 01:07:58 2002 +0000
+++ b/libmpdemux/demux_film.c	Sun Feb 10 01:23:36 2002 +0000
@@ -1,6 +1,9 @@
 /*
 	FILM file parser for the MPlayer program
 	by Mike Melanson
+
+        Details of the FILM file format can be found at:
+          http://www.pcisys.net/~melanson/codecs/
 */
 
 #include <stdio.h>
@@ -20,6 +23,10 @@
 #define CHUNK_FDSC mmioFOURCC('F', 'D', 'S', 'C')
 #define CHUNK_STAB mmioFOURCC('S', 'T', 'A', 'B')
 
+#define VERSION_1_01 mmioFOURCC('1', '.', '0', '1')
+
+#define MAGIC_FPS_CONSTANT 27
+
 typedef struct _film_chunk_t
 {
   off_t chunk_offset;
@@ -39,6 +46,8 @@
   int total_video_chunks;
   int total_audio_sample_count;
   film_chunk_t *chunks;
+  unsigned int ticks;
+  unsigned int film_version;
 } film_data_t;
 
 #if 0
@@ -65,7 +74,11 @@
 //     1 = successfully read a packet
 int demux_film_fill_buffer(demuxer_t *demuxer)
 {
+  int i;
+  unsigned char byte_swap;
+  int cvid_size;
   sh_video_t *sh_video = demuxer->video->sh;
+  sh_audio_t *sh_audio = demuxer->audio->sh;
   film_data_t *film_data = (film_data_t *)demuxer->priv;
   film_chunk_t film_chunk;
 
@@ -77,16 +90,82 @@
 
   // position stream and fetch chunk
   stream_seek(demuxer->stream, film_chunk.chunk_offset);
+
+  // load the chunks manually (instead of using ds_read_packet()), since
+  // they require some adjustment
+  // (all ones in flags1 indicates an audio chunk)
   if (film_chunk.flags1 == 0xFFFFFFFF)
-    ds_read_packet(demuxer->audio, demuxer->stream, film_chunk.chunk_size,
-      0,  /* pts */
-      film_chunk.chunk_offset, 0);
+  {
+    demux_packet_t* dp=new_demux_packet(film_chunk.chunk_size);
+    stream_read(demuxer->stream, dp->buffer, film_chunk.chunk_size);
+    dp->pts = 0;
+    dp->pos = film_chunk.chunk_offset;
+    dp->flags = 0;
+
+    // adjust the data before queuing it:
+    //   8-bit: signed -> unsigned
+    //  16-bit: big-endian -> little-endian
+    if (sh_audio->wf->wBitsPerSample == 8)
+      for (i = 0; i < film_chunk.chunk_size; i++)
+        dp->buffer[i] += 128;
+    else
+      for (i = 0; i < film_chunk.chunk_size; i += 2)
+      {
+        byte_swap = dp->buffer[i];
+        dp->buffer[i] = dp->buffer[i + 1];
+        dp->buffer[i + 1] = byte_swap;
+      }
+
+    // append packet to DS stream
+    ds_add_packet(demuxer->audio, dp);
+    film_data->current_chunk++;
+  }
   else
-    ds_read_packet(demuxer->video, demuxer->stream, film_chunk.chunk_size,
-      film_chunk.video_chunk_number / sh_video->fps,
-      film_chunk.chunk_offset, 0);
+  {
+    // check the tick to see if it's time to dispatch a new frame
+    if ((film_data->film_version == VERSION_1_01) &&
+        ((film_chunk.flags1 & 0x7FFFFFFF) != film_data->ticks++))
+    {
+      demux_packet_t* dp=new_demux_packet(0);
+      dp->pts = 0;
+      dp->pos = 0;
+      dp->flags = 0;
+      ds_add_packet(demuxer->video, dp);
+    }
+    // if the demuxer is dealing with CVID data, deal with it a special way
+    else if (sh_video->format == mmioFOURCC('c', 'v', 'i', 'd'))
+    {
+      // account for 2 extra bytes
+      demux_packet_t* dp=new_demux_packet(film_chunk.chunk_size - 2);
 
-  film_data->current_chunk++;
+      // these CVID data chunks appear to have 2 extra bytes; skip them
+      stream_read(demuxer->stream, dp->buffer, 10);
+      stream_skip(demuxer->stream, 2);
+      stream_read(demuxer->stream, dp->buffer + 10, film_chunk.chunk_size - 12);
+      dp->pts = film_chunk.video_chunk_number / sh_video->fps;
+      dp->pos = film_chunk.chunk_offset;
+      dp->flags = (film_chunk.flags1 & 0x80000000) ? 1 : 0;
+
+      // fix the CVID chunk size by adding 6
+      cvid_size = (dp->buffer[1] << 16) | (dp->buffer[2] << 8) | dp->buffer[3];
+      cvid_size += 6;
+      dp->buffer[1] = (cvid_size >> 16) & 0xFF;
+      dp->buffer[2] = (cvid_size >>  8) & 0xFF;
+      dp->buffer[3] = (cvid_size >>  0) & 0xFF;
+
+      // append packet to DS stream
+      ds_add_packet(demuxer->video, dp);
+      film_data->current_chunk++;
+    }
+    else
+    {
+      ds_read_packet(demuxer->video, demuxer->stream, film_chunk.chunk_size,
+        film_chunk.video_chunk_number / sh_video->fps,  /* pts */
+        film_chunk.chunk_offset, (film_chunk.flags1 & 0x80000000) ? 1 : 0);
+      film_data->current_chunk++;
+    }
+  }
+
   return 1;
 }
 
@@ -110,6 +189,7 @@
   film_data->current_chunk = 0;
   film_data->total_video_chunks = 0;
   film_data->chunks = NULL;
+  film_data->ticks = 0;
 
   // go back to the beginning
   stream_reset(demuxer->stream);
@@ -127,12 +207,13 @@
   // get the header size, which implicitly points past the header and
   // to the start of the data
   header_size = stream_read_dword(demuxer->stream);
+  film_data->film_version = stream_read_fourcc(demuxer->stream);
   demuxer->movi_start = header_size;
   demuxer->movi_end = demuxer->stream->end_pos;
   header_size -= 16;
 
   // skip to where the next chunk should be
-  stream_skip(demuxer->stream, 8);
+  stream_skip(demuxer->stream, 4);
 
   // traverse through the header
   while (header_size > 0)
@@ -158,11 +239,10 @@
         sh_video->format = video_format;
         sh_video->disp_h = stream_read_dword(demuxer->stream);
         sh_video->disp_w = stream_read_dword(demuxer->stream);
-        sh_video->fps = stream_read_char(demuxer->stream);
-        sh_video->frametime = 1/sh_video->fps;
+        stream_skip(demuxer->stream, 1);  // unknown byte
         mp_msg(MSGT_DECVIDEO, MSGL_V,
-          "  FILM video: %d x %d, %f fps\n", sh_video->disp_w,
-          sh_video->disp_h, sh_video->fps);
+          "  FILM video: %d x %d\n", sh_video->disp_w,
+          sh_video->disp_h);
       }
       else
         stream_skip(demuxer->stream, 9);
@@ -176,7 +256,6 @@
         demuxer->audio->sh = sh_audio;
         sh_audio->ds = demuxer->audio;
 
-        // go through the bother of making a WAVEFORMATEX structure
         sh_audio->wf = (WAVEFORMATEX *)malloc(sizeof(WAVEFORMATEX));
 
         // uncompressed PCM format
@@ -187,7 +266,8 @@
         stream_skip(demuxer->stream, 1);  // skip unknown byte
         sh_audio->wf->nSamplesPerSec = stream_read_word(demuxer->stream);
         sh_audio->wf->nAvgBytesPerSec = 
-          sh_audio->wf->nSamplesPerSec * sh_audio->wf->wBitsPerSample / 8;
+          sh_audio->wf->nSamplesPerSec * sh_audio->wf->wBitsPerSample 
+          * sh_audio->wf->nChannels / 8;
         stream_skip(demuxer->stream, 6);  // skip the rest of the unknown
 
         mp_msg(MSGT_DECVIDEO, MSGL_V,
@@ -201,8 +281,15 @@
 
     case CHUNK_STAB:
       mp_msg(MSGT_DECVIDEO, MSGL_V, "parsing STAB chunk\n");
-      // skip unknown dword
-      stream_skip(demuxer->stream, 4);
+
+      // FPS hack based on empirical observation
+      if (sh_video)
+      {
+        sh_video->fps = stream_read_dword(demuxer->stream);
+        if (film_data->film_version != VERSION_1_01)
+          sh_video->fps = MAGIC_FPS_CONSTANT;
+        sh_video->frametime = 1 / sh_video->fps;
+      }
 
       // fetch the number of chunks
       film_data->total_chunks = stream_read_dword(demuxer->stream);
@@ -220,22 +307,28 @@
         film_chunk = film_data->chunks[i];
         film_chunk.chunk_offset = 
           demuxer->movi_start + stream_read_dword(demuxer->stream);
-        film_chunk.chunk_size = stream_read_dword(demuxer->stream) - 8;
+        film_chunk.chunk_size = stream_read_dword(demuxer->stream);
         film_chunk.flags1 = stream_read_dword(demuxer->stream);
         film_chunk.flags2 = stream_read_dword(demuxer->stream);
         film_data->chunks[i] = film_chunk;
 
         // audio housekeeping
-        if ((film_chunk.flags1 == 0xFFFFFFFF) && 
-          (film_chunk.chunk_size > largest_audio_chunk))
-          largest_audio_chunk = film_chunk.chunk_size;
-        film_data->total_audio_sample_count +=
-          (chunk_size / sh_audio->wf->nChannels);
+        if (sh_audio)
+        {
+          if ((film_chunk.flags1 == 0xFFFFFFFF) && 
+            (film_chunk.chunk_size > largest_audio_chunk))
+            largest_audio_chunk = film_chunk.chunk_size;
+          film_data->total_audio_sample_count +=
+            (chunk_size / sh_audio->wf->nChannels);
+        }
 
         // video housekeeping
-        if (film_chunk.flags1 != 0xFFFFFFFF)
-          film_chunk.video_chunk_number =
-            film_data->total_video_chunks++;
+        if (sh_video)
+        {
+          if (film_chunk.flags1 != 0xFFFFFFFF)
+            film_chunk.video_chunk_number =
+              film_data->total_video_chunks++;
+        }
       }
       break;