[mythtv] [PATCH] (take 2) pcHDTV - Rewrite PIDs in PMT and PAT tables of recorded streams

Jason Hoos jhoos at thwack.net
Thu Oct 30 11:33:44 EST 2003


Patch is attached this time. :)

- Jason

On Thu, Oct 30, 2003 at 02:37:51AM -0600, Jason Hoos wrote:
> Since I was bound and determined this week to get both of my whopping 2 HDTV 
> stations working (I really need to get my condo association to fix the roof 
> antenna!), attached is another patch for hdtvrecorder.  This one adds code to 
> rewrite the stream PIDs that are in the PAT and PMT tables.  This came about 
> because one of the stations I get has 4 substations, and they list them in 
> reverse order in the PAT, which caused ffmpeg major headaches.  hdtvrecorder 
> stripped out all but program 2 (they were numbered 2-5), and ffmpeg tried to 
> grab the first station that was listed in the PAT, which was program 5.  It 
> then promptly died with an obscure "avformat error: -1" message.
> 
> This patch adds two private methods to hdtvreceiver called RewritePAT and 
> RewritePMT.  In addition to changing the PIDs in these tables to match the 
> PIDs that are really being written to disk, they also change the program 
> number of the output stream to 1.  Most importantly (with regards to the 
> above problem), RewritePAT removes mention of the other programs from the PAT 
> and leaves only the one that's actually in the file.
> 
> I've put this (and my previous patch) through a small battery of 
> channel-changing and recording tests, and it seems to be working pretty well 
> for the two stations I can test it with.
> 
> - Jason
> 

> _______________________________________________
> mythtv-dev mailing list
> mythtv-dev at mythtv.org
> http://mythtv.org/cgi-bin/mailman/listinfo/mythtv-dev

-------------- next part --------------
--- mythtv/libs/libmythtv/hdtvrecorder.cpp.new	2003-10-30 01:37:37.000000000 -0600
+++ mythtv/libs/libmythtv/hdtvrecorder.cpp	2003-10-30 02:06:31.000000000 -0600
@@ -18,6 +18,13 @@
    * Rewrite PIDs after channel change to be the same as before, so
      decoder can follow
 
+   Oct. 30, 2003 by Jason Hoos:
+   * added code to rewrite PIDs in the MPEG PAT and PMT tables.  fixes 
+     a problem with stations (particularly, WGN in Chicago) that list their 
+     programs in reverse order in the PAT, which was confusing ffmpeg 
+     (ffmpeg was looking for program 5, but only program 2 was left in 
+     the stream, so it choked).
+
    References / example code: 
      ATSC standards a.54, a.69 (www.atsc.org)
      ts2pes from mpegutils from dvb (www.linuxtv.org)
@@ -47,6 +58,7 @@
 extern "C" {
 #include "../libavcodec/avcodec.h"
 #include "../libavformat/avformat.h"
+#include "../libavformat/mpegts.h"
 }
 
 //#define BUFFER_SIZE 4096
@@ -438,6 +450,119 @@
     buffer[2] = b2;
 }
 
