changeset 16164:ec76d55a25f1

Support more MythTV nuv files, based on Gentoo portage patch
author reimar
date Mon, 01 Aug 2005 18:52:20 +0000
parents ed52261ea934
children a9f7eff9e437
files libmpdemux/demux_nuv.c libmpdemux/nuppelvideo.h
diffstat 2 files changed, 170 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/libmpdemux/demux_nuv.c	Mon Aug 01 18:52:20 2005 +0000
+++ b/libmpdemux/demux_nuv.c	Mon Aug 01 18:52:20 2005 +0000
@@ -43,6 +43,22 @@
 	nuv_position_t *current_position;
 } nuv_priv_t;
 
+/**
+ * \brief find best matching bitrate (in kbps) out of a table
+ * \param bitrate bitrate to find best match for
+ * \return best match from table
+ */
+static int nearestBitrate(int bitrate) {
+  const int rates[17] = {8000, 16000, 24000, 32000, 40000, 48000, 56000,
+                         64000, 80000, 96000, 112000, 128000, 160000,
+                         192000, 224000, 256000, 320000};
+  int i;
+  for (i = 0; i < 16; i++) {
+    if ((rates[i] + rates[i + 1]) / 2 > bitrate)
+      break;
+  }
+  return rates[i];
+}
 
 /**
  * Seek to a position relative to the current position, indicated in time.
@@ -154,12 +170,19 @@
 	    rtjpeg_frameheader.packetlength);
 #endif
 
-	/* Skip Seekpoint, Text and Sync for now */
+	/* Skip Seekpoint, Extended header and Sync for now */
 	if ((rtjpeg_frameheader.frametype == 'R') ||
-	    (rtjpeg_frameheader.frametype == 'T') ||
+	    (rtjpeg_frameheader.frametype == 'X') ||
 	    (rtjpeg_frameheader.frametype == 'S'))
 	    return 1;
 	
+	/* Skip seektable and text (these have a payload) */
+	if ((rtjpeg_frameheader.frametype == 'Q') ||
+	    (rtjpeg_frameheader.frametype == 'T')) {
+	  stream_skip(demuxer->stream, rtjpeg_frameheader.packetlength);
+	  return 1;
+	}
+
 	if (((rtjpeg_frameheader.frametype == 'D') &&
 	    (rtjpeg_frameheader.comptype == 'R')) ||
 	    (rtjpeg_frameheader.frametype == 'V'))
@@ -181,9 +204,7 @@
 
 
 	} else
-	/* copy PCM only */
-	if (demuxer->audio && (rtjpeg_frameheader.frametype == 'A') &&
-	    (rtjpeg_frameheader.comptype == '0'))
+	if (demuxer->audio && (rtjpeg_frameheader.frametype == 'A'))
 	{
 	    priv->current_audio_frame++;
 	    if (want_audio) {
@@ -194,15 +215,99 @@
 			       orig_pos + 12, 0 );
 	    } else {
 	      /* skip audio block */
-	      stream_seek ( demuxer->stream,
-			    stream_tell ( demuxer->stream )
-			    + rtjpeg_frameheader.packetlength );
+	      stream_skip ( demuxer->stream,
+			    rtjpeg_frameheader.packetlength );
 	    }
 	}
 
 	return 1;
 }
 
