[mythtv] HD3000 buffer overrun solution - patches for hdtvrecorder.cpp/.h against mythtv 0.16

Eric Anderson rico99 at sbcglobal.net
Mon Dec 6 17:19:43 UTC 2004


Sure. Attached below.

-Eric


On Dec 6, 2004, at 9:02 AM, David George wrote:

> On 12/6/2004 11:36 AM, Eric Anderson wrote:
>
>> Diffs are attached below and should be applied against the 
>> 'release-0-16' CVS tag.
>
> Could you resubmit a single diff using -u (for unified).
>
> Thanks,
> David


-------------- next part --------------
Index: hdtvrecorder.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/hdtvrecorder.cpp,v
retrieving revision 1.26
diff -u -r1.26 hdtvrecorder.cpp
--- hdtvrecorder.cpp	6 Jul 2004 04:44:36 -0000	1.26
+++ hdtvrecorder.cpp	6 Dec 2004 17:17:03 -0000
@@ -142,13 +142,9 @@
 
 void HDTVRecorder::StartRecording(void)
 {
-    uint8_t * buf;
-    uint8_t * end;
-    unsigned int errors=0;
+    int ret;
     int len;
-    unsigned char data_byte[4];
     int insync = 0;
-    int ret;
 
     chanfd = open(videodevice.ascii(), O_RDWR);
     if (chanfd <= 0)
@@ -165,100 +161,200 @@
         return;
     }
 
+    /* 
+     * Spawn a new thread for doing the read transfers.
+     */
+
     encoding = true;
     recording = true;
 