+bool HDTVRecorder::RewritePAT(unsigned char *buffer, int pid)
+{
+    int pos = 0;
+    int sec_len = ((buffer[1] << 8) | buffer[2]) & 0x0fff;
+    sec_len += 3;  // adjust for 3 header bytes
+
+    // Rewrite the PAT table, eliminating any other streams that the old
+    // one may have described so that ffmpeg can only find ours (useful since
+    // some stations put programs in their PAT tables in descending order 
+    // instead of ascending, which would confuse ffmpeg).  While we're at it
+    // change the program number to 1 in case it wasn't that already.
+    
+    // PAT format: 2-byte TSID, 2-byte PID, repeated for each PID
+    // TODO: typically PIDs seem to be padded with 1-bits.  Someone 
+    // with better knowledge of PAT table formats should confirm this.
+    // If it is wrong, fix the '0xe0' values in RewritePMT too.
+    
+    pos = 8;
+
+    buffer[pos] = 0x00;
+    buffer[pos+1] = 0x01;
+    buffer[pos+2] = 0xe0 | ((pid & 0x1f00) >> 8);
+    buffer[pos+3] = pid & 0xff;
+    pos += 4;
+
+    // rewrite section length, add 4 for checksum bytes
+    int new_len = (pos + 4) - 3;
+    buffer[1] = (buffer[1] & 0xf0) | ((new_len & 0x0f00) >> 8);
+    buffer[2] = new_len & 0xff;
+
+    // fix the checksum
+    unsigned int crc = mpegts_crc32(buffer, pos);
+    buffer[pos++] = (crc & 0xff000000) >> 24;
+    buffer[pos++] = (crc & 0x00ff0000) >> 16;
+    buffer[pos++] = (crc & 0x0000ff00) >> 8;
+    buffer[pos++] = (crc & 0x000000ff);
+
+    // pad the rest of the packet with 0xff
+    memset(buffer + pos, 0xff, sec_len - pos);
+
+    return true;
+}
+
+bool HDTVRecorder::RewritePMT(unsigned char *buffer, int old_pid, int new_pid)
+{
+    // TODO: if it's possible for a PMT to span packets, then this function
+    // doesn't know how to deal with anything after the first packet.  On the
+    // other hand, neither does ffmpeg as near as I can tell.
+    
+    int pos = 8;
+    int pid;
+    int sec_len = ((buffer[1] & 0x0f) << 8) + buffer[2];
+    sec_len += 3;  // adjust for 3 header bytes
+
+    // rewrite program number to 1
+    buffer[3] = 0x00;
+    buffer[4] = 0x01;
+
+    // rewrite pcr_pid
+    pid = ((buffer[pos] << 8) | buffer[pos+1]) & 0x1fff;
+    if (pid == VIDEO_PID(old_pid)) {
+        pid = VIDEO_PID(new_pid);
+    } 
+    else if (pid == AUDIO_PID(old_pid)) {
+        pid = AUDIO_PID(new_pid);
+    }
+    else {
+        return false;  // don't know how to rewrite
+    }
+    buffer[pos] = ((pid & 0x1f00) >> 8) | 0xe0;
+    buffer[pos+1] = pid & 0x00ff;
+    pos += 2;
+
+    // skip program info
+    if (buffer[pos] == 0xff)
+	return false;  // premature end of packet
+    pos += 2 + (((buffer[pos] << 8) | buffer[pos+1]) & 0x0fff);
+
+    // rewrite other pids
+    while (pos < sec_len - 4) {
+        if (buffer[pos] == 0xff)
+            break;  // hit end of packet, ok here
+        pos++;
+
+        pid = ((buffer[pos] << 8) | buffer[pos+1]) & 0x1fff;
+        if (pid == VIDEO_PID(old_pid)) {
+            pid = VIDEO_PID(new_pid);
+        } 
+        else if (pid == AUDIO_PID(old_pid)) {
+            pid = AUDIO_PID(new_pid);
+        }
+
+        buffer[pos] = ((pid & 0x1f00) >> 8) | 0xe0;
+        buffer[pos+1] = pid & 0x00ff;
+        pos += 2;
+
+        if (buffer[pos] == 0xff)
+            return false;  // premature end of packet
+	// bounds checked at top of while loop
+        pos += 2 + (((buffer[pos] << 8) | buffer[pos+1]) & 0x0fff);
+    }
+
+    // fix the checksum
+    unsigned int crc = mpegts_crc32(buffer, sec_len - 4);
+    buffer[sec_len - 4] = (crc & 0xff000000) >> 24;
+    buffer[sec_len - 3] = (crc & 0x00ff0000) >> 16;
+    buffer[sec_len - 2] = (crc & 0x0000ff00) >> 8;
+    buffer[sec_len - 1] = (crc & 0x000000ff);
+
+    return true;
+}
+
+    
 int HDTVRecorder::ProcessData(unsigned char *buffer, int len)
 {
     int pos = 0;
@@ -545,8 +670,8 @@
             // PID 0: Program Association Table -- lists all other
             // PIDs that make up the program(s) in the whole stream.
             // Based on info in this table, and on which subprogram
-            // the user // desires, we should determine which PIDs'
-            // payloads to write // to the ring buffer.
+            // the user desires, we should determine which PIDs'
+            // payloads to write to the ring buffer.
 
             // Example PAT scan code is in the pcHDTV's dtvscan.c,
             // demux_ts_parse_pat
@@ -561,11 +686,17 @@
             
             // We should rewrite the PAT to describe just the one
             // stream we are recording.  Always use the same set of
-            // PIDs // so the decoder has an easier time following
+            // PIDs so the decoder has an easier time following
             // channel changes.
-
-            // decoder needs PAT, write it to the stream
-            ringBuffer->Write(&buffer[packet_start_pos], 188);
+            if (base_pid) {
+                // sec_start should pretty much always be pos + 1, but
+                // just in case...
+                int sec_start = pos + buffer[pos] + 1;
+
+                // write to ringbuffer if pids gets rewritten successfully.
+                if (RewritePAT(&buffer[sec_start], first_base_pid))
+                    ringBuffer->Write(&buffer[packet_start_pos], 188);
+            }
         }
         else if (pid == psip_pid)
         {
@@ -589,8 +720,8 @@
                  // delay until first GOP to avoid decoder crash on res change
                 RewritePID(&(buffer[packet_start_pos]), 
                            VIDEO_PID(first_base_pid));
-            ringBuffer->Write(&buffer[packet_start_pos], 188);
-        }
+                ringBuffer->Write(&buffer[packet_start_pos], 188);
+            }
         }
         else if (pid == AUDIO_PID(base_pid))
         {
@@ -599,14 +730,26 @@
             {
                 RewritePID(&(buffer[packet_start_pos]), 
                            AUDIO_PID(first_base_pid));
-            ringBuffer->Write(&buffer[packet_start_pos], 188);
-        }
+                ringBuffer->Write(&buffer[packet_start_pos], 188);
+            }
         }
         else if (pid == base_pid)
         {
             // decoder needs base PID
+            int sec_start = pos + buffer[pos] + 1;
             RewritePID(&(buffer[packet_start_pos]), first_base_pid);
-            ringBuffer->Write(&buffer[packet_start_pos], 188);
+
+            // if it's a PMT table, rewrite the PIDs contained in it too
+            if (buffer[sec_start] == 0x02) {
+                if (RewritePMT(&(buffer[sec_start]), base_pid, first_base_pid))
+                {
+                    ringBuffer->Write(&buffer[packet_start_pos], 188);
+                }
+            }
+            else {
+                // some other kind of packet?  possible?  do we care?
+                ringBuffer->Write(&buffer[packet_start_pos], 188);
+            }
         }
         else 
         {
--- mythtv/libs/libmythtv/hdtvrecorder.h	23 Oct 2003 19:57:18 -0000	1.6
+++ mythtv/libs/libmythtv/hdtvrecorder.h	30 Oct 2003 08:08:41 -0000
@@ -48,6 +48,8 @@
 
     int ResyncStream(unsigned char *buffer, int curr_pos, int len);
     void RewritePID(unsigned char *buffer, int pid);
+    bool RewritePAT(unsigned char *buffer, int pid);
+    bool RewritePMT(unsigned char *buffer, int old_pid, int new_pid);
     bool recording;
     bool encoding;
 


More information about the mythtv-dev mailing list