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

Eric Anderson rico99 at sbcglobal.net
Mon Dec 6 16:36:11 UTC 2004


Hi -

I'm relatively new to myth, but some friends of mine and I have recently
bought HD3000 cards and have been suffering from a lot of kernel
buffer overruns when recording with Myth.

I discovered that by adding a separate thread to read data from the 
device, these
buffer overruns pretty much go away. There's already bufffering in the 
RingBuffer
code which avoids problems with the file write blocking for an extended 
period
of time, but if any of the database calls (which track ff/rew 
positions) block for
a while, the 0.16 myth code pretty much stops reading from the video 
device. If
this condition persists for long enough, you get a buffer overrun. 
Other solutions,
such as increasing the kernel buffer size help some, but don't 
eliminate the problem
completely.

So with the new read thread, I've seen a huge difference. I no longer 
see buffer
overruns while recording a hi-def program, even under heavy system load.

By the way, I set the read buffer size to 16-MB. This may be way 
over-kill, but I
haven't had time to add watermark code, etc. -- to find out how much is 
needed.
It may be possible to trim this somewhat, although it may be system 
dependent, etc.

My system is a 2.6-GHz Xeon, HD-3000 card, Sony OEM PC, 1-Gig of memory
running a custom 2.6.5 kernel.

Diffs are attached below and should be applied against the 
'release-0-16' CVS tag.

Enjoy!

-Eric Anderson
rico99  .. sbcglobal.net


-------------- next part --------------
Index: hdtvrecorder.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/hdtvrecorder.cpp,v
retrieving revision 1.26
diff -r1.26 hdtvrecorder.cpp
145,147c145
<     uint8_t * buf;
<     uint8_t * end;
<     unsigned int errors=0;
---
>     int ret;
149d146
<     unsigned char data_byte[4];
151d147
<     int ret;
167a164,167
>     /* 
>      * Spawn a new thread for doing the read transfers.
>      */
> 
171,184c171,227
<     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);
> 		}
> 	    }
> 	}
>     }
186,187c229,230
<                 if (len != 4) 
<                    return;
---
>     FinishRecording();
>     recording = false;
189,195c232,234
<                 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);
> 	
> }
197,198c236,238
<                 buf = buffer;
<                 end = buf + 188;
---
> int HDTVRecorder::WaitForData(int bytes)
> {
>     int fifo_bytes;
200,203c240,251
<                 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;
205,224c253,259
<                 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);
> }
226,228c261,263
<                                 usleep(50);
<                                 continue;
<                             }
---
> int HDTVRecorder::WaitForSpace(int bytes)
> {
>     int fifo_space;
230,231c265,270
<                             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;
233,248c272,278
<                             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);
> }
250,255d279
<                         }
<                     }
<                 }
<             } // while(2)
<         }
<     } // while(1)
257d280
<     FinishRecording();
259c282,353
<     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);
261a356,357
> 
> 
Index: hdtvrecorder.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/hdtvrecorder.h,v
retrieving revision 1.17
diff -r1.17 hdtvrecorder.h
49a50,56
>     int WaitForData(int bytes);
>     int WaitForSpace(int bytes);
>     void RdPtrInc(int bytes);
>     void WrPtrInc(int bytes);
>     static void *XferThread(void *param);
>     int XferLoop();
> 
100,101c107,121
< #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];
-------------- next part --------------
  
   
-------------- next part --------------
Index: hdtvrecorder.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/hdtvrecorder.cpp,v
retrieving revision 1.26
diff -c -r1.26 hdtvrecorder.cpp
*** hdtvrecorder.cpp	6 Jul 2004 04:44:36 -0000	1.26
--- hdtvrecorder.cpp	6 Dec 2004 16:21:19 -0000
***************
*** 142,154 ****
  
  void HDTVRecorder::StartRecording(void)
  {
!     uint8_t * buf;
!     uint8_t * end;
!     unsigned int errors=0;
      int len;
-     unsigned char data_byte[4];
      int insync = 0;
-     int ret;
  
      chanfd = open(videodevice.ascii(), O_RDWR);
      if (chanfd <= 0)
--- 142,150 ----
  
  void HDTVRecorder::StartRecording(void)
  {
!     int ret;
      int len;
      int insync = 0;
  
      chanfd = open(videodevice.ascii(), O_RDWR);
      if (chanfd <= 0)
***************
*** 165,264 ****
          return;
      }
  
      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
  
!                 if (len != 4) 
!                    return;
  
!                 len = read(chanfd, buffer+4, 184);
!                 
!                 if (len != 184) 
!                 {
!                     cerr <<"HD End of file found in packet "<<len<<endl;
!                     return;
!                 }
  
!                 buf = buffer;
!                 end = buf + 188;
  
!                 buf[0] = data_byte[0];
!                 buf[1] = data_byte[1];
!                 buf[2] = data_byte[2];
!                 buf[3] = data_byte[3];
  
!                 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();
  
!                                 usleep(50);
!                                 continue;
!                             }
  
!                             ret = read(chanfd, &(buffer[remainder]), 
!                                        188*PACKETS - remainder);
  
!                             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);
!                             }
  
-                         }
-                     }
-                 }
-             } // while(2)
-         }
-     } // while(1)
  
-     FinishRecording();
  
!     recording = false;
  }
  
  int HDTVRecorder::GetVideoFd(void)
  {
      return chanfd;
--- 161,360 ----
          return;
      }
  
+     /* 
+      * Spawn a new thread for doing the read transfers.
+      */
+ 
      encoding = true;
      recording = true;
  
!     // 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);
! 		}
! 	    }
! 	}
!     }
  
!     FinishRecording();
!     recording = false;
  
!     pthread_join(xfer_thread, NULL);
! 	
! }
  
! int HDTVRecorder::WaitForData(int bytes)
! {
!     int fifo_bytes;
  
!     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;
  
! 	// Block on empty flag
! 	pthread_mutex_lock(&xfer_lock);
! 	pthread_cond_wait(&empty_cond, &xfer_lock);
! 	pthread_mutex_unlock(&xfer_lock);
!     }
!     return(fifo_bytes);
! }
  
! int HDTVRecorder::WaitForSpace(int bytes)
! {
!     int fifo_space;
  
!     while (1) {
! 	fifo_space = rd_ptr - wr_ptr;
! 	if (fifo_space <= 0) {
! 	    fifo_space += HD_BUFFER_SIZE;
! 	}
! 	if (fifo_space >= bytes) break;
  
! 	// Block on full flag
! 	pthread_mutex_lock(&xfer_lock);
! 	pthread_cond_wait(&empty_cond, &xfer_lock);
! 	pthread_mutex_unlock(&xfer_lock);
!     }
!     return(fifo_space);
! }
  
  
  
! 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 -c -r1.17 hdtvrecorder.h
*** hdtvrecorder.h	2 Jun 2004 03:45:29 -0000	1.17
--- hdtvrecorder.h	6 Dec 2004 16:21:19 -0000
***************
*** 47,52 ****
--- 47,59 ----
      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,104 ****
      int lowest_video_pid;
      int video_pid_packets;
  
! #define HD_BUFFER_SIZE 255868
!     unsigned char buffer[HD_BUFFER_SIZE];
  };
  
  #endif
--- 104,124 ----
      int lowest_video_pid;
      int video_pid_packets;
  
!     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


More information about the mythtv-dev mailing list