-    while (encoding) 
-    {
-        insync = 0;
-        len = read(chanfd, &data_byte[0], 1); // read next byte
-        if (len == 0) 
-            return;  // end of file
-
-        if (data_byte[0] == SYNC_BYTE)
-        {
-            read(chanfd, buffer, 187); //read next 187 bytes-remainder of packet
-            // process next packet
-            while (encoding) 
-            {
-                len = read(chanfd, &data_byte[0], 4); // read next 4 bytes
+    // Create mutex variable
+    ret = pthread_mutex_init(&xfer_lock, NULL);
+    if (ret) {
+	cerr << "pthread_mutex_init() failed!" << endl;
+	perror("pthread_mutex_init()");
+	return;
+    }
+
+    // Create condition variables
+    ret = pthread_cond_init(&empty_cond, NULL);
+    if (ret) {
+	cerr << "pthread_cond_init() failed!" << endl;
+	perror("pthread_cond_init()");
+	return;
+    }
+    ret = pthread_cond_init(&full_cond, NULL);
+    if (ret) {
+	cerr << "pthread_cond_init() failed!" << endl;
+	perror("pthread_cond_init()");
+	return;
+    }
+
+    // Clear read/write pointers
+    rd_ptr = 0;
+    wr_ptr = 0;
+
+    // Create a new thread
+    ret = pthread_create(&xfer_thread, NULL, XferThread, this);
+    if (ret) {
+	cerr << "pthread_create() failed!" << endl;
+	perror("pthread_create()");
+	return;
+    }
+
+    while (encoding) {
+
+	// Ensure 188 bytes in buffer
+	WaitForData(188);
+	if (buffer[rd_ptr] != SYNC_BYTE) {
+	    insync = 0;
+	    RdPtrInc(1);
+	} else {
+	    insync++;
+	    RdPtrInc(188);
+
+	    if (insync == 10) {
+
+		// Loop
+		while (encoding) {
+		    int remainder;
+		    len = WaitForData(188);
+		    remainder = ProcessData(&buffer[rd_ptr], len);
+		    RdPtrInc(len - remainder);
+		}
+	    }
+	}
+    }
 
-                if (len != 4) 
-                   return;
+    FinishRecording();
+    recording = false;
 
-                len = read(chanfd, buffer+4, 184);
-                
-                if (len != 184) 
-                {
-                    cerr <<"HD End of file found in packet "<<len<<endl;
-                    return;
-                }
+    pthread_join(xfer_thread, NULL);
+	
+}
 
-                buf = buffer;
-                end = buf + 188;
+int HDTVRecorder::WaitForData(int bytes)
+{
+    int fifo_bytes;
 
-                buf[0] = data_byte[0];
-                buf[1] = data_byte[1];
-                buf[2] = data_byte[2];
-                buf[3] = data_byte[3];
+    while (1) {
+	fifo_bytes = wr_ptr - rd_ptr;
+	if (fifo_bytes < 0) {
+	    fifo_bytes = (HD_BUFFER_SIZE - rd_ptr);
+	    // The writer always copies HD_BUFFER_WRAP bytes *beyond* the 
+	    // end of the fifo. This gives us a minimum number of contiguous
+	    // bytes for routines that can't deal with the wrap. Another
+            // solution to this problem would be to have all code that accesses
+            // the buffer perform a wrap computation.
+	    fifo_bytes += (wr_ptr > HD_BUFFER_WRAP) ? HD_BUFFER_WRAP : wr_ptr;
+	}
+	if (fifo_bytes >= bytes) break;
 
-                if (buf[0] != SYNC_BYTE) 
-                {
-                    VERBOSE(VB_RECORD, "Bad SYNC byte!!!");
-                    errors++;
-                    insync = 0;
-                    break;
-                } 
-                else 
-                {
-                    insync++;
-                    if (insync == 10) 
-                    {
-                        int remainder = 0;
-                        // TRANSFER DATA
-                        while (encoding) 
-                        {
-                            if (paused)
-                            {
-                                mainpaused = true;
-                                pauseWait.wakeAll();
+	// Block on empty flag
+	pthread_mutex_lock(&xfer_lock);
+	pthread_cond_wait(&empty_cond, &xfer_lock);
+	pthread_mutex_unlock(&xfer_lock);
+    }
+    return(fifo_bytes);
+}
 
-                                usleep(50);
-                                continue;
-                            }
+int HDTVRecorder::WaitForSpace(int bytes)
+{
+    int fifo_space;
 
-                            ret = read(chanfd, &(buffer[remainder]), 
-                                       188*PACKETS - remainder);
+    while (1) {
+	fifo_space = rd_ptr - wr_ptr;
+	if (fifo_space <= 0) {
+	    fifo_space += HD_BUFFER_SIZE;
+	}
+	if (fifo_space >= bytes) break;
 
-                            if (ret < 0)
-                            {
-                                cerr << "HD error reading from: "
-                                     << videodevice << endl;
-                                perror("read");
-                                continue;
-                            }
-                            else if (ret > 0)
-                            {
-                                ret += remainder;
-                                remainder = ProcessData(buffer, ret);
-                                if (remainder > 0) // leftover bytes
-                                    memmove(&(buffer[0]), 
-                                            &(buffer[188*PACKETS-remainder]),
-                                            remainder);
-                            }
+	// Block on full flag
+	pthread_mutex_lock(&xfer_lock);
+	pthread_cond_wait(&empty_cond, &xfer_lock);
+	pthread_mutex_unlock(&xfer_lock);
+    }
+    return(fifo_space);
+}
 
-                        }
-                    }
-                }
-            } // while(2)
-        }
-    } // while(1)
 
-    FinishRecording();
 
