# HG changeset patch
# User Petri Hintukainen <phintuka@users.sourceforge.net>
# Date 1239974783 -10800
# Node ID a5469413b1386432591cc9cdd7d06445f455b01a
# Parent  0f9b0da1e22c526116af74cb5f8825604073c99b
Support for HDMV/BluRay ts variant:
- Added file type extensions m2ts and mts
- Added support for HDMV 192-byte ts packets
  (ignore 4-byte header before each ts packet).
  TS packet size is autodetected while syncing
- Changed NPKT_PER_READ from 100 to 96. This results to integral number
  of 188-byte or 192-byte packets (188*96 == 192*94).
- Support audio inside PES PID 0xfd.
- Added stream descriptor STREAM_SPU_HDMV (0x90).
  Ignore HDMV subtitle streams (no decoder yet).

diff -r 0f9b0da1e22c -r a5469413b138 src/demuxers/demux_ts.c
--- a/src/demuxers/demux_ts.c	Mon Apr 13 21:39:54 2009 +0100
+++ b/src/demuxers/demux_ts.c	Fri Apr 17 16:26:23 2009 +0300
@@ -32,6 +32,10 @@
  *
  * Date        Author
  * ----        ------
+ *
+ *  8-Apr-2009 Petri Hintukainen <phi@sdf-eu.org>
+ *                  - support for 192-byte packets (HDMV/BluRay)
+ *                  - support for audio inside PES PID 0xfd (HDMV/BluRay)
  *
  * 28-Nov-2004 Mike Lampard <mlampard>
  *                  - Added support for PMT sections larger than 1 ts packet 
@@ -172,9 +176,9 @@
 #define SYNC_BYTE   0x47
 
 #define MIN_SYNCS 3
-#define NPKT_PER_READ 100
+#define NPKT_PER_READ 96  // 96*188 = 94*192
 
-#define BUF_SIZE (NPKT_PER_READ * PKT_SIZE)
+#define BUF_SIZE (NPKT_PER_READ * (PKT_SIZE + 4))
 
 #define MAX_PES_BUF_SIZE 2048
 
@@ -220,6 +224,7 @@
       ISO_14496_PART10_VIDEO = 0x1b,    /* ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264) */
       STREAM_VIDEO_MPEG = 0x80,
       STREAM_AUDIO_AC3 = 0x81,
+      STREAM_SPU_HDMV = 0x90,
     } streamType;
 
 #define WRAP_THRESHOLD       270000
@@ -287,6 +292,10 @@
   input_plugin_t  *input;
 
   int              status;
+
+  int              hdmv;       /* -1 = unknown, 0 = mpeg-ts, 1 = hdmv/m2ts */
+  int              pkt_size;   /* TS packet size */
+  int              pkt_offset; /* TS packet offset */
 
   int              blockSize;
   int              rate;
@@ -446,12 +455,12 @@
 }
 
 /* Send a BUF_SPU_DVB to let xine know of that channel. */
