[MPlayer-dev-eng] patch: YUV4MPEG2 demuxer

Rik Snel rsnel at cube.dyndns.org
Wed Dec 26 19:02:34 CET 2001


Hello all,

These patches add support for the YUV4MPEG2 file format, this format
is used by mjpegtools-1.5.x or higher. The format is documented at the end
of libmpdemux/yuv4mpeg.h . Users of the mjpegtools will we able to
mencode(1) their captures. (for more info on mjpegtools, see
mjpeg.sourceforge.net)

Why is it needed. The mjpegtools capture in the AVI format, so it is
already possible to for them to mencode(1). The problem is that the
mjpegtools contain nice filters (motion compensating denoiser, inverse
telecine), all these filters speak YUV4MPEG2, so YUV4MPEG2 input is really
useful. (as in lav2yuv bla.avi | yuvdenoise | yuvfancyfilter | mencoder -)

Explanation of patch files:

-patch.y4m

This contains demux_y4m.c yuv4mpeg.* (ripped from mjpegtools) and
modifications to the Makefile and demuxer.c . This shouldn't break
anything (and it worked for me). Seeking is not supported, but can be done
(but I didn't look at it).

-patch.stream

It changes stuff in the heart of the streaming code. The problem is that
the mjpegtools are too slow for mplayer (when piping through stdin).

Scenario: mjpegtools generate header "YUV4MPEG2 BLA BLA BLA\n". The first
call to read_stream_char reads the header. (because the rest is not
available yet) Some check_fileformat functions want to see more of the
file so stream_fill_buffer will be called and it will overwrite the header
in the buffer. (it will never come back) By the time y4m_check_file is
reached, it cannot determinate the file type.

Solution: APPEND to the buffer if buf_len < STREAM_WATERLEVEL (currently
1984) and overwrite the buffer otherwise.

I implement this solution in stream.c. The implementation is a bit ugly
because it adds a second exit point in the function stream_fill_buffer.
I've tried to play some (normal and YUV4MPEG2) files, and they worked,

Small note: YUV4MPEG2 doesn't do sound, people who use mencoder(1) should
add the sound later (transcode will probably do that for them)

The patches apply against current CVS.

Greetings,

Rik.

PS: I'm not sure if patch.y4m qualifies as 'really big' (als in
DOC/tech/patches.txt), so I did not compress it.

--------
Nothing is ever a total loss; it can always serve as a bad example.
-------------- next part --------------
diff -Naur main/libmpdemux/stream.c main.dev/libmpdemux/stream.c
--- main/libmpdemux/stream.c	Mon Dec  3 18:14:36 2001
+++ main.dev/libmpdemux/stream.c	Wed Dec 26 15:37:17 2001
@@ -40,15 +40,27 @@
   switch(s->type){
   case STREAMTYPE_FILE:
   case STREAMTYPE_STREAM:
+  //printf("(s->buf_len-1)%2048+1=%d\n", (s->buf_len-1)%2048+1);
+  if ((s->buf_len-1)%STREAM_BUFFER_SIZE + 1 > STREAM_WATERLEVEL) {
+      s->buf_len=0;
+      s->buf_pos=0;
+  } else {
+	  //printf("Append: buf_pos=%d, buf_len=%d.\n", s->buf_pos, s->buf_len);
+      s->buf_len=s->buf_len%STREAM_BUFFER_SIZE;
+  }
 #ifdef STREAMING
     if( s->streaming_ctrl!=NULL ) {
-	    len=s->streaming_ctrl->streaming_read(s->fd,s->buffer,STREAM_BUFFER_SIZE, s->streaming_ctrl);break;
+	    len=s->streaming_ctrl->streaming_read(s->fd,s->buffer+s->buf_len,STREAM_BUFFER_SIZE-s->buf_len, s->streaming_ctrl);
     } else {
-      len=read(s->fd,s->buffer,STREAM_BUFFER_SIZE);break;
+      len=read(s->fd,s->buffer+s->buf_len,STREAM_BUFFER_SIZE-s->buf_len);
     }
 #else
-    len=read(s->fd,s->buffer,STREAM_BUFFER_SIZE);break;
+    len=read(s->fd,s->buffer+s->buf_len,STREAM_BUFFER_SIZE-s->buf_len);
 #endif
+  if(len<=0){ s->eof=1; s->buf_pos=s->buf_len=0; return 0; }
+  s->buf_len+=len;
+  s->pos+=len;
+  return len;
 #ifdef HAVE_VCD
   case STREAMTYPE_VCD:
 #ifdef VCD_CACHE
diff -Naur main/libmpdemux/stream.h main.dev/libmpdemux/stream.h
--- main/libmpdemux/stream.h	Wed Dec 26 13:17:46 2001
+++ main.dev/libmpdemux/stream.h	Wed Dec 26 15:37:17 2001
@@ -2,6 +2,7 @@
 #define __STREAM_H
 
 #define STREAM_BUFFER_SIZE 2048
+#define STREAM_WATERLEVEL 1984
 
 #define STREAMTYPE_FILE 0
 #define STREAMTYPE_VCD  1
@@ -14,6 +15,7 @@
 #define VCD_SECTOR_OFFS 24
 #define VCD_SECTOR_DATA 2324
 
+#include <stdio.h> /* for printf in an inline function */
 #ifdef STREAMING
 #include "network.h"
 #endif
-------------- next part --------------
diff -Naur main/libmpdemux/Makefile main.dev/libmpdemux/Makefile
--- main/libmpdemux/Makefile	Sun Dec 23 23:08:47 2001
+++ main.dev/libmpdemux/Makefile	Wed Dec 26 15:37:15 2001
@@ -3,7 +3,7 @@
 
 include ../config.mak
 
-SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c demux_mpg.c demux_viv.c demuxer.c dvdauth.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c frequencies.c demux_fli.c
+SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c demux_mpg.c demux_viv.c demuxer.c dvdauth.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c frequencies.c demux_fli.c demux_y4m.c yuv4mpeg.c yuv4mpeg_ratio.c
 ifeq ($(STREAMING),yes)
 SRCS += asf_streaming.c url.c http.c network.c rtp.c
 endif
diff -Naur main/libmpdemux/demux_y4m.c main.dev/libmpdemux/demux_y4m.c
--- main/libmpdemux/demux_y4m.c	Thu Jan  1 01:00:00 1970
+++ main.dev/libmpdemux/demux_y4m.c	Wed Dec 26 16:57:39 2001
@@ -0,0 +1,141 @@
+//  Y4M file parser by Rik Snel (using yuv4mpeg*.[ch] from
+//  mjpeg.sourceforge.net) (derived from demux_viv.c)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h> /* strtok */
+
+#include "config.h"
+#include "mp_msg.h"
+#include "help_mp.h"
+#include "yuv4mpeg.h"
+
+#include "stream.h"
+#include "demuxer.h"
+#include "stheader.h"
+#include "bswap.h"
+
+typedef struct {
+    int framenum; 
+    y4m_stream_info_t* si;
+} y4m_priv_t;
+
+int y4m_check_file(demuxer_t* demuxer){
+    int orig_pos = stream_tell(demuxer->stream);
+    char buf[10];
+    
+    mp_msg(MSGT_DEMUX, MSGL_V, "Checking for YUV4MPEG2\n");
+    
+    stream_read(demuxer->stream, buf, 9);
+    buf[9] = 0;
+
+    if (strncmp("YUV4MPEG2", buf, 9)) {
+	    mp_msg(MSGT_DEMUX, MSGL_DBG2, "Failed: YUV4MPEG2\n");
+	    return 0;
+    }
+
+    mp_msg(MSGT_DEMUX,MSGL_DBG2,"Success: YUV4MPEG2\n");
+
+    stream_seek(demuxer->stream, orig_pos);
+
+return 1;
+}
+
+
+// return value:
+//     0 = EOF or no stream found
+//     1 = successfully read a packet
+int demux_y4m_fill_buffer(demuxer_t *demux) {
+  demux_stream_t *ds=demux->video;
+  demux_packet_t *dp;
+  y4m_priv_t *priv=demux->priv;
+  y4m_frame_info_t fi;
+  unsigned char *buf[3];
+  int err, size;
+
+  demux->filepos=stream_tell(demux->stream);
+
+  size = ((sh_video_t*)ds->sh)->disp_w*((sh_video_t*)ds->sh)->disp_h;
+
+  dp = new_demux_packet(3*size/2);
+
+  /* swap U and V components */
+  buf[0] = dp->buffer;
+  buf[1] = dp->buffer + 5*size/4;
+  buf[2] = dp->buffer + size;
+
+  if ((err=y4m_read_frame(demux->stream, priv->si, &fi, buf)) != Y4M_OK) {
+      mp_msg(MSGT_DEMUX, MSGL_V, "error reading frame %s\n", y4m_strerr(err));
+      return 0;
+  }
+
+  /* This seems to be the right way to calculate the presentation time stamp */
+  dp->pts=(float)priv->framenum/((sh_video_t*)ds->sh)->fps;
+  priv->framenum++;
+  dp->pos=demux->filepos;
+  dp->flags=0;
+  ds_add_packet(ds, dp);
+
+  return 1;
+}
+
+void demux_open_y4m(demuxer_t* demuxer){
+    y4m_priv_t* priv;
+    y4m_ratio_t framerate;
+    sh_video_t* sh=new_sh_video(demuxer,0);
+    int err;
+
+    demuxer->priv = malloc(sizeof(y4m_priv_t));
+    priv = demuxer->priv;
+
+    priv->framenum = 0;
+    priv->si = malloc(sizeof(y4m_stream_info_t));
+
+    y4m_init_stream_info(priv->si);
+    if ((err=y4m_read_stream_header(demuxer->stream, priv->si)) != Y4M_OK) 
+	    mp_msg(MSGT_DEMUXER, MSGL_FATAL, "error parsing YUV4MPEG header: %s\n", y4m_strerr(err));
+	    
+    sh->format = mmioFOURCC('Y', 'V', '1', '2');
+
+    if(!sh->fps) {
+        framerate = y4m_si_get_framerate(priv->si);
+        if (framerate.d != 0)
+            sh->fps=(float)framerate.n/(float)framerate.d;
+        else
+            sh->fps=15.0f;
+    }
+    sh->frametime=1.0f/sh->fps;
+
+    sh->disp_w = y4m_si_get_width(priv->si);
+    sh->disp_h = y4m_si_get_height(priv->si);
+
+    sh->bih=malloc(sizeof(BITMAPINFOHEADER));
+    memset(sh->bih,0,sizeof(BITMAPINFOHEADER));
+    sh->bih->biSize=40;
+    sh->bih->biWidth = priv->si->width;
+    sh->bih->biHeight = priv->si->height;
+    sh->bih->biPlanes=3;
+    sh->bih->biBitCount=12;
+    sh->bih->biCompression=sh->format;
+    sh->bih->biSizeImage=sh->bih->biWidth*sh->bih->biHeight*3/2; /* YV12 */
+
+    demuxer->video->sh=sh;
+    sh->ds=demuxer->video;
+    demuxer->video->id=0;
+		
+    /* disable seeking, lazy */
+    demuxer->seekable = 0;
+
+    printf("YUV4MPEG2 Video stream %d size: display: %dx%d, codec: %ux%u\n",
+            demuxer->video->id, sh->disp_w, sh->disp_h, sh->bih->biWidth,
+            sh->bih->biHeight);
+}
+
+void demux_close_y4m(demuxer_t *demuxer)
+{
+    y4m_fini_stream_info(((y4m_priv_t*)demuxer->priv)->si);
+    free(((y4m_priv_t*)demuxer->priv)->si);
+    free(demuxer->priv);
+    return;
+}
diff -Naur main/libmpdemux/demuxer.c main.dev/libmpdemux/demuxer.c
--- main/libmpdemux/demuxer.c	Mon Dec 17 01:24:03 2001
+++ main.dev/libmpdemux/demuxer.c	Wed Dec 26 15:37:15 2001
@@ -167,6 +167,7 @@
 extern int demux_tv_fill_buffer(demuxer_t *demux, tvi_handle_t *tvh);
 extern int demux_open_tv(demuxer_t *demuxer, tvi_handle_t *tvh);
 #endif
+int demux_y4m_fill_buffer(demuxer_t *demux);
 
 int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){
   // Note: parameter 'ds' can be NULL!
@@ -184,6 +185,7 @@
 #ifdef USE_TV
     case DEMUXER_TYPE_TV: return demux_tv_fill_buffer(demux, tv_handler);
 #endif
+    case DEMUXER_TYPE_Y4M: return demux_y4m_fill_buffer(demux);
   }
   return 0;
 }