-    recording = false;
+void HDTVRecorder::RdPtrInc(int bytes)
+{
+    int rd_ptr_tmp = rd_ptr + bytes;
+
+    if (rd_ptr_tmp >= HD_BUFFER_SIZE) rd_ptr_tmp -= HD_BUFFER_SIZE;
+    rd_ptr = rd_ptr_tmp;
+    pthread_cond_signal(&full_cond);  // No longer full.
+}
+
+void HDTVRecorder::WrPtrInc(int bytes)
+{
+    int wr_ptr_tmp = wr_ptr + bytes;
+
+    if (wr_ptr_tmp >= HD_BUFFER_SIZE) wr_ptr_tmp -= HD_BUFFER_SIZE;
+    wr_ptr = wr_ptr_tmp;
+    pthread_cond_signal(&empty_cond);  // No longer empty.
+}
+
+
+void *HDTVRecorder::XferThread(void *param)
+{
+    HDTVRecorder *hdtvrec = (HDTVRecorder *)param;
+    hdtvrec->XferLoop();
+
+    return NULL;
+}
+
+
+int HDTVRecorder::XferLoop()
+{
+    int read_bytes;
+    int ret;
+
+    while (recording) {
+
+	// Wait for space
+	read_bytes = WaitForSpace(HD_BUFFER_MIN_READ);
+
+	// Never read more then HD_BUFFER_MAX_READ
+	if (read_bytes > HD_BUFFER_MAX_READ) read_bytes = HD_BUFFER_MAX_READ;
+
+	// Avoid reading beyond end of buffer+wrap
+	if (wr_ptr + read_bytes > HD_BUFFER_SIZE + HD_BUFFER_WRAP) {
+	    read_bytes = HD_BUFFER_SIZE + HD_BUFFER_WRAP - wr_ptr;
+	}
+
+	ret = read(chanfd, &buffer[wr_ptr], read_bytes);
+	if (ret < 0) {
+	    cerr << "HD error reading from: " << videodevice << endl;
+	    perror("read");
+	    continue;
+	} else if (ret > 0) {
+	    // If we loaded beyond HD_BUFFER_SIZE, copy remainder from end to start.
+	    if (wr_ptr + ret > HD_BUFFER_SIZE) {
+		memmove(buffer, &buffer[HD_BUFFER_SIZE], (wr_ptr + ret - HD_BUFFER_SIZE));
+	    }
+	    // If we loaded below HD_BUFFER_WRAP, copy remainder from start to end.
+	    if (wr_ptr < HD_BUFFER_WRAP) {
+		memmove(&buffer[HD_BUFFER_SIZE + wr_ptr], &buffer[wr_ptr], (HD_BUFFER_WRAP - wr_ptr));
+	    }
+	}
+
+	// Advance write pointer
+	WrPtrInc(ret);
+	pthread_cond_signal(&empty_cond);
+    }
+
+    // Close the device
+    if (chanfd > 0) close(chanfd);
+    chanfd = 0;
+
+    return(0);
 }
 
+
+
 int HDTVRecorder::GetVideoFd(void)
 {
     return chanfd;
Index: hdtvrecorder.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/hdtvrecorder.h,v
retrieving revision 1.17
diff -u -r1.17 hdtvrecorder.h
--- hdtvrecorder.h	2 Jun 2004 03:45:29 -0000	1.17
+++ hdtvrecorder.h	6 Dec 2004 17:17:03 -0000
@@ -47,6 +47,13 @@
     bool SetupRecording();
     void FinishRecording();
 
+    int WaitForData(int bytes);
+    int WaitForSpace(int bytes);
+    void RdPtrInc(int bytes);
+    void WrPtrInc(int bytes);
+    static void *XferThread(void *param);
+    int XferLoop();
+
     int ProcessData(unsigned char *buffer, int len);
     void FindKeyframes(const unsigned char *buffer, 
                        int packet_start_pos,
@@ -97,8 +104,21 @@
     int lowest_video_pid;
     int video_pid_packets;
 
-#define HD_BUFFER_SIZE 255868
-    unsigned char buffer[HD_BUFFER_SIZE];
+    int wr_ptr;
+    int rd_ptr;
+
+    pthread_t xfer_thread;
+    pthread_mutex_t xfer_lock;
+    pthread_cond_t empty_cond;
+    pthread_cond_t full_cond;
+
+
+#define HD_BUFFER_SIZE      (16*1024*1024)
+#define HD_BUFFER_WRAP      256
+#define HD_BUFFER_MIN_READ  (8*1024)
+#define HD_BUFFER_MAX_READ  (1024*1024)
+
+    unsigned char buffer[HD_BUFFER_SIZE + HD_BUFFER_WRAP];
 };
 
 #endif
-------------- next part --------------




More information about the mythtv-dev mailing list