[mythtv] Simple player

Matt Zimmerman mythtv-dev@snowman.net
Fri, 6 Dec 2002 19:17:29 -0500


--EuxKj2iCbKjpUGkD
Content-Type: multipart/mixed; boundary="vtzGhvizbBRQ85DL"
Content-Disposition: inline


--vtzGhvizbBRQ85DL
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Fri, Dec 06, 2002 at 06:42:29PM -0500, Mark Musone wrote:

> If I'm not mistaken, the files are in nuppel video format. a bunch of
> players can play them (check out the nupplevideo site) but most
> importantly, mplayer can play nupplevideo format (I havent myself tested
> it, and I'm not in linux right now, but it's simple to try it)
>=20
> mplayer can also handle converting and transcoding between different
> formats. So, in a nutshell, mplayer should be able to play it and also
> convert it to different formats..

I think that mplayer may be able to play MythTV 0.7 files if RTjpeg is used,
but I haven't bothered to look into this, since I don't use RTjpeg.

Stock mplayer can play vanilla NuppelVideo files (such as those produced by
nvrec), which use RTjpeg, but not MythTV files, which contain some
extensions to the NuppelVideo format and can use other codecs.

I have modified mplayer to understand MythTV video files (and thus mencoder
works as well).  It basically works, and can be used for viewing and
lossless conversion and transcoding, but there is still one issue to resolve
that I have not had time to track down.

The patch is attached.  If anyone can track down why mencoder complains:

duplicate 1 frame(s)!!!   =20

and fix it or tell me what the problem is, that would be fantastic.  I have
only tested it with mpeg4 video and mp3 audio.

THIS IS NOT A FINISHED WORK.  I WILL NOT ANSWER ANY QUESTIONS ABOUT THIS
PATCH FROM PEOPLE WHO DO NOT UNDERSTAND IT.  IF IT DOES NOT WORK FOR YOU,
EITHER FIX IT OR KEEP QUIET.  I WILL NOT SUPPORT IT IN ANY WAY UNTIL IT IS
COMPLETE.

--=20
 - mdz

--vtzGhvizbBRQ85DL
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="mplayer-mythtv.diff"
Content-Transfer-Encoding: quoted-printable

diff -ru orig/mplayer-0.90pre10/libmpdemux/demux_nuv.c mplayer-0.90pre10/li=
bmpdemux/demux_nuv.c
--- orig/mplayer-0.90pre10/libmpdemux/demux_nuv.c	2002-09-30 17:10:41.00000=
0000 -0400
+++ mplayer-0.90pre10/libmpdemux/demux_nuv.c	2002-11-27 23:55:57.000000000 =
-0500
@@ -43,6 +43,7 @@
 	nuv_position_t *current_position;
 } nuv_priv_t;
=20
+#define MKTAG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
=20
 /**
  * Seek to a position relative to the current position, indicated in time.
@@ -147,17 +148,19 @@
 	    return 0; /* EOF */
=20
 #if 0
-	printf("NUV frame: frametype: %c, comptype: %c, packetlength: %d\n",
+	printf("NUV frame: frametype: %c, comptype: %c, packetlength: %d, timecod=
e: %d\n",
 	    rtjpeg_frameheader.frametype, rtjpeg_frameheader.comptype,
-	    rtjpeg_frameheader.packetlength);
+	    rtjpeg_frameheader.packetlength, rtjpeg_frameheader.timecode);
 #endif
=20
 	/* Skip Seekpoint, Text and Sync for now */
 	if ((rtjpeg_frameheader.frametype =3D=3D 'R') ||
 	    (rtjpeg_frameheader.frametype =3D=3D 'T') ||
-	    (rtjpeg_frameheader.frametype =3D=3D 'S'))
+            (rtjpeg_frameheader.frametype =3D=3D 'X') ||
+            (rtjpeg_frameheader.frametype =3D=3D 'Q') ||
+            (rtjpeg_frameheader.frametype =3D=3D 'S'))
 	    return 1;
-=09
+
 	if (((rtjpeg_frameheader.frametype =3D=3D 'D') &&
 	    (rtjpeg_frameheader.comptype =3D=3D 'R')) ||
 	    (rtjpeg_frameheader.frametype =3D=3D 'V'))
@@ -175,13 +178,13 @@
 	    /* put RTjpeg tables, Video info to video buffer */
 	    stream_seek ( demuxer->stream, orig_pos );
 	    ds_read_packet ( demuxer->video, demuxer->stream, rtjpeg_frameheader.=
packetlength + 12,=20
-		    rtjpeg_frameheader.timecode*0.001, orig_pos, 0 );
+                             rtjpeg_frameheader.timecode*0.001, orig_pos, =
0 );
=20
=20
 	} else
 	/* copy PCM only */
 	if (demuxer->audio && (rtjpeg_frameheader.frametype =3D=3D 'A') &&
-	    (rtjpeg_frameheader.comptype =3D=3D '0'))
+	    1 /*(rtjpeg_frameheader.comptype =3D=3D '0')*/)
 	{
 	    priv->current_audio_frame++;
 	    if (want_audio) {
@@ -192,15 +195,95 @@
 			       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 );
 	    }
 	}
