[mythtv] [patch] hdtvrecorder and pat/pmt parsing

Steve Brown sbrown at cortland.com
Wed Dec 3 17:53:53 EST 2003


Rather than parsing the pat and pmt, hdtvrecorder assumed that the pmt 
pid was 1 less than the lowest pid found in the first 50 ts packets. 
This usually works.

However, it broke for KING-HD here in Seattle, where the pmt is 0x30 and 
the video/audio pids are 0x11 and 0x14 respectively. The symptom is an 
"avformat error -1" from avformatdecoder.cpp.

The attached patch now plays all my local hd stations. Other 
prebuffering and osd update hiccups are unaffected.

I'd appreciate some confirmation that I haven't busted something in the 
process.

The patch is against the current cvs.

Steve



-------------- next part --------------
Index: libs/libmythtv/hdtvrecorder.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/hdtvrecorder.cpp,v
retrieving revision 1.11
diff -u -r1.11 hdtvrecorder.cpp
--- libs/libmythtv/hdtvrecorder.cpp	16 Nov 2003 19:08:33 -0000	1.11
+++ libs/libmythtv/hdtvrecorder.cpp	3 Dec 2003 22:06:24 -0000
@@ -98,7 +98,8 @@
     desired_program = 0;
     pat_pid = 0;
     psip_pid = 0x1ffb;
-    base_pid = 0; // will be filled in when we find it
+    audio_pid = video_pid = -1;
+    pmt_pid = -1;
     output_base_pid = 0;
     ts_packets = 0; // cumulative packet count
 
@@ -456,6 +457,10 @@
         return false;  // new packet would exceed boundary, skip it
 
     pos = 8;  // section header is 8 bytes, skip it
+//fprintf(stderr, "%s: old:0x%0x 0x%0x new: 0x0001 0x%0x\n", 
+//	__FUNCTION__, (buffer[pos] << 8) | buffer[pos + 1],
+//	(buffer[pos + 2] << 8) | buffer[pos + 3],
+//	pid);
 
     buffer[pos] = 0x00;
     buffer[pos+1] = 0x01;
@@ -481,8 +486,7 @@
     return true;
 }
 