+/* Scan for the extended data in MythTV nuv streams */
+static int demux_xscan_nuv(demuxer_t* demuxer, int width, int height) {
+  int i;
+  int res = 0;
+  off_t orig_pos = stream_tell(demuxer->stream);
+  struct rtframeheader rtjpeg_frameheader;
+  struct extendeddata ext;
+  sh_video_t* sh_video = demuxer->video->sh;
+  sh_audio_t* sh_audio = demuxer->audio->sh;
+
+  for (i = 0; i < 2; ++i) {
+    if (stream_read(demuxer->stream, (char*)&rtjpeg_frameheader,
+              sizeof(rtjpeg_frameheader)) < sizeof(rtjpeg_frameheader))
+      goto out;
+
+    if (rtjpeg_frameheader.frametype != 'X')
+      stream_skip(demuxer->stream, rtjpeg_frameheader.packetlength);
+  }
+  
+  if ( rtjpeg_frameheader.frametype != 'X' )
+    goto out;
+
+  if (rtjpeg_frameheader.packetlength != sizeof(ext)) {
+    mp_msg(MSGT_DEMUXER, MSGL_WARN, 
+           "NUV extended frame does not have expected length, ignoring\n");
+    goto out;
+  }
+  le2me_extendeddata(&ext);
+
+  if (stream_read(demuxer->stream, (char*)&ext, sizeof(ext)) < sizeof(ext))
+    goto out;
+
+  if (ext.version != 1) {
+    mp_msg(MSGT_DEMUXER, MSGL_WARN,
+           "NUV extended frame has unknown version number (%d), ignoring\n",
+           ext.version);
+    goto out;
+  }
+
+  mp_msg(MSGT_DEMUXER, MSGL_V, "Detected MythTV stream\n");
+
+  /* Video parameters */
+  mp_msg(MSGT_DEMUXER, MSGL_V, "FOURCC: %c%c%c%c\n",
+         (ext.video_fourcc >> 24) & 0xff,
+         (ext.video_fourcc >> 16) & 0xff,
+         (ext.video_fourcc >> 8) & 0xff,
+         (ext.video_fourcc) & 0xff);
+  sh_video->format = ext.video_fourcc;
+  sh_video->i_bps = ext.lavc_bitrate;
+
+  /* Audio parameters */
+  if (ext.audio_fourcc == mmioFOURCC('L', 'A', 'M', 'E')) {
+    sh_audio->format = 0x55;
+  } else if (ext.audio_fourcc == mmioFOURCC('R', 'A', 'W', 'A')) {
+    sh_audio->format = 0x1;
+  } else {
+    mp_msg(MSGT_DEMUXER, MSGL_WARN,
+           "Unknown audio format 0x%x\n", ext.audio_fourcc);
+  }
+
+  sh_audio->channels = ext.audio_channels;
+  sh_audio->samplerate = ext.audio_sample_rate;
+  sh_audio->i_bps = sh_audio->channels * sh_audio->samplerate *
+                    ext.audio_bits_per_sample;
+  if (sh_audio->format != 0x1)
+    sh_audio->i_bps = nearestBitrate(sh_audio->i_bps /
+                                     ext.audio_compression_ratio);
+  sh_audio->wf->wFormatTag = sh_audio->format;
+  sh_audio->wf->nChannels = sh_audio->channels;
+  sh_audio->wf->nSamplesPerSec = sh_audio->samplerate;
+  sh_audio->wf->nAvgBytesPerSec = sh_audio->i_bps / 8;
+  sh_audio->wf->nBlockAlign = sh_audio->channels * 2;
+  sh_audio->wf->wBitsPerSample = ext.audio_bits_per_sample;
+  sh_audio->wf->cbSize = 0;
+
+  mp_msg(MSGT_DEMUXER, MSGL_V,
+         "channels=%d bitspersample=%d samplerate=%d compression_ratio=%d\n",
+          ext.audio_channels, ext.audio_bits_per_sample,
+          ext.audio_sample_rate, ext.audio_compression_ratio);
+  return 1;
+out:
+  stream_reset(demuxer->stream);
+  stream_seek(demuxer->stream, orig_pos);
+  return 0;
+}
 
 demuxer_t* demux_open_nuv ( demuxer_t* demuxer )
 {
@@ -282,6 +387,13 @@
 	    sh_audio->wf->cbSize = 0;
 	}
 
+	/* Check for extended data (X frame) and read settings from it */
+	if (!demux_xscan_nuv(demuxer, rtjpeg_fileheader.width,
+	                     rtjpeg_fileheader.height))
+	  /* Otherwise assume defaults */
+	  mp_msg(MSGT_DEMUXER, MSGL_V, "No NUV extended frame, using defaults\n");
+
+
 	priv->index_list = (nuv_position_t*) malloc(sizeof(nuv_position_t));
 	priv->index_list->frame = 0;
 	priv->index_list->time = 0;