-static void demux_send_special_spu_buf( demux_ts_t *this, int spu_channel )
+static void demux_send_special_spu_buf( demux_ts_t *this, uint32_t spu_type, int spu_channel )
 {
   buf_element_t *buf;
 
   buf = this->video_fifo->buffer_pool_alloc( this->video_fifo );
-  buf->type = BUF_SPU_DVB|spu_channel;
+  buf->type = spu_type|spu_channel;
   buf->content = buf->mem;
   buf->size = 0;
   this->video_fifo->put( this->video_fifo, buf );
@@ -741,7 +750,7 @@
   p += header_len + 9;
   packet_len -= header_len + 3;
 
-  if (stream_id == 0xbd) {
+  if (stream_id == 0xbd || stream_id == 0xfd /* HDMV */) {
 
     int spu_id;
       
@@ -1291,6 +1300,20 @@
       return;
     }
 
+#ifdef TS_PMT_LOG
+    printf ("demux_ts: PMT: pid: 0x%.4x stream_type: 0x%.2x info_length: %d",
+	    pid, stream[0], stream_info_length);
+    if (stream_info_length > 0) {
+      printf (" info: ");
+      int ind;
+      for (ind = 5; ind < coded_length; ind++)
+	printf ("%.2x ", stream[ind]);
+      for (ind = 5; ind < coded_length; ind++)
+	printf ("%c", stream[ind] >= 32 && stream[ind] < 127 ? stream[ind] : '.');
+    }
+    printf ("\n");
+#endif
+
     /*
      * Squirrel away the first audio and the first video stream. TBD: there
      * should really be a way to select the stream of interest.
@@ -1413,7 +1436,7 @@
 		lang->media_index = this->media_num;
 		this->media[this->media_num].type = no;
 		demux_ts_pes_new(this, this->media_num, pid, this->video_fifo, stream[0]);
-		demux_send_special_spu_buf( this, no );
+		demux_send_special_spu_buf( this, BUF_SPU_DVB, no );
 #ifdef TS_LOG
 		printf("demux_ts: DVBSUB: pid 0x%.4x: %s  page %ld %ld type %2.2x\n",
 		       pid, lang->desc.lang,
@@ -1426,6 +1449,31 @@
       }
       break;
 
+    case STREAM_SPU_HDMV:
+      if (this->hdmv > 0) {
+	if (pid >= 0x1200 && pid < 0x1300) {
+#if 0 /* disabled: no HDMV SPU decoder yet */
+	  /* HDMV Presentation Graphics / SPU */
+	  demux_ts_spu_lang *lang = &this->spu_langs[this->spu_langs_count];
+
+	  memset(lang->desc.lang, 0, sizeof(lang->desc.lang));
+	  /*memcpy(lang->desc.lang, &stream[pos], 3);*/
+	  /*lang->desc.lang[3] = 0;*/
+	  lang->pid = pid;
+	  lang->media_index = this->media_num;
+	  this->media[this->media_num].type = this->spu_langs_count;
+	  demux_ts_pes_new(this, this->media_num, pid, this->video_fifo, stream[0]);
+	  demux_send_special_spu_buf( this, BUF_SPU_HDMV, this->spu_langs_count );
+	  this->spu_langs_count++;
+#endif
+#ifdef TS_PMT_LOG 
+	  printf("demux_ts: HDMV subtitle stream_type: 0x%.2x pid: 0x%.4x\n",
+		 stream[0], pid);
+#endif
+	  break;
+	}
+      }
+      /* fall thru */
     default:
 
 /* This following section handles all the cases where the audio track info is stored in PMT user info with stream id >= 0x80
@@ -1454,8 +1502,20 @@
                 this->media[this->media_num].type = this->audio_tracks_count;
                 demux_ts_get_lang_desc(this, this->audio_tracks[this->audio_tracks_count].lang,
                                        stream + 5, stream_info_length);
+#ifdef TS_PMT_LOG
+		printf ("demux_ts: PMT AC3? audio stream_type: 0x%.2x pid: 0x%.4x "
+			"format_identifier: 0x%.8x lang: %s\n",
+			stream[0], pid, format_identifier, 
+			this->audio_tracks[this->audio_tracks_count].lang); 
+#endif
                 this->audio_tracks_count++;
                 break;
+            } else {
+#ifdef TS_PMT_LOG
+		printf ("demux_ts: PMT unknown stream_type: 0x%.2x pid: 0x%.4x "
+			"format_identifier: 0x%.8x\n",
+			stream[0], pid, format_identifier);
+#endif
             }
         }
       } else {
@@ -1512,10 +1572,10 @@
   xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_ts: about to resync!\n");
 
   for (p=0; p < npkt_read; p++) {
-    for(n=0; n < PKT_SIZE; n++) {
+    for(n=0; n < this->pkt_size; n++) {
       sync_ok = 1;
       for (i=0; i < MIN(MIN_SYNCS, npkt_read - p); i++) {
-	if (buf[n + ((i+p) * PKT_SIZE)] != SYNC_BYTE) {
+	if (buf[this->pkt_offset + n + ((i+p) * this->pkt_size)] != SYNC_BYTE) {
 	  sync_ok = 0;
 	  break;
 	}
@@ -1527,13 +1587,13 @@
 
   if (sync_ok) {
     /* Found sync, fill in */
-    memmove(&buf[0], &buf[n + p * PKT_SIZE],
-	    ((PKT_SIZE * (npkt_read - p)) - n));
+    memmove(&buf[0], &buf[n + p * this->pkt_size],
+	    ((this->pkt_size * (npkt_read - p)) - n));
     read_length = this->input->read(this->input,
-				    &buf[(PKT_SIZE * (npkt_read - p)) - n],
-				    n + p * PKT_SIZE);
+				    &buf[(this->pkt_size * (npkt_read - p)) - n],
+				    n + p * this->pkt_size);
     /* FIXME: when read_length is not as required... we now stop demuxing */