-bool HDTVRecorder::RewritePMT(unsigned char *buffer, int old_pid, int new_pid,
-                              int pkt_len)
+bool HDTVRecorder::RewritePMT(unsigned char *buffer, int new_pid, int pkt_len)
 {
     // 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
@@ -492,6 +496,7 @@
     int pid;
     int sec_len = ((buffer[1] & 0x0f) << 8) + buffer[2];
     sec_len += 3;  // adjust for 3 header bytes
+//fprintf(stderr, "%s: new_pid:0x%0x\n", __FUNCTION__, new_pid);
 
     // make sure section doesn't span packet bounds
     if (sec_len > pkt_len)
@@ -500,20 +505,6 @@
     // 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
@@ -525,13 +516,19 @@
     while (pos < sec_len - 4) {
         if (buffer[pos] == 0xff)
             break;  // hit end of packet, ok here
+        int stream_type = (int)buffer[pos];
+//cerr << __FUNCTION__ << " stream_type:0x" << hex << stream_type << dec << endl;
+
         pos++;
 
         pid = ((buffer[pos] << 8) | buffer[pos+1]) & 0x1fff;
-        if (pid == VIDEO_PID(old_pid)) {
+
+        if (stream_type == 0x02) {
+            video_pid = pid;
             pid = VIDEO_PID(new_pid);
-        } 
-        else if (pid == AUDIO_PID(old_pid)) {
+        }
+        else if (stream_type == 0x81 | stream_type == 0x04) {
+            audio_pid = pid;
             pid = AUDIO_PID(new_pid);
         }
 
@@ -545,6 +542,20 @@
         pos += 2 + (((buffer[pos] << 8) | buffer[pos+1]) & 0x0fff);
     }
 
+    // rewrite pcr_pid
+    pid = ((buffer[8] << 8) | buffer[9]) & 0x1fff;
+    if (pid == video_pid) {
+        pid = VIDEO_PID(new_pid);
+    } 
+    else if (pid == audio_pid) {
+        pid = AUDIO_PID(new_pid);
+    }
+    else {
+        return false;  // don't know how to rewrite
+    }
+    buffer[8] = ((pid & 0x1f00) >> 8) | 0xe0;
+    buffer[9] = pid & 0x00ff;
+
     // fix the checksum
     unsigned int crc = mpegts_crc32(buffer, sec_len - 4);
     buffer[sec_len - 4] = (crc & 0xff000000) >> 24;
@@ -608,7 +619,7 @@
         }
         payload_unit_start_indicator = (get1bit(buffer[pos], 1));
         pid = ((buffer[pos] << 8) + buffer[pos+1]) & 0x1fff;
-        //cerr << "found PID " << pid << endl;
+        //cerr << "found PID 0x" << hex << pid << dec << endl;
         pos += 2;
         if (get1bit(buffer[pos], 1)) 
         {
@@ -675,22 +686,43 @@
             // PIDs we are looking for, and can thus "tune" the
             // subprogram more quickly.
 
-            // For now we're using the dirty hack below instead.
-            
-            // We should rewrite the PAT to describe just the one
+            // We 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
             // channel changes.
-            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], output_base_pid,
-                               packet_end_pos - sec_start))
-                    ringBuffer->Write(&buffer[packet_start_pos], 188);
+            //
+            // Basically, the output PMT is pid 0x10, video is 0x11 and
+            // audio is 0x14. 
+            //
+            // sec_start should pretty much always be pos + 1, but
+            // just in case...
+            int sec_start = pos + buffer[pos] + 1;
+            int sec_len = (buffer[sec_start + 1] & 0x0f) << 8 | buffer[sec_start + 2]; 
+            int i;
+//cerr << __FUNCTION__ << " desired_program:" << 	desired_program 
+//                     << " table_id:0x" << hex << (int)buffer[sec_start] << dec
+//                     << " sec_len:" << sec_len << endl;
+            pmt_pid = -1; 
+            for (i = sec_start + 8; i < sec_start + 3 + sec_len - 4; i += 4) {
+                int prog_num = (buffer[i] << 8) | buffer[i + 1];
+                int prog_pid = (buffer[i + 2] & 0x0e) << 8 | buffer[i + 3];
+                if (prog_num == desired_program) {
+                    pmt_pid = prog_pid;
+                    break;
+                }
             }
+            if (pmt_pid == -1) {
+                pmt_pid = (buffer[sec_start + 8 + 2] & 0x0e) << 8 | 
+                                buffer[sec_start + 8 + 3];
+                cerr << __FUNCTION__ << " no match for desired program"
+                    << " using first program in PAT" << endl;
+            }
+            output_base_pid = 16;
+
+            // write to ringbuffer if pids gets rewritten successfully.
+            if (RewritePAT(&buffer[sec_start], output_base_pid,
+                           packet_end_pos - sec_start))
+                ringBuffer->Write(&buffer[packet_start_pos], 188);
         }
         else if (pid == psip_pid)
         {
@@ -704,7 +736,7 @@
             // downloaded XMLTV data.
 
         }
-        else if (pid == VIDEO_PID(base_pid))
+        else if (pid == video_pid)
         {
             FindKeyframes(buffer, packet_start_pos, pos, 
                     adaptation_field_control, payload_unit_start_indicator);
@@ -717,7 +749,7 @@
                 ringBuffer->Write(&buffer[packet_start_pos], 188);
             }
         }
-        else if (pid == AUDIO_PID(base_pid))
+        else if (pid == audio_pid)
         {
             // decoder needs audio, of course (just this PID)
             if (gopset || firstgoppos) 
@@ -727,7 +759,7 @@
                 ringBuffer->Write(&buffer[packet_start_pos], 188);
             }
         }