=20
 	return 1;
 }
=20
+/* Scan for the extended data in MythTV nuv streams */
+int demux_xscan_nuv ( demuxer_t* demuxer )
+{
+  int i;
+  struct rtframeheader rtjpeg_frameheader;
+  struct extendeddata ext;
+  sh_video_t* sh_video =3D demuxer->video->sh;
+  sh_audio_t* sh_audio =3D demuxer->audio->sh;
+
+  for( i =3D 0 ; i < 2 ; ++i ) {
+    if (stream_read ( demuxer->stream, (char*)& rtjpeg_frameheader, sizeof=
 ( rtjpeg_frameheader ) ) < sizeof(rtjpeg_frameheader))
+      return 0; /* EOF */
+
+    if (rtjpeg_frameheader.frametype !=3D 'X')
+      stream_skip( demuxer->stream, rtjpeg_frameheader.packetlength );
+  }
+ =20
+  if ( rtjpeg_frameheader.frametype !=3D 'X' ) {
+    stream_reset( demuxer->stream );
+    return 0; /* No X frame in the expected place */
+  }
+
+  if ( rtjpeg_frameheader.packetlength !=3D sizeof(ext) ) {
+    printf("NUV extended frame does not have expected length, ignoring\n");
+    stream_reset( demuxer->stream );
+    return 0;
+  }
+
+  if (stream_read( demuxer->stream, (char*)& ext, sizeof(ext)) < sizeof(ex=
t)) {
+    stream_reset( demuxer->stream );
+    return 0; /* EOF */
+  }
+
+  if ( ext.version !=3D 1 ) {
+    printf("NUV extended frame has unknown version number (%d), ignoring\n=
",
+           ext.version);
+    stream_reset( demuxer->stream );
+    return 0;
+  }
+
+  printf("Detected MythTV stream, reading extended format information\n");
+
+  /* Video parameters */
+  printf("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);
+  if ( ext.video_fourcc =3D=3D mmioFOURCC('R', 'J', 'P', 'G') ) {
+    sh_video->format =3D mmioFOURCC('N', 'U', 'V', '1');
+  } else {
+    sh_video->format =3D ext.video_fourcc;
+    sh_video->i_bps =3D ext.lavc_bitrate;
+  }
+
+  /* Audio parameters */
+  if ( ext.audio_fourcc =3D=3D mmioFOURCC('L', 'A', 'M', 'E') ) {
+    sh_audio->format =3D 0x55;
+  } else if ( ext.audio_fourcc =3D=3D mmioFOURCC('R', 'A', 'W', 'A') ) {
+    sh_audio->format =3D 0x1;
+  } else {
+    printf("Warning! unknown audio format %d\n", ext.audio_fourcc);
+  }
+
+  sh_audio->samplerate =3D ext.audio_sample_rate;
+  sh_audio->channels =3D ext.audio_channels;
+  sh_audio->i_bps =3D ext.audio_channels
+    * ext.audio_bits_per_sample * ext.audio_sample_rate /
+    ext.audio_compression_ratio;
+  sh_audio->wf->wBitsPerSample =3D ext.audio_bits_per_sample;
+  sh_audio->wf->nAvgBytesPerSec =3D sh_audio->i_bps / 8;
+  sh_audio->wf->nBlockAlign =3D sh_audio->channels * 2;
+  sh_audio->wf->cbSize =3D 0;
+  sh_audio->wf->nSamplesPerSec =3D ext.audio_sample_rate;
+  sh_audio->wf->wFormatTag =3D sh_audio->format;
+  sh_audio->wf->nChannels =3D ext.audio_channels;
+
+  stream_reset( demuxer->stream );
+
+  return 1;
+}
=20
 demuxer_t* demux_open_nuv ( demuxer_t* demuxer )
 {
@@ -240,8 +323,6 @@
          */
 	sh_video->ds =3D demuxer->video;
=20
-	/* Custom fourcc for internal MPlayer use */
-	sh_video->format =3D mmioFOURCC('N', 'U', 'V', '1');
=20
 	sh_video->disp_w =3D rtjpeg_fileheader.width;
 	sh_video->disp_h =3D rtjpeg_fileheader.height;
@@ -258,26 +339,42 @@
 	sh_video->fps =3D rtjpeg_fileheader.fps;
 	sh_video->frametime =3D 1 / sh_video->fps;
=20
-	if (rtjpeg_fileheader.audioblocks !=3D 0)
-	{
-	    sh_audio =3D new_sh_audio(demuxer, 0);
-	    demuxer->audio->sh =3D sh_audio;
-	    sh_audio->ds =3D demuxer->audio;
-	    sh_audio->format =3D 0x1;
-	    sh_audio->channels =3D 2;
-	    sh_audio->samplerate =3D 44100;
-	   =20
-	    sh_audio->wf =3D malloc(sizeof(WAVEFORMATEX));
-	    memset(sh_audio->wf, 0, sizeof(WAVEFORMATEX));
-	    sh_audio->wf->wFormatTag =3D sh_audio->format;
-	    sh_audio->wf->nChannels =3D sh_audio->channels;
-	    sh_audio->wf->wBitsPerSample =3D 16;
-	    sh_audio->wf->nSamplesPerSec =3D sh_audio->samplerate;
-	    sh_audio->wf->nAvgBytesPerSec =3D sh_audio->wf->nChannels*
-		sh_audio->wf->wBitsPerSample*sh_audio->wf->nSamplesPerSec/8;
-	    sh_audio->wf->nBlockAlign =3D sh_audio->channels * 2;
-	    sh_audio->wf->cbSize =3D 0;
-	}
+        if (rtjpeg_fileheader.audioblocks !=3D 0)
+          {
+            sh_audio =3D new_sh_audio(demuxer, 0);
+            demuxer->audio->sh =3D sh_audio;
+            sh_audio->ds =3D demuxer->audio;
+            sh_audio->wf =3D malloc(sizeof(WAVEFORMATEX));
+            memset(sh_audio->wf, 0, sizeof(WAVEFORMATEX));
+          }
+
+        /* Check for extended data (X frame) and read settings from it */
+        if (! demux_xscan_nuv( demuxer ) ) {
+          /* Otherwise assume defaults */
+          printf("No NUV extended frame, using defaults\n");
+
+          /* Custom fourcc for internal MPlayer use */
+          sh_video->format =3D mmioFOURCC('N', 'U', 'V', '1');
+
+          if (rtjpeg_fileheader.audioblocks !=3D 0)
+            {
+              sh_audio->format =3D 0x1;
+              sh_audio->channels =3D 2;
+              sh_audio->samplerate =3D 44100;
+              sh_audio->wf->wBitsPerSample =3D 16;
+            }
+
+          if (rtjpeg_fileheader.audioblocks !=3D 0)
+            {
+              sh_audio->wf->wFormatTag =3D sh_audio->format;
+              sh_audio->wf->nChannels =3D sh_audio->channels;
+              sh_audio->wf->nSamplesPerSec =3D sh_audio->samplerate;
+              sh_audio->wf->nAvgBytesPerSec =3D sh_audio->wf->nChannels*
+                sh_audio->wf->wBitsPerSample*sh_audio->wf->nSamplesPerSec/=
8;
+              sh_audio->wf->nBlockAlign =3D sh_audio->channels * 2;
+              sh_audio->wf->cbSize =3D 0;
+            }
+        }
=20
 	priv->index_list =3D (nuv_position_t*) malloc(sizeof(nuv_position_t));
 	priv->index_list->frame =3D 0;
@@ -300,9 +397,12 @@
=20
 	stream_read(demuxer->stream,(char*)&ns,sizeof(ns));
=20
-	if ( strncmp ( ns.finfo, "NuppelVideo", 12 ) )=20
+	if ( strncmp ( ns.finfo, "NuppelVideo", 12 ) &&
+	     strncmp ( ns.finfo, "MythTVVideo", 12 ) )=20
 		return 0; /* Not a NuppelVideo file */
-	if ( strncmp ( ns.version, "0.05", 5 ) )=20
+	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 */
=20
 	/* Return to original position */
diff -ru orig/mplayer-0.90pre10/libmpdemux/nuppelvideo.h mplayer-0.90pre10/=
libmpdemux/nuppelvideo.h
--- orig/mplayer-0.90pre10/libmpdemux/nuppelvideo.h	2001-12-27 17:20:15.000=
000000 -0500
+++ mplayer-0.90pre10/libmpdemux/nuppelvideo.h	2002-11-27 23:56:10.00000000=
0 -0500
@@ -90,3 +90,30 @@
     unsigned char *buffer_offset;
 } audbuffertyp;
=20
+/* for MythTV */
+typedef struct extendeddata
+{
+   int version;            // yes, this is repeated from the file header
+   int video_fourcc;       // video encoding method used=20
+   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;

--vtzGhvizbBRQ85DL--

--EuxKj2iCbKjpUGkD
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)

iD8DBQE98T4YArxCt0PiXR4RAkCmAKDTGu1ecq7RlpfIcmH5lxqN5GhxTQCeKYte
xZcOSL3OXwDzQNoSo5TbrG8=
=z4LI
-----END PGP SIGNATURE-----

--EuxKj2iCbKjpUGkD--