-    if (read_length != (n + p * PKT_SIZE)) {
+    if (read_length != (n + p * this->pkt_size)) {
       xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, 
 	       "demux_ts_tsync_correct: sync found, but read failed\n");
       return 0;
@@ -1552,6 +1612,32 @@
 
   sync_ok = 1;
 
+  if (this->hdmv) {
+    this->pkt_size   = PKT_SIZE + 4; /* ts packet + 4-byte header */
+    this->pkt_offset = 4;
+    for (i=0; i < MIN(MIN_SYNCS, npkt_read - 3); i++) {
+      if (buf[this->pkt_offset + i * this->pkt_size] != SYNC_BYTE) {
+	sync_ok = 0;
+	break;
+      }
+    }
+    if (sync_ok) {
+      if (this->hdmv < 0) {
+        /* fix npkt_read (packet size is 192, not 188) */
+        this->npkt_read = npkt_read * PKT_SIZE / this->pkt_size;
+      }
+      this->hdmv = 1;
+      return sync_ok;
+    }
+    if (this->hdmv > 0)
+      return sync_correct(this, buf, npkt_read);
+
+    /* plain ts */
+    this->hdmv       = 0;
+    this->pkt_size   = PKT_SIZE;
+    this->pkt_offset = 0;
+  }
+
   for (i=0; i < MIN(MIN_SYNCS, npkt_read); i++) {
     if (buf[i * PKT_SIZE] != SYNC_BYTE) {
       sync_ok = 0;
@@ -1575,15 +1661,15 @@
     /* NEW: handle read returning less packets than NPKT_PER_READ... */
     do {
       read_length = this->input->read(this->input, this->buf,
-				      PKT_SIZE * NPKT_PER_READ);
-      if (read_length < 0 || read_length % PKT_SIZE) {
+				      this->pkt_size * NPKT_PER_READ);
+      if (read_length < 0 || read_length % this->pkt_size) {
 	xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, 
 		 "demux_ts: read returned %d bytes (not a multiple of %d!)\n",
-		 read_length, PKT_SIZE);
+		 read_length, this->pkt_size);
 	this->status = DEMUX_FINISHED;
 	return NULL;
       }
-      this->npkt_read = read_length / PKT_SIZE;
+      this->npkt_read = read_length / this->pkt_size;
 
 #ifdef TS_READ_STATS
       this->rstat[this->npkt_read]++;
@@ -1610,7 +1696,7 @@
       return NULL;
     }
   }
-  return_pointer = &(this->buf)[PKT_SIZE * this->packet_number];
+  return_pointer = &(this->buf)[this->pkt_offset + this->pkt_size * this->packet_number];
   this->packet_number++;
   return return_pointer;
 }
@@ -1758,7 +1844,7 @@
   /*
    * Discard packets that are obviously bad.
    */
-  if (sync_byte != 0x47) {
+  if (sync_byte != SYNC_BYTE) {
     xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, 
 	     "demux error! invalid ts sync byte %.2x\n", sync_byte);
     return;
@@ -2170,31 +2256,17 @@
     }
 }
 
+static int detect_ts(uint8_t *buf, size_t len, int ts_size)
+{
+  int    i, j;
+  int    try_again, ts_detected = 0;
+  size_t packs = len / ts_size - 2;
 
-static demux_plugin_t *open_plugin (demux_class_t *class_gen, 
-				    xine_stream_t *stream, 
-				    input_plugin_t *input) {
-  
-  demux_ts_t *this;
-  int         i;
-
-  switch (stream->content_detection_method) {
-
-  case METHOD_BY_CONTENT: {
-    uint8_t buf[2069];
-    int     i, j;
-    int     try_again, ts_detected;
-
-    if (!_x_demux_read_header(input, buf, 2069))
-      return NULL;
-
-    ts_detected = 0;
-
-    for (i = 0; i < 188; i++) {
+  for (i = 0; i < ts_size; i++) {
       try_again = 0;
-      if (buf[i] == 0x47) {
-	for (j = 1; j <= 10; j++) {
-	  if (buf[i + j*188] != 0x47) {
+    if (buf[i] == SYNC_BYTE) {
+      for (j = 1; j < packs; j++) {
+	if (buf[i + j*ts_size] != SYNC_BYTE) {
 	    try_again = 1;
 	    break;
 	  }
@@ -2208,13 +2280,41 @@
       }
     }
 
-    if (!ts_detected)
+  return ts_detected;
+}
+
+static demux_plugin_t *open_plugin (demux_class_t *class_gen, 
+				    xine_stream_t *stream, 
+				    input_plugin_t *input) {
+  
+  demux_ts_t *this;
+  int         i;
+  int         hdmv = -1;
+
+  switch (stream->content_detection_method) {
+
+  case METHOD_BY_CONTENT: {
+    uint8_t buf[2069];
+
+    if (!_x_demux_read_header(input, buf, sizeof(buf)))
+      return NULL;
+
+    if (detect_ts(buf, sizeof(buf), PKT_SIZE))
+      hdmv = 0;
+    else if (detect_ts(buf, sizeof(buf), PKT_SIZE+4)) 
+      hdmv = 1;
+    else 
       return NULL;
   }
     break;
 
   case METHOD_BY_EXTENSION: {
     const char *const mrl = input->get_mrl (input);
+
+    if (_x_demux_check_extension (mrl, "m2ts mts"))
+      hdmv = 1;
+    else
+      hdmv = 0;
 
     /* check extension */
     const char *const extensions = class_gen->get_extensions (class_gen);
@@ -2302,6 +2402,11 @@
   /* dvb */
   this->event_queue = xine_event_new_queue (this->stream);
   
+  /* HDMV */
+  this->hdmv       = hdmv;
+  this->pkt_offset = (hdmv > 0) ? 4 : 0;
+  this->pkt_size   = PKT_SIZE + this->pkt_offset;
+
   this->numPreview=0;
   
   return &this->demux_plugin;
@@ -2320,7 +2425,7 @@
 }
 
 static const char *get_extensions (demux_class_t *this_gen) {
-  return "ts m2t trp";
+  return "ts m2t trp m2ts mts";
 }
 
 static const char *get_mimetypes (demux_class_t *this_gen) {