@@ -304,9 +416,12 @@
 	if(stream_read(demuxer->stream,(char*)&ns,sizeof(ns)) != sizeof(ns))
 		return 0;
 
-	if ( strncmp ( ns.finfo, "NuppelVideo", 12 ) ) 
+	if ( strncmp ( ns.finfo, "NuppelVideo", 12 ) &&
+	     strncmp ( ns.finfo, "MythTVVideo", 12 ) ) 
 		return 0; /* Not a NuppelVideo file */
-	if ( strncmp ( ns.version, "0.05", 5 ) ) 
+	if ( strncmp ( ns.version, "0.05", 5 ) &&
+	     strncmp ( ns.version, "0.06", 5 ) &&
+	     strncmp ( ns.version, "0.07", 5 ) )
 		return 0; /* Wrong version NuppelVideo file */
 
 	/* Return to original position */
--- a/libmpdemux/nuppelvideo.h	Mon Aug 01 18:52:20 2005 +0000
+++ b/libmpdemux/nuppelvideo.h	Mon Aug 01 18:52:20 2005 +0000
@@ -71,6 +71,34 @@
    					// R:     do not use here! (fixed 'RTjjjjjjjjjjjjjj')
 } rtframeheader;
 
+/* for MythTV */
+typedef struct __attribute__((packed)) extendeddata
+{
+   int version;            // yes, this is repeated from the file header
+   int video_fourcc;       // video encoding method used 
+   int audio_fourcc;       // audio encoding method used
+   // generic data
+   int audio_sample_rate;
+   int audio_bits_per_sample;
+   int audio_channels;
+   // codec specific
+   // mp3lame
+   int audio_compression_ratio;
+   int audio_quality;
+   // rtjpeg
+   int rtjpeg_quality;
+   int rtjpeg_luma_filter;
+   int rtjpeg_chroma_filter;
+   // libavcodec
+   int lavc_bitrate;
+   int lavc_qmin;
+   int lavc_qmax;
+   int lavc_maxqdiff;
+   // unused for later -- total size of 128 integers.
+   // new fields must be added at the end, above this comment.
+   int expansion[113];
+} extendeddata;
+
 #define FRAMEHEADERSIZE sizeof(rtframeheader)
 #define FILEHEADERSIZE  sizeof(rtfileheader)
 
@@ -108,4 +136,21 @@
     (h)->timecode = le2me_32((h)->timecode);			\
     (h)->packetlength = le2me_32((h)->packetlength);		\
   }
+#define le2me_extendeddata(h) {					\
+   (h)->version = le2me_32((h)->version);			\
+   (h)->video_fourcc = le2me_32((h)->video_fourcc);		\
+   (h)->audio_fourcc = le2me_32((h)->audio_fourcc);		\
+   (h)->audio_sample_rate = le2me_32((h)->audio_sample_rate);	\
+   (h)->audio_bits_per_sample = le2me_32((h)->audio_bits_per_sample);\
+   (h)->audio_channels = le2me_32((h)->audio_channels);		\
+   (h)->audio_compression_ratio = le2me_32((h)->audio_compression_ratio);\
+   (h)->audio_quality = le2me_32((h)->audio_quality);		\
+   (h)->rtjpeg_quality = le2me_32((h)->rtjpeg_quality);		\
+   (h)->rtjpeg_luma_filter = le2me_32((h)->rtjpeg_luma_filter);	\
+   (h)->rtjpeg_chroma_filter = le2me_32((h)->rtjpeg_chroma_filter);\
+   (h)->lavc_bitrate = le2me_32((h)->lavc_bitrate);		\
+   (h)->lavc_qmin = le2me_32((h)->lavc_qmin);			\
+   (h)->lavc_qmax = le2me_32((h)->lavc_qmax);			\
+   (h)->lavc_maxqdiff = le2me_32((h)->lavc_maxqdiff);		\
+  }