@@ -361,6 +363,8 @@
 
 extern int vivo_check_file(demuxer_t *demuxer);
 extern void demux_open_vivo(demuxer_t *demuxer);
+extern int y4m_check_file(demuxer_t *demuxer);
+extern void demux_open_y4m(demuxer_t *demuxer);
 
 demuxer_t* demux_open(stream_t *stream,int file_format,int audio_id,int video_id,int dvdsub_id){
 
@@ -425,6 +429,14 @@
       file_format=DEMUXER_TYPE_VIVO;
   }
 }
+//=============== Try to open as Y4M file: =================
+if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_Y4M){
+  demuxer=new_demuxer(stream,DEMUXER_TYPE_Y4M,audio_id,video_id,dvdsub_id);
+  if(y4m_check_file(demuxer)){
+      mp_msg(MSGT_DEMUXER,MSGL_INFO,"Detected YUV4MPEG2 file format!\n");
+      file_format=DEMUXER_TYPE_Y4M;
+  }
+}
 //=============== Try to open as FLI file: =================
 if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_FLI){
   demuxer=new_demuxer(stream,DEMUXER_TYPE_FLI,audio_id,video_id,dvdsub_id);
@@ -515,6 +527,10 @@
  }
  case DEMUXER_TYPE_VIVO: {
   demux_open_vivo(demuxer);
+  break;
+ }
+ case DEMUXER_TYPE_Y4M: {
+  demux_open_y4m(demuxer);
   break;
  }
  case DEMUXER_TYPE_ASF: {
diff -Naur main/libmpdemux/demuxer.h main.dev/libmpdemux/demuxer.h
--- main/libmpdemux/demuxer.h	Sun Nov 25 00:58:12 2001
+++ main.dev/libmpdemux/demuxer.h	Wed Dec 26 15:37:15 2001
@@ -13,6 +13,7 @@
 #define DEMUXER_TYPE_VIVO 8
 #define DEMUXER_TYPE_TV 9
 #define DEMUXER_TYPE_FLI 10
+#define DEMUXER_TYPE_Y4M 11
 
 #define DEMUXER_TIME_NONE 0
 #define DEMUXER_TIME_PTS 1
diff -Naur main/libmpdemux/yuv4mpeg.c main.dev/libmpdemux/yuv4mpeg.c
--- main/libmpdemux/yuv4mpeg.c	Thu Jan  1 01:00:00 1970
+++ main.dev/libmpdemux/yuv4mpeg.c	Wed Dec 26 15:37:15 2001
@@ -0,0 +1,766 @@
+/*
+ *  yuv4mpeg.c:  Functions for reading and writing "new" YUV4MPEG streams
+ *
+ *  Copyright (C) 2001 Matthew J. Marjanovic <maddog at mir.com>
+ *
+ *  This file is ripped from the lavtools package (mjpeg.sourceforge.net)
+ *  Ported to mplayer by Rik Snel <snel at phys.uu.nl>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "yuv4mpeg.h"
+#include "yuv4mpeg_intern.h"
+#include "mp_msg.h"
+
+static int _y4mparam_allow_unknown_tags = 1;  /* default is forgiveness */
+
+static void *(*_y4m_alloc)(size_t bytes) = malloc;
+static void (*_y4m_free)(void *ptr) = free;
+
+int y4m_allow_unknown_tags(int yn) {
+	int old = _y4mparam_allow_unknown_tags;
+  	if (yn >= 0) _y4mparam_allow_unknown_tags = (yn) ? 1 : 0;
+  	return old;
+}
+
+
+
+/*************************************************************************
+ *
+ * Convenience functions for fd read/write
+ *
+ *   - guaranteed to transfer entire payload (or fail)
+ *   - returns:
+ *               0 on complete success
+ *               +(# of remaining bytes) on eof (for y4m_read)
+ *               -(# of rem. bytes) on error (and ERRNO should be set)
+ *     
+ *************************************************************************/
+
+
+ssize_t y4m_read(stream_t *s, char *buf, size_t len)
+{
+   ssize_t n;
+
+   while (len > 0) {
+     n = stream_read(s, buf, len);
+     if (n <= 0) {
+       /* return amount left to read */
+       if (n == 0)
+	 return len;  /* n == 0 --> eof */
+       else
+	 return -len; /* n < 0 --> error */
+     }
+     buf += n;
+     len -= n;
+   }
+   return 0;
+}
+
+
+#if 0 /* not needed */
+ssize_t y4m_write(int fd, char *buf, size_t len)
+{
+   ssize_t n;
+
+   while (len > 0) {
+     n = write(fd, buf, len);
+     if (n < 0) return -len;  /* return amount left to write */
+     buf += n;
+     len -= n;
+   }
+   return 0;
+}
+#endif
+
+
+/*************************************************************************
+ *
+ * "Extra tags" handling
+ *
+ *************************************************************************/
+
+
+static char *y4m_new_xtag()
+{
+  return _y4m_alloc(Y4M_MAX_XTAG_SIZE * sizeof(char));
+}
+
+
+void y4m_init_xtag_list(y4m_xtag_list_t *xtags)
+{
+  int i;
+  xtags->count = 0;
+  for (i = 0; i < Y4M_MAX_XTAGS; i++) {
+    xtags->tags[i] = NULL;
+  }
+}
+
+
+void y4m_fini_xtag_list(y4m_xtag_list_t *xtags)
+{
+  int i;
+  for (i = 0; i < Y4M_MAX_XTAGS; i++) {
+    if (xtags->tags[i] != NULL) {
+      _y4m_free(xtags->tags[i]);
+      xtags->tags[i] = NULL;
+    }
+  }
+  xtags->count = 0;
+}
+
+
+void y4m_copy_xtag_list(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src)
+{
+  int i;
+  for (i = 0; i < src->count; i++) {
+    if (dest->tags[i] == NULL) 
+      dest->tags[i] = y4m_new_xtag();
+    strncpy(dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE);
+  }
+  dest->count = src->count;
+}
+
+
+
+static int y4m_snprint_xtags(char *s, int maxn, y4m_xtag_list_t *xtags)
+{
+  int i, room;
+  
+  for (i = 0, room = maxn - 1; i < xtags->count; i++) {
+    int n = snprintf(s, room + 1, " %s", xtags->tags[i]);
+    if ((n < 0) || (n > room)) return Y4M_ERR_HEADER;
+    s += n;
+    room -= n;
+  }
+  s[0] = '\n';  /* finish off header with newline */
+  s[1] = '\0';  /* ...and end-of-string           */
+  return Y4M_OK;
+}
+
+
+int y4m_xtag_count(const y4m_xtag_list_t *xtags)
+{
+  return xtags->count;
+}
+
+
+const char *y4m_xtag_get(const y4m_xtag_list_t *xtags, int n)
+{
+  if (n >= xtags->count)
+    return NULL;
+  else
+    return xtags->tags[n];
+}
+
+
+int y4m_xtag_add(y4m_xtag_list_t *xtags, const char *tag)
+{
+  if (xtags->count >= Y4M_MAX_XTAGS) return Y4M_ERR_XXTAGS;
+  if (xtags->tags[xtags->count] == NULL) {
+    xtags->tags[xtags->count] = y4m_new_xtag();
+  }
+  strncpy(xtags->tags[xtags->count], tag, Y4M_MAX_XTAG_SIZE);
+  (xtags->count)++;
+  return Y4M_OK;
+}
+
+
+int y4m_xtag_remove(y4m_xtag_list_t *xtags, int n)
+{
+  int i;
+  char *q;
+
+  if ((n < 0) || (n >= xtags->count)) return Y4M_ERR_RANGE;
+  q = xtags->tags[n];
+  for (i = n; i < (xtags->count - 1); i++)
+    xtags->tags[i] = xtags->tags[i+1];
+  xtags->tags[i] = q;
+  (xtags->count)--;
+  return Y4M_OK;
+}
+
+
+int y4m_xtag_clearlist(y4m_xtag_list_t *xtags)
+{
+  xtags->count = 0;
+  return Y4M_OK;
+}
+
+
+int y4m_xtag_addlist(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src)
+{
+  int i, j;
+
+  if ((dest->count + src->count) > Y4M_MAX_XTAGS) return Y4M_ERR_XXTAGS;
+  for (i = dest->count, j = 0;
+       j < src->count;
+       i++, j++) {
+    if (dest->tags[i] == NULL) 
+      dest->tags[i] = y4m_new_xtag();
+    strncpy(dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE);
+  }
+  dest->count += src->count;
+  return Y4M_OK;
+}  
+
+
+/*************************************************************************
+ *
+ * Creators/destructors for y4m_*_info_t structures
+ *
+ *************************************************************************/
+
+
+void y4m_init_stream_info(y4m_stream_info_t *info)
+{
+  if (info == NULL) return;
+  /* initialize info */
+  info->width = Y4M_UNKNOWN;
+  info->height = Y4M_UNKNOWN;
+  info->interlace = Y4M_UNKNOWN;
+  info->framerate = y4m_fps_UNKNOWN;
+  info->sampleaspect = y4m_sar_UNKNOWN;
+  y4m_init_xtag_list(&(info->x_tags));
+}
+
+
+void y4m_copy_stream_info(y4m_stream_info_t *dest, y4m_stream_info_t *src)
+{
+  if ((dest == NULL) || (src == NULL)) return;
+  /* copy info */
+  dest->width = src->width;
+  dest->height = src->height;
+  dest->interlace = src->interlace;
+  dest->framerate = src->framerate;
+  dest->sampleaspect = src->sampleaspect;
+  y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags));
+}
+
+
+void y4m_fini_stream_info(y4m_stream_info_t *info)
+{
+  if (info == NULL) return;
+  y4m_fini_xtag_list(&(info->x_tags));
+}
+
+
+void y4m_si_set_width(y4m_stream_info_t *si, int width)
+{
+  si->width = width;
+  si->framelength = (si->height * si->width) * 3 / 2;
+}
+
+int y4m_si_get_width(y4m_stream_info_t *si)
+{ return si->width; }
+
+void y4m_si_set_height(y4m_stream_info_t *si, int height)
+{
+  si->height = height; 
+  si->framelength = (si->height * si->width) * 3 / 2;
+}
+
+int y4m_si_get_height(y4m_stream_info_t *si)
+{ return si->height; }
+
+void y4m_si_set_interlace(y4m_stream_info_t *si, int interlace)
+{ si->interlace = interlace; }
+
+int y4m_si_get_interlace(y4m_stream_info_t *si)
+{ return si->interlace; }
+
+void y4m_si_set_framerate(y4m_stream_info_t *si, y4m_ratio_t framerate)
+{ si->framerate = framerate; }
+
+y4m_ratio_t y4m_si_get_framerate(y4m_stream_info_t *si)
+{ return si->framerate; }
+
+void y4m_si_set_sampleaspect(y4m_stream_info_t *si, y4m_ratio_t sar)
+{ si->sampleaspect = sar; }
+
+y4m_ratio_t y4m_si_get_sampleaspect(y4m_stream_info_t *si)
+{ return si->sampleaspect; }
+
+int y4m_si_get_framelength(y4m_stream_info_t *si)
+{ return si->framelength; }
+
+y4m_xtag_list_t *y4m_si_xtags(y4m_stream_info_t *si)
+{ return &(si->x_tags); }
+
+
+
+void y4m_init_frame_info(y4m_frame_info_t *info)
+{
+  if (info == NULL) return;
+  /* initialize info */
+  y4m_init_xtag_list(&(info->x_tags));
+}
+
+
+void y4m_copy_frame_info(y4m_frame_info_t *dest, y4m_frame_info_t *src)
+{
+  if ((dest == NULL) || (src == NULL)) return;
+  /* copy info */
+  y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags));
+}
+
+
+void y4m_fini_frame_info(y4m_frame_info_t *info)
+{
+  if (info == NULL) return;
+  y4m_fini_xtag_list(&(info->x_tags));
+}
+
+
+
+/*************************************************************************
+ *
+ * Tag parsing 
+ *
+ *************************************************************************/
+
+int y4m_parse_stream_tags(char *s, y4m_stream_info_t *i)
+{
+  char *token, *value;
+  char tag;
+  int err;
+
+  /* parse fields */
+  for (token = strtok(s, Y4M_DELIM); 
+       token != NULL; 
+       token = strtok(NULL, Y4M_DELIM)) {
+    if (token[0] == '\0') continue;   /* skip empty strings */
+    tag = token[0];
+    value = token + 1;
+    switch (tag) {
+    case 'W':  /* width */
+      i->width = atoi(value);
+      if (i->width <= 0) return Y4M_ERR_RANGE;
+      break;
+    case 'H':  /* height */
+      i->height = atoi(value); 
+      if (i->height <= 0) return Y4M_ERR_RANGE;
+      break;
+    case 'F':  /* frame rate (fps) */
+      if ((err = y4m_parse_ratio(&(i->framerate), value)) != Y4M_OK)
+	return err;
+      if (i->framerate.n < 0) return Y4M_ERR_RANGE;
+      break;
+    case 'I':  /* interlacing */
+      switch (value[0]) {
+      case 'p':  i->interlace = Y4M_ILACE_NONE; break;
+      case 't':  i->interlace = Y4M_ILACE_TOP_FIRST; break;
+      case 'b':  i->interlace = Y4M_ILACE_BOTTOM_FIRST; break;
+      case '?':
+      default:
+	i->interlace = Y4M_UNKNOWN; break;
+      }
+      break;
+    case 'A':  /* sample (pixel) aspect ratio */
+      if ((err = y4m_parse_ratio(&(i->sampleaspect), value)) != Y4M_OK)
+	return err;
+      if (i->sampleaspect.n < 0) return Y4M_ERR_RANGE;
+      break;
+    case 'X':  /* 'X' meta-tag */
+      if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
+      break;
+    default:
+      /* possible error on unknown options */
+      if (_y4mparam_allow_unknown_tags) {
+	/* unknown tags ok:  store in xtag list and warn... */
+	if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
+	mp_msg(MSGT_DEMUX, MSGL_WARN, "Unknown stream tag encountered:  '%s'\n", token);
+      } else {
+	/* unknown tags are *not* ok */
+	return Y4M_ERR_BADTAG;
+      }
+      break;
+    }
+  }
+  /* Error checking... width and height must be known since we can't
+   * parse without them
+   */
+  if( i->width == Y4M_UNKNOWN || i->height == Y4M_UNKNOWN )
+	  return Y4M_ERR_HEADER;
+  /* ta da!  done. */
+  return Y4M_OK;
+}
+
+
+
+static int y4m_parse_frame_tags(char *s, y4m_frame_info_t *i)
+{
+  char *token, *value;
+  char tag;
+  int err;
+
+  /* parse fields */
+  for (token = strtok(s, Y4M_DELIM); 
+       token != NULL; 
+       token = strtok(NULL, Y4M_DELIM)) {
+    if (token[0] == '\0') continue;   /* skip empty strings */
+    tag = token[0];
+    value = token + 1;
+    switch (tag) {
+    case 'X':  /* 'X' meta-tag */
+      if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
+      break;
+    default:
+      /* possible error on unknown options */
+      if (_y4mparam_allow_unknown_tags) {
+	/* unknown tags ok:  store in xtag list and warn... */
+	if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
+	mp_msg(MSGT_DEMUX, MSGL_WARN, "Unknown frame tag encountered:  '%s'\n", token);
+      } else {
+	/* unknown tags are *not* ok */
+	return Y4M_ERR_BADTAG;
+      }
+      break;
+    }
+  }
+  /* ta da!  done. */
+  return Y4M_OK;
+}
+
+
+
+
+
+/*************************************************************************
+ *
+ * Read/Write stream header
+ *
+ *************************************************************************/
+
+
+int y4m_read_stream_header(stream_t *s, y4m_stream_info_t *i)
+{
+   char line[Y4M_LINE_MAX];
+   char *p;
+   int n;
+   int err;
+
+   /* read the header line */
+   for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) {
+     if (y4m_read(s, p, 1)) 
+       return Y4M_ERR_SYSTEM;
+     if (*p == '\n') {
+       *p = '\0';           /* Replace linefeed by end of string */
+       break;
+     }
+   }
+   if (n >= Y4M_LINE_MAX)
+      return Y4M_ERR_HEADER;
+   /* look for keyword in header */
+   if (strncmp(line, Y4M_MAGIC, strlen(Y4M_MAGIC)))
+    return Y4M_ERR_MAGIC;
+   if ((err = y4m_parse_stream_tags(line + strlen(Y4M_MAGIC), i)) != Y4M_OK)
+     return err;
+
+   i->framelength = (i->height * i->width) * 3 / 2;
+   return Y4M_OK;
+}
+
+
+#if 0
+int y4m_write_stream_header(int fd, y4m_stream_info_t *i)
+{
+  char s[Y4M_LINE_MAX+1];
+  int n;
+  int err;
+
+  y4m_ratio_reduce(&(i->framerate));
+  y4m_ratio_reduce(&(i->sampleaspect));
+  n = snprintf(s, sizeof(s), "%s W%d H%d F%d:%d I%s A%d:%d",
+	       Y4M_MAGIC,
+	       i->width,
+	       i->height,
+	       i->framerate.n, i->framerate.d,
+	       (i->interlace == Y4M_ILACE_NONE) ? "p" :
+	       (i->interlace == Y4M_ILACE_TOP_FIRST) ? "t" :
+	       (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "b" : "?",
+	       i->sampleaspect.n, i->sampleaspect.d);
+  if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER;
+  if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(i->x_tags))) 
+      != Y4M_OK) 
+    return err;
+  /* non-zero on error */
+  return (y4m_write(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK);
+}
+#endif
+
+
+
+
+/*************************************************************************
+ *
+ * Read/Write frame header
+ *
+ *************************************************************************/
+
+int y4m_read_frame_header(stream_t *s, y4m_frame_info_t *i)
+{
+  char line[Y4M_LINE_MAX];
+  char *p;
+  int n;
+  ssize_t remain;
+  
+  /* This is more clever than read_stream_header...
+     Try to read "FRAME\n" all at once, and don't try to parse
+     if nothing else is there...
+  */
+  remain = y4m_read(s, line, sizeof(Y4M_FRAME_MAGIC));
+  if (remain != 0)
+  {
+	  /* A clean EOF should end exactly at a frame-boundary */
+	  if( remain == sizeof(Y4M_FRAME_MAGIC) )
+		  return Y4M_ERR_EOF;
+	  else
+		  return Y4M_ERR_SYSTEM;
+  }
+  if (strncmp(line, Y4M_FRAME_MAGIC, sizeof(Y4M_FRAME_MAGIC)-1))
+    return Y4M_ERR_MAGIC;
+  if (line[sizeof(Y4M_FRAME_MAGIC)-1] == '\n')
+    return Y4M_OK; /* done -- no tags:  that was the end-of-line. */
+
+  if (line[sizeof(Y4M_FRAME_MAGIC)-1] != Y4M_DELIM[0]) {
+    return Y4M_ERR_MAGIC; /* wasn't a space -- what was it? */
+  }
+
+  /* proceed to get the tags... (overwrite the magic) */
+  for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) {
+    if (y4m_read(s, p, 1))
+      return Y4M_ERR_SYSTEM;
+    if (*p == '\n') {
+      *p = '\0';           /* Replace linefeed by end of string */
+      break;
+    }
+  }
+  if (n >= Y4M_LINE_MAX) return Y4M_ERR_HEADER;
+  /* non-zero on error */
+  return y4m_parse_frame_tags(line, i);
+}
+
+
+#if 0
+int y4m_write_frame_header(int fd, y4m_frame_info_t *i)
+{
+  char s[Y4M_LINE_MAX+1];
+  int n;
+  int err;
+  
+  n = snprintf(s, sizeof(s), "%s", Y4M_FRAME_MAGIC);
+  if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER;
+  if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(i->x_tags))) 
+      != Y4M_OK) 
+    return err;
+  /* non-zero on error */
+  return (y4m_write(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK);
+}
+#endif
+
+
+
+/*************************************************************************
+ *
+ * Read/Write entire frame
+ *
+ *************************************************************************/
+
+int y4m_read_frame(stream_t *s, y4m_stream_info_t *si, 
+		   y4m_frame_info_t *fi, unsigned char *yuv[3])
+{
+  int err;
+  int w = si->width;
+  int h = si->height;
+  
+  /* Read frame header */
+  if ((err = y4m_read_frame_header(s, fi)) != Y4M_OK) return err;
+  /* Read luminance scanlines */
+  if (y4m_read(s, yuv[0], w*h)) return Y4M_ERR_SYSTEM;
+  /* Read chrominance scanlines */
+  if (y4m_read(s, yuv[1], w*h/4)) return Y4M_ERR_SYSTEM;
+  if (y4m_read(s, yuv[2], w*h/4)) return Y4M_ERR_SYSTEM;
+
+  return Y4M_OK;
+}
+
+
+
+#if 0
+int y4m_write_frame(int fd, y4m_stream_info_t *si, 
+		    y4m_frame_info_t *fi, unsigned char *yuv[3])
+{
+  int err;
+  int w = si->width;
+  int h = si->height;
+
+  /* Write frame header */
+  if ((err = y4m_write_frame_header(fd, fi)) != Y4M_OK) return err;
+  /* Write luminance,chrominance scanlines */
+  if (y4m_write(fd, yuv[0], w*h) ||
+      y4m_write(fd, yuv[1], w*h/4) ||
+      y4m_write(fd, yuv[2], w*h/4))
+    return Y4M_ERR_SYSTEM;
+  return Y4M_OK;
+}
+#endif
+
+
+/*************************************************************************
+ *
+ * Read/Write entire frame, (de)interleaved (to)from two separate fields
+ *
+ *************************************************************************/
+
+#if 0
+int y4m_read_fields(int fd, y4m_stream_info_t *si, y4m_frame_info_t *fi,
+                    unsigned char *upper_field[3], 
+                    unsigned char *lower_field[3])
+{
+  int i, y, err;
+  int width = si->width;
+  int height = si->height;
+  
+  /* Read frame header */
+  if ((err = y4m_read_frame_header(fd, fi)) != Y4M_OK) return err;
+  /* Read Y', Cb, and Cr planes */
+  for (i = 0; i < 3; i++) {
+    unsigned char *srctop = upper_field[i];
+    unsigned char *srcbot = lower_field[i];
+    /* alternately write one line from each */
+    for (y = 0; y < height; y += 2) {
+      if (y4m_read(fd, srctop, width)) return Y4M_ERR_SYSTEM;
+      srctop += width;
+      if (y4m_read(fd, srcbot, width)) return Y4M_ERR_SYSTEM;
+      srcbot += width;
+    }
+    /* for chroma, width/height are half as big */
+    if (i == 0) {
+      width /= 2;
+      height /= 2;
+    }
+  }
+  return Y4M_OK;
+}
+
+
+
+int y4m_write_fields(int fd, y4m_stream_info_t *si, y4m_frame_info_t *fi,
+                     unsigned char *upper_field[3], 
+                     unsigned char *lower_field[3])
+{
+  int i, y, err;
+  int width = si->width;
+  int height = si->height;
+
+  /* Write frame header */
+  if ((err = y4m_write_frame_header(fd, fi)) != Y4M_OK) return err;
+  /* Write Y', Cb, and Cr planes */
+  for (i = 0; i < 3; i++) {
+    unsigned char *srctop = upper_field[i];
+    unsigned char *srcbot = lower_field[i];
+    /* alternately write one line from each */
+    for (y = 0; y < height; y += 2) {
+      if (y4m_write(fd, srctop, width)) return Y4M_ERR_SYSTEM;
+      srctop += width;
+      if (y4m_write(fd, srcbot, width)) return Y4M_ERR_SYSTEM;
+      srcbot += width;
+    }
+    /* for chroma, width/height are half as big */
+    if (i == 0) {
+      width /= 2;
+      height /= 2;
+    }
+  }
+  return Y4M_OK;
+}
+#endif
+
+
+/*************************************************************************
+ *
+ * Handy logging of stream info
+ *
+ *************************************************************************/
+
+void y4m_log_stream_info(const char *prefix, y4m_stream_info_t *i)
+{
+  char s[256];
+
+  snprintf(s, sizeof(s), "  frame size:  ");
+  if (i->width == Y4M_UNKNOWN)
+    snprintf(s+strlen(s), sizeof(s)-strlen(s), "(?)x");
+  else
+    snprintf(s+strlen(s), sizeof(s)-strlen(s), "%dx", i->width);
+  if (i->height == Y4M_UNKNOWN)
+    snprintf(s+strlen(s), sizeof(s)-strlen(s), "(?) pixels ");
+  else
+    snprintf(s+strlen(s), sizeof(s)-strlen(s), "%d pixels ", i->height);
+  if (i->framelength == Y4M_UNKNOWN)
+    snprintf(s+strlen(s), sizeof(s)-strlen(s), "(? bytes)");
+  else
+    snprintf(s+strlen(s), sizeof(s)-strlen(s), "(%d bytes)", i->framelength);
+  mp_msg(MSGT_DEMUX, MSGL_V, "%s%s\n", prefix, s);
+  if ((i->framerate.n == 0) && (i->framerate.d == 0))
+    mp_msg(MSGT_DEMUX, MSGL_V, "%s  frame rate:  ??? fps\n", prefix);
+  else
+    mp_msg(MSGT_DEMUX, MSGL_V, "%s  frame rate:  %d/%d fps (~%f)\n", prefix,
+	      i->framerate.n, i->framerate.d, 
+	      (double) i->framerate.n / (double) i->framerate.d);
+  mp_msg(MSGT_DEMUX, MSGL_V, "%s   interlace:  %s\n", prefix,
+	  (i->interlace == Y4M_ILACE_NONE) ? "none/progressive" :
+	  (i->interlace == Y4M_ILACE_TOP_FIRST) ? "top-field-first" :
+	  (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "bottom-field-first" :
+	  "anyone's guess");
+  if ((i->sampleaspect.n == 0) && (i->sampleaspect.d == 0))
+    mp_msg(MSGT_DEMUX, MSGL_V, "%ssample aspect ratio:  ?:?\n", prefix);
+  else
+    mp_msg(MSGT_DEMUX, MSGL_V, "%ssample aspect ratio:  %d:%d\n", prefix,
+	      i->sampleaspect.n, i->sampleaspect.d);
+}
+
+
+/*************************************************************************
+ *
+ * Convert error code to string
+ *
+ *************************************************************************/
+
+const char *y4m_strerr(int err)
+{
+  switch (err) {
+  case Y4M_OK:          return "no error";
+  case Y4M_ERR_RANGE:   return "parameter out of range";
+  case Y4M_ERR_SYSTEM:  return "stream ended unexpectedly (failed read/write)";
+  case Y4M_ERR_HEADER:  return "bad stream or frame header";
+  case Y4M_ERR_BADTAG:  return "unknown header tag";
+  case Y4M_ERR_MAGIC:   return "bad header magic";
+  case Y4M_ERR_XXTAGS:  return "too many xtags";
+  case Y4M_ERR_EOF:     return "end-of-file";
+  default: 
+    return "unknown error code";
+  }
+}
+
+
diff -Naur main/libmpdemux/yuv4mpeg.h main.dev/libmpdemux/yuv4mpeg.h
--- main/libmpdemux/yuv4mpeg.h	Thu Jan  1 01:00:00 1970
+++ main.dev/libmpdemux/yuv4mpeg.h	Wed Dec 26 15:37:15 2001
@@ -0,0 +1,453 @@
+/*
+ *  yuv4mpeg.h:  Functions for reading and writing "new" YUV4MPEG2 streams.
+ *
+ *               Stream format is described at the end of this file.
+ *
+ *
+ *  Copyright (C) 2001 Matthew J. Marjanovic <maddog at mir.com>
+ *
+ *  This file is ripped from the lavtools package (mjpeg.sourceforge.net)
+ *  Ported to mplayer by Rik Snel <snel at phys.uu.nl>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __YUV4MPEG_H__
+#define __YUV4MPEG_H__
+
+#include <stdlib.h>
+#include "mp_msg.h"
+#include "stream.h"
+
+
+/************************************************************************
+ *  error codes returned by y4m_* functions
+ ************************************************************************/
+#define Y4M_OK          0
+#define Y4M_ERR_RANGE   1
+#define Y4M_ERR_SYSTEM  2
+#define Y4M_ERR_HEADER  3
+#define Y4M_ERR_BADTAG  4
+#define Y4M_ERR_MAGIC   5
+#define Y4M_ERR_EOF     6
+#define Y4M_ERR_XXTAGS  7
+
+
+/* generic 'unknown' value for integer parameters (e.g. interlace, height) */
+#define Y4M_UNKNOWN -1
+
+
+
+/************************************************************************
+ *  'ratio' datatype, for rational numbers
+ *                                     (see 'ratio' functions down below)
+ ************************************************************************/
+typedef struct _y4m_ratio {
+  int n;  /* numerator   */
+  int d;  /* denominator */
+} y4m_ratio_t;
+
+
+/************************************************************************
+ *  useful standard framerates (as ratios)
+ ************************************************************************/
+extern const y4m_ratio_t y4m_fps_UNKNOWN;
+extern const y4m_ratio_t y4m_fps_NTSC_FILM;  /* 24000/1001 film (in NTSC)  */
+extern const y4m_ratio_t y4m_fps_FILM;       /* 24fps film                 */
+extern const y4m_ratio_t y4m_fps_PAL;        /* 25fps PAL                  */
+extern const y4m_ratio_t y4m_fps_NTSC;       /* 30000/1001 NTSC            */
+extern const y4m_ratio_t y4m_fps_30;         /* 30fps                      */
+extern const y4m_ratio_t y4m_fps_PAL_FIELD;  /* 50fps PAL field rate       */
+extern const y4m_ratio_t y4m_fps_NTSC_FIELD; /* 60000/1001 NTSC field rate */
+extern const y4m_ratio_t y4m_fps_60;         /* 60fps                      */
+
+/************************************************************************
+ *  useful standard sample (pixel) aspect ratios
+ ************************************************************************/
+extern const y4m_ratio_t y4m_sar_UNKNOWN; 
+extern const y4m_ratio_t y4m_sar_SQUARE;        /* square pixels */
+extern const y4m_ratio_t y4m_sar_NTSC_CCIR601;  /* 525-line (NTSC) Rec.601 */
+extern const y4m_ratio_t y4m_sar_NTSC_16_9;     /* 16:9 NTSC/Rec.601       */
+extern const y4m_ratio_t y4m_sar_NTSC_SVCD_4_3; /* NTSC SVCD 4:3           */
+extern const y4m_ratio_t y4m_sar_NTSC_SVCD_16_9;/* NTSC SVCD 16:9          */
+extern const y4m_ratio_t y4m_sar_PAL_CCIR601;   /* 625-line (PAL) Rec.601  */
+extern const y4m_ratio_t y4m_sar_PAL_16_9;      /* 16:9 PAL/Rec.601        */
+extern const y4m_ratio_t y4m_sar_PAL_SVCD_4_3;  /* PAL SVCD 4:3            */
+extern const y4m_ratio_t y4m_sar_PAL_SVCD_16_9; /* PAL SVCD 16:9           */
+
+
+/************************************************************************
+ *  'xtag_list' --- list of unparsed and/or meta/X header tags
+ *
+ *     Do not touch this structure directly!
+ *
+ *     Use the y4m_xtag_*() functions (see below).
+ *     You must initialize/finalize this structure before/after use.
+ ************************************************************************/
+#define Y4M_MAX_XTAGS 32        /* maximum number of xtags in list       */
+#define Y4M_MAX_XTAG_SIZE 32    /* max length of an xtag (including 'X') */
+typedef struct _y4m_xtag_list {
+  int count;
+  char *tags[Y4M_MAX_XTAGS];
+} y4m_xtag_list_t;
+
+
+
+/************************************************************************
+ *  'stream_info' --- stream header information
+ *
+ *     Do not touch this structure directly!
+ *
+ *     Use the y4m_si_*() functions (see below).
+ *     You must initialize/finalize this structure before/after use.
+ ************************************************************************/
+typedef struct _y4m_stream_info {
+  /* values from header */
+  int width;
+  int height;
+  int interlace;            /* see Y4M_ILACE_* definitions below   */
+  y4m_ratio_t framerate;    /* frames-per-second;   0:0 == unknown */
+  y4m_ratio_t sampleaspect; /* pixel width/height;  0:0 == unknown */
+  /* computed/derivative values */
+  int framelength;    /* bytes of data per frame (not including header) */
+  /* mystical X tags */
+  y4m_xtag_list_t x_tags;
+} y4m_stream_info_t;
+
+/* possible options for the interlace parameter */
+#define Y4M_ILACE_NONE          0   /* non-interlaced, progressive frame */
+#define Y4M_ILACE_TOP_FIRST     1   /* interlaced, top-field first       */
+#define Y4M_ILACE_BOTTOM_FIRST  2   /* interlaced, bottom-field first    */
+
+
+/************************************************************************
+ *  'frame_info' --- frame header information
+ *
+ *     Do not touch this structure directly!
+ *
+ *     Use the y4m_fi_*() functions (see below).
+ *     You must initialize/finalize this structure before/after use.
+ ************************************************************************/
+typedef struct _y4m_frame_info {
+  /* mystical X tags */
+  y4m_xtag_list_t x_tags;
+} y4m_frame_info_t;
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#endif
+
+
+/************************************************************************
+ *  'ratio' functions
+ ************************************************************************/
+
+/* 'normalize' a ratio (remove common factors) */
+void y4m_ratio_reduce(y4m_ratio_t *r);
+
+/* parse "nnn:ddd" into a ratio (returns Y4M_OK or Y4M_ERR_RANGE) */
+int y4m_parse_ratio(y4m_ratio_t *r, const char *s);
+
+/* quick test of two ratios for equality (i.e. identical components) */
+#define Y4M_RATIO_EQL(a,b) ( ((a).n == (b).n) && ((a).d == (b).d) )
+
+/* quick conversion of a ratio to a double (no divide-by-zero check!) */
+#define Y4M_RATIO_DBL(r) ((double)(r).n / (double)(r).d)
+
+
+
+/************************************************************************
+ *  'xtag' functions
+ *
+ * o Before using an xtag_list (but after the structure/memory has been
+ *    allocated), you must initialize it via y4m_init_xtag_list().
+ * o After using an xtag_list (but before the structure is released),
+ *    call y4m_fini_xtag_list() to free internal memory.
+ *
+ ************************************************************************/
+
+/* initialize an xtag_list structure */
+void y4m_init_xtag_list(y4m_xtag_list_t *xtags);
+
+/* finalize an xtag_list structure */
+void y4m_fini_xtag_list(y4m_xtag_list_t *xtags);
+
+/* make one xtag_list into a copy of another */
+void y4m_copy_xtag_list(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src);
+
+/* return number of tags in an xtag_list */
+int y4m_xtag_count(const y4m_xtag_list_t *xtags);
+
+/* access n'th tag in an xtag_list */
+const char *y4m_xtag_get(const y4m_xtag_list_t *xtags, int n);
+
+/* append a new tag to an xtag_list
+    returns:          Y4M_OK - success
+              Y4M_ERR_XXTAGS - list is already full */
+int y4m_xtag_add(y4m_xtag_list_t *xtags, const char *tag);
+
+/* remove a tag from an xtag_list 
+    returns:         Y4M_OK - success
+              Y4M_ERR_RANGE - n is out of range */
+int y4m_xtag_remove(y4m_xtag_list_t *xtags, int n);
+
+/* remove all tags from an xtag_list 
+    returns:   Y4M_OK - success       */
+int y4m_xtag_clearlist(y4m_xtag_list_t *xtags);
+
+/* append copies of tags from src list to dest list
+    returns:          Y4M_OK - success
+              Y4M_ERR_XXTAGS - operation would overfill dest list */
+int y4m_xtag_addlist(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src);
+
+
+
+/************************************************************************
+ *  '*_info' functions
+ *
+ * o Before using a *_info structure (but after the structure/memory has
+ *    been allocated), you must initialize it via y4m_init_*_info().
+ * o After using a *_info structure (but before the structure is released),
+ *    call y4m_fini_*_info() to free internal memory.
+ * o Use the 'set' and 'get' accessors to modify or access the fields in
+ *    the structures; don't touch the structure directly.  (Ok, so there
+ *    is no really convenient C syntax to prevent you from doing this,
+ *    but we are all responsible programmers here, so just don't do it!)
+ *
+ ************************************************************************/
+
+/* initialize a stream_info structure */
+void y4m_init_stream_info(y4m_stream_info_t *i);
+
+/* finalize a stream_info structure */
+void y4m_fini_stream_info(y4m_stream_info_t *i);
+
+/* make one stream_info into a copy of another */
+void y4m_copy_stream_info(y4m_stream_info_t *dest, y4m_stream_info_t *src);
+
+/* access or set stream_info fields */
+void y4m_si_set_width(y4m_stream_info_t *si, int width);
+int y4m_si_get_width(y4m_stream_info_t *si);
+void y4m_si_set_height(y4m_stream_info_t *si, int height);
+int y4m_si_get_height(y4m_stream_info_t *si);
+void y4m_si_set_interlace(y4m_stream_info_t *si, int interlace);
+int y4m_si_get_interlace(y4m_stream_info_t *si);
+void y4m_si_set_framerate(y4m_stream_info_t *si, y4m_ratio_t framerate);
+y4m_ratio_t y4m_si_get_framerate(y4m_stream_info_t *si);
+void y4m_si_set_sampleaspect(y4m_stream_info_t *si, y4m_ratio_t sar);
+y4m_ratio_t y4m_si_get_sampleaspect(y4m_stream_info_t *si);
+int y4m_si_get_framelength(y4m_stream_info_t *si);
+
+/* access stream_info xtag_list */
+y4m_xtag_list_t *y4m_si_xtags(y4m_stream_info_t *si);
+
+
+/* initialize a frame_info structure */
+void y4m_init_frame_info(y4m_frame_info_t *i);
+
+/* finalize a frame_info structure */
+void y4m_fini_frame_info(y4m_frame_info_t *i);
+
+/* make one frame_info into a copy of another */
+void y4m_copy_frame_info(y4m_frame_info_t *dest, y4m_frame_info_t *src);
+
+/* access frame_info xtag_list */
+y4m_xtag_list_t *y4m_fi_xtags(y4m_frame_info_t *fi);
+
+
+
+/************************************************************************
+ *  blocking read and write functions
+ *
+ *  o guaranteed to transfer entire payload (or fail)
+ *  o return values:
+ *                         0 (zero)   complete success
+ *          -(# of remaining bytes)   error (and errno left set)
+ *          +(# of remaining bytes)   EOF (for y4m_read only)
+ *
+ ************************************************************************/
+
+/* read len bytes from fd into buf */
+ssize_t y4m_read(stream_t *s, char *buf, size_t len);
+
+#if 0
+/* write len bytes from fd into buf */
+ssize_t y4m_write(int fd, char *buf, size_t len);
+#endif
+
+
+/************************************************************************
+ *  stream header processing functions
+ *  
+ *  o return values:
+ *                   Y4M_OK - success
+ *                Y4M_ERR_* - error (see y4m_strerr() for descriptions)
+ *
+ ************************************************************************/
+
+/* parse a string of stream header tags */
+int y4m_parse_stream_tags(char *s, y4m_stream_info_t *i);
+
+/* read a stream header from file descriptor fd */
+int y4m_read_stream_header(stream_t *s, y4m_stream_info_t *i);
+
+#if 0
+/* write a stream header to file descriptor fd */
+int y4m_write_stream_header(int fd,  y4m_stream_info_t *i);
+#endif
+
+
+/************************************************************************
+ *  frame processing functions
+ *  
+ *  o return values:
+ *                   Y4M_OK - success
+ *                Y4M_ERR_* - error (see y4m_strerr() for descriptions)
+ *
+ ************************************************************************/
+
+/* read a frame header from file descriptor fd */
+int y4m_read_frame_header(stream_t *s, y4m_frame_info_t *i);
+
+#if 0
+/* write a frame header to file descriptor fd */
+int y4m_write_frame_header(int fd, y4m_frame_info_t *i);
+#endif
+
+/* read a complete frame (header + data)
+   o yuv[3] points to three buffers, one each for Y, U, V planes */
+int y4m_read_frame(stream_t *s, y4m_stream_info_t *si, 
+		   y4m_frame_info_t *fi, unsigned char *yuv[3]);
+
+#if 0
+/* write a complete frame (header + data)
+   o yuv[3] points to three buffers, one each for Y, U, V planes */
+int y4m_write_frame(int fd, y4m_stream_info_t *si, 
+		    y4m_frame_info_t *fi, unsigned char *yuv[3]);
+#endif
+
+#if 0
+/* read a complete frame (header + data), but de-interleave fields
+    into two separate buffers
+   o upper_field[3] same as yuv[3] above, but for upper field
+   o lower_field[3] same as yuv[3] above, but for lower field
+*/
+int y4m_read_fields(int fd, y4m_stream_info_t *si, y4m_frame_info_t *fi,
+		    unsigned char *upper_field[3], 
+		    unsigned char *lower_field[3]);
+
+/* write a complete frame (header + data), but interleave fields
+    from two separate buffers
+   o upper_field[3] same as yuv[3] above, but for upper field
+   o lower_field[3] same as yuv[3] above, but for lower field
+*/
+int y4m_write_fields(int fd, y4m_stream_info_t *si, y4m_frame_info_t *fi,
+		     unsigned char *upper_field[3], 
+		     unsigned char *lower_field[3]);
+
+#endif
+
+/************************************************************************
+ *  miscellaneous functions
+ ************************************************************************/
+
+/* convenient dump of stream header info via mjpeg_log facility
+ *  - each logged/printed line is prefixed by 'prefix'
+ */
+void y4m_log_stream_info(const char *prefix, y4m_stream_info_t *i);
+
+/* convert a Y4M_ERR_* error code into mildly explanatory string */
+const char *y4m_strerr(int err);
+
+/* set 'allow_unknown_tag' flag for library...
+    o yn = 0 :  unknown header tags will produce a parsing error
+    o yn = 1 :  unknown header tags/values will produce a warning, but
+                 are otherwise passed along via the xtags list
+    o yn = -1:  don't change, just return current setting
+
+   return value:  previous setting of flag
+*/
+int y4m_allow_unknown_tags(int yn);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/************************************************************************
+ ************************************************************************
+
+  Description of the (new!, forever?) YUV4MPEG2 stream format:
+
+  STREAM consists of
+    o one '\n' terminated STREAM-HEADER
+    o unlimited number of FRAMEs
+
+  FRAME consists of
+    o one '\n' terminated FRAME-HEADER
+    o "length" octets of planar YCrCb 4:2:0 image data
+        (if frame is interlaced, then the two fields are interleaved)
+
+
+  STREAM-HEADER consists of
+     o string "YUV4MPEG2 "  (note the space after the '2')
+     o unlimited number of ' ' separated TAGGED-FIELDs
+     o '\n' line terminator
+
+  FRAME-HEADER consists of
+     o string "FRAME "  (note the space after the 'E')
+     o unlimited number of ' ' separated TAGGED-FIELDs
+     o '\n' line terminator
+
+
+  TAGGED-FIELD consists of
+     o single ascii character tag
+     o VALUE (which does not contain whitespace)
+
+  VALUE consists of
+     o integer (base 10 ascii representation)
+  or o RATIO
+  or o single ascii character
+  or o generic ascii string
+
+  RATIO consists of
+     o numerator (integer)
+     o ':' (a colon)
+     o denominator (integer)
+
+
+  The currently supported tags for the STREAM-HEADER:
+     W - [integer] frame width, pixels, should be > 0
+     H - [integer] frame height, pixels, should be > 0
+     I - [char] interlacing:  p - progressive (none)
+                            t - top-field-first
+                            b - bottom-field-first
+		            ? - unknown
+     F - [ratio] frame-rate, 0:0 == unknown
+     A - [ratio] sample (pixel) aspect ratio, 0:0 == unknown
+     X - [character string] 'metadata' (unparsed, but passed around)
+
+  The currently supported tags for the FRAME-HEADER:
+     X - character string 'metadata' (unparsed, but passed around)
+
+ ************************************************************************
+ ************************************************************************/
+
+#endif /* __YUV4MPEG_H__ */
+
+
diff -Naur main/libmpdemux/yuv4mpeg_intern.h main.dev/libmpdemux/yuv4mpeg_intern.h
--- main/libmpdemux/yuv4mpeg_intern.h	Thu Jan  1 01:00:00 1970
+++ main.dev/libmpdemux/yuv4mpeg_intern.h	Wed Dec 26 15:37:15 2001
@@ -0,0 +1,78 @@
+/*
+ *  yuv4mpeg_intern.h:  Internal constants for "new" YUV4MPEG streams
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens at philips.com>
+ *
+ *  This file is part of the lavtools package (mjpeg.sourceforge.net).  
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __YUV4MPEG_INTERN_H__
+#define __YUV4MPEG_INTERN_H__
+
+
+#define Y4M_MAGIC "YUV4MPEG2"
+#define Y4M_FRAME_MAGIC "FRAME"
+
+#define Y4M_DELIM " "  /* single-character(space) separating tagged fields */
+
+#define Y4M_LINE_MAX 256   /* max number of characters in a header line
+                               (including the '\n', but not the '\0') */
+
+
+/* standard framerate ratios */
+#define Y4M_FPS_UNKNOWN    { 0, 0 }
+#define Y4M_FPS_NTSC_FILM  { 24000, 1001 }
+#define Y4M_FPS_FILM       { 24, 1 }
+#define Y4M_FPS_PAL        { 25, 1 }
+#define Y4M_FPS_NTSC       { 30000, 1001 }
+#define Y4M_FPS_30         { 30, 1 }
+#define Y4M_FPS_PAL_FIELD  { 50, 1 }
+#define Y4M_FPS_NTSC_FIELD { 60000, 1001 }
+#define Y4M_FPS_60         { 60, 1 }
+
+/* standard sample/pixel aspect ratios */
+#define Y4M_SAR_UNKNOWN        {   0, 0  }
+#define Y4M_SAR_SQUARE         {   1, 1  }
+#define Y4M_SAR_NTSC_CCIR601   {  10, 11 }
+#define Y4M_SAR_NTSC_16_9      {  40, 33 }
+#define Y4M_SAR_NTSC_SVCD_4_3  {  15, 11 }
+#define Y4M_SAR_NTSC_SVCD_16_9 {  20, 11 }
+#define Y4M_SAR_PAL_CCIR601    {  59, 54 }
+#define Y4M_SAR_PAL_16_9       { 118, 81 }
+#define Y4M_SAR_PAL_SVCD_4_3   {  59, 36 }
+#define Y4M_SAR_PAL_SVCD_16_9  {  59, 27 }
+
+#define Y4M_SAR_MPEG1_1 Y4M_SAR_SQUARE
+#define Y4M_SAR_MPEG1_2  { 10000, 6735 }
+#define Y4M_SAR_MPEG1_3  { 10000, 7031 } /* Anamorphic 16:9 PAL */
+#define Y4M_SAR_MPEG1_4  { 10000, 7615 }
+#define Y4M_SAR_MPEG1_5  { 10000, 8055 }
+#define Y4M_SAR_MPEG1_6  { 10000, 8437 } /* Anamorphic 16:9 NTSC */
+#define Y4M_SAR_MPEG1_7  { 10000, 8935 } 
+#define Y4M_SAR_MPEG1_8  { 10000, 9375 } /* PAL/SECAM 4:3 */
+#define Y4M_SAR_MPEG1_9  { 10000, 9815 }
+#define Y4M_SAR_MPEG1_10 { 10000, 10255 }
+#define Y4M_SAR_MPEG1_11 { 10000, 10695 }
+#define Y4M_SAR_MPEG1_12 { 10000, 11250 } /* NTSC 4:3 */
+#define Y4M_SAR_MPEG1_13 { 10000, 11575 }
+#define Y4M_SAR_MPEG1_14 { 10000, 12015 }
+
+#define Y4M_DAR_MPEG2_1 { 1, 1}
+#define Y4M_DAR_MPEG2_2 { 4, 3 }
+#define Y4M_DAR_MPEG2_3 { 16, 9 }
+#define Y4M_DAR_MPEG2_4 { 221, 100 }
+
+#endif __YUV4MPEG_INTERN_H__
diff -Naur main/libmpdemux/yuv4mpeg_ratio.c main.dev/libmpdemux/yuv4mpeg_ratio.c
--- main/libmpdemux/yuv4mpeg_ratio.c	Thu Jan  1 01:00:00 1970
+++ main.dev/libmpdemux/yuv4mpeg_ratio.c	Wed Dec 26 15:37:15 2001
@@ -0,0 +1,113 @@
+/*
+ *  yuv4mpeg_ratio.c:  Functions for dealing with y4m_ratio_t datatype.
+ *
+ *  Copyright (C) 2001 Matthew J. Marjanovic <maddog at mir.com>
+ *
+ *  This file is part of the lavtools packaged (mjpeg.sourceforge.net)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include "yuv4mpeg.h"
+#include "yuv4mpeg_intern.h"
+
+
+/* useful list of standard framerates */
+const y4m_ratio_t y4m_fps_UNKNOWN    = Y4M_FPS_UNKNOWN;
+const y4m_ratio_t y4m_fps_NTSC_FILM  = Y4M_FPS_NTSC_FILM;
+const y4m_ratio_t y4m_fps_FILM       = Y4M_FPS_FILM;
+const y4m_ratio_t y4m_fps_PAL        = Y4M_FPS_PAL;
+const y4m_ratio_t y4m_fps_NTSC       = Y4M_FPS_NTSC;
+const y4m_ratio_t y4m_fps_30         = Y4M_FPS_30;
+const y4m_ratio_t y4m_fps_PAL_FIELD  = Y4M_FPS_PAL_FIELD;
+const y4m_ratio_t y4m_fps_NTSC_FIELD = Y4M_FPS_NTSC_FIELD;
+const y4m_ratio_t y4m_fps_60         = Y4M_FPS_60;
+
+/* useful list of standard pixel aspect ratios */
+const y4m_ratio_t y4m_sar_UNKNOWN        = Y4M_SAR_UNKNOWN;
+const y4m_ratio_t y4m_sar_SQUARE         = Y4M_SAR_SQUARE;
+const y4m_ratio_t y4m_sar_NTSC_CCIR601   = Y4M_SAR_NTSC_CCIR601;
+const y4m_ratio_t y4m_sar_NTSC_16_9      = Y4M_SAR_NTSC_16_9;
+const y4m_ratio_t y4m_sar_NTSC_SVCD_4_3  = Y4M_SAR_NTSC_SVCD_4_3;
+const y4m_ratio_t y4m_sar_NTSC_SVCD_16_9 = Y4M_SAR_NTSC_SVCD_16_9;
+const y4m_ratio_t y4m_sar_PAL_CCIR601    = Y4M_SAR_PAL_CCIR601;
+const y4m_ratio_t y4m_sar_PAL_16_9       = Y4M_SAR_PAL_16_9;
+const y4m_ratio_t y4m_sar_PAL_SVCD_4_3   = Y4M_SAR_PAL_SVCD_4_3;
+const y4m_ratio_t y4m_sar_PAL_SVCD_16_9  = Y4M_SAR_PAL_SVCD_16_9;
+
+
+/*
+ *  Euler's algorithm for greatest common divisor
+ */
+
+static int gcd(int a, int b)
+{
+  a = (a >= 0) ? a : -a;
+  b = (b >= 0) ? b : -b;
+
+  while (b > 0) {
+    int x = b;
+    b = a % b;
+    a = x;
+  }
+  return a;
+}
+    
+
+/*************************************************************************
+ *
+ * Remove common factors from a ratio
+ *
+ *************************************************************************/
+
+
+void y4m_ratio_reduce(y4m_ratio_t *r)
+{
+  int d;
+  if ((r->n == 0) && (r->d == 0)) return;  /* "unknown" */
+  d = gcd(r->n, r->d);
+  r->n /= d;
+  r->d /= d;
+}
+
+
+
+/*************************************************************************
+ *
+ * Parse "nnn:ddd" into a ratio
+ *
+ * returns:         Y4M_OK  - success
+ *           Y4M_ERR_RANGE  - range error 
+ *
+ *************************************************************************/
+
+int y4m_parse_ratio(y4m_ratio_t *r, const char *s)
+{
+  char *t = strchr(s, ':');
+
+  if (t == NULL) return Y4M_ERR_RANGE;
+  r->n = atoi(s);
+  r->d = atoi(t+1);
+  if (r->d < 0) return Y4M_ERR_RANGE;
+  /* 0:0 == unknown, so that is ok, otherwise zero denominator is bad */
+  if ((r->d == 0) && (r->n != 0)) return Y4M_ERR_RANGE;
+  y4m_ratio_reduce(r);
+  return Y4M_OK;
+}
+


More information about the MPlayer-dev-eng mailing list