[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