-        else if (pid == base_pid)
+        else if (pid == pmt_pid)
         {
             // decoder needs base PID
             int sec_start = pos + buffer[pos] + 1;
@@ -735,7 +767,7 @@
 
             // 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, output_base_pid,
+                if (RewritePMT(&(buffer[sec_start]), output_base_pid,
                                packet_end_pos - sec_start))
                 {
                     ringBuffer->Write(&buffer[packet_start_pos], 188);
@@ -746,39 +778,6 @@
                 ringBuffer->Write(&buffer[packet_start_pos], 188);
             }
         }
-        else 
-        {
-            // Not a PID we're watching
-            
-            // As a hack until we start decoding PAT, find the lowest
-            // video PID in the first 50 packets and record only that
-            // stream
-            if ((pid & 0xff0f) == 0x0001 && !base_pid) 
-            {
-                // Look for our desired subprogram here to ensure we're
-                // actually seeing packets from it... if not, we'll
-                // fall back on the 1st one.
-                int program_num = (pid & 0x00f0) >> 4;
-                if (program_num == desired_program && desired_program != 0) 
-                {
-                    base_pid = pid - 1;
-                    if (output_base_pid == 0)
-                        output_base_pid = 16;
-                }
-                else 
-                {
-                    if (pid < lowest_video_pid)
-                        lowest_video_pid = pid;
-                    video_pid_packets++;
-                    if (video_pid_packets >= 50) 
-                    {
-                        base_pid = lowest_video_pid - 1;
-                        if (output_base_pid == 0)
-                            output_base_pid = 0x10;
-                    }
-                }
-            }
-        }
         // Advance to next TS packet
         pos = packet_end_pos;
     }
@@ -796,8 +795,9 @@
     framesWritten = 0;
     gopset = false;
     firstgoppos = 0;
-    base_pid = 0;
     ts_packets = 0;
+    pmt_pid = -1;
+    video_pid = audio_pid = -1;
     lowest_video_pid = 0x1fff;
     video_pid_packets = 0;
     
@@ -876,7 +876,7 @@
 void HDTVRecorder::ChannelNameChanged(const QString& new_chan)
 {
     RecorderBase::ChannelNameChanged(new_chan);
-
+//cerr << __FUNCTION__ << " new_chan:" << new_chan << endl;
     desired_program = 0;
     // look up freqid
     pthread_mutex_lock(db_lock);
@@ -899,4 +899,5 @@
     {
         desired_program = atoi(freqid.mid(pos+1).ascii());
     }
+//cerr << __FUNCTION__ << " curChannelName:" << curChannelName << " desired_program:" << desired_program << endl;
 }
Index: libs/libmythtv/hdtvrecorder.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/hdtvrecorder.h,v
retrieving revision 1.9
diff -u -r1.9 hdtvrecorder.h
--- libs/libmythtv/hdtvrecorder.h	16 Nov 2003 19:08:33 -0000	1.9
+++ libs/libmythtv/hdtvrecorder.h	3 Dec 2003 22:06:24 -0000
@@ -51,8 +51,7 @@
     int ResyncStream(unsigned char *buffer, int curr_pos, int len);
     void RewritePID(unsigned char *buffer, int pid);
     bool RewritePAT(unsigned char *buffer, int pid, int pkt_len);
-    bool RewritePMT(unsigned char *buffer, int old_pid, int new_pid, 
-                    int pkt_len);
+    bool RewritePMT(unsigned char *buffer, int new_pid, int pkt_len);
     bool recording;
     bool encoding;
 
@@ -77,8 +76,10 @@
     int desired_program;
 
     int pat_pid;
+    int pmt_pid;
+    int video_pid;
+    int audio_pid;
     int psip_pid;
-    int base_pid;
     int output_base_pid;
 
     int ts_packets;


More information about the mythtv-dev mailing list