Skip to content
Browse files

Merge of various recorder changes from mythtv-rec2.

 * Fixes #9191. Channel switching doesn't work with cx18 driver.
 * Fixes #9451. IPTVRecorder doesn't produce proper TS packets for big DVB tables.
 * Fixes #9709. TFW IOBOUND.
 * This also fixes pause status locking in various recorders.
 * This also replaces a few spinlocks with QWaitConditions.
 * This also adds a DVB ASI recorder implementation in just 1033 lines. (Uninteresting to most).
 * This makes changes to the DeviceReadBuffer to make it more efficient and quicker to respond.
 * This changes the ThreadedFileWriter so that it can expand the level of buffering it does when there are temporary slowdowns in writing to disk.

This will probably cause some regressions, that's why I'm pushing this on a Monday. This refactors all the recorders and stream readers to eliminate a lot of duplicate code, and also fixes a number of small bugs and long time annoyances see the dkristjansson/mythtv-rec branch for details.
  • Loading branch information...
1 parent 8bf5157 commit 9b22460f53bf6313516f33987e4260897c6076a3 @daniel-kristjansson daniel-kristjansson committed May 11, 2011
Showing with 7,394 additions and 5,902 deletions.
  1. +11 −0 mythtv/configure
  2. +223 −98 mythtv/libs/libmythtv/DeviceReadBuffer.cpp
  3. +23 −29 mythtv/libs/libmythtv/DeviceReadBuffer.h
  4. +22 −234 mythtv/libs/libmythtv/NuppelVideoRecorder.cpp
  5. +6 −22 mythtv/libs/libmythtv/NuppelVideoRecorder.h
  6. +234 −297 mythtv/libs/libmythtv/ThreadedFileWriter.cpp
  7. +24 −33 mythtv/libs/libmythtv/ThreadedFileWriter.h
  8. +16 −6 mythtv/libs/libmythtv/analogsignalmonitor.cpp
  9. +50 −0 mythtv/libs/libmythtv/asichannel.cpp
  10. +44 −0 mythtv/libs/libmythtv/asichannel.h
  11. +194 −0 mythtv/libs/libmythtv/asirecorder.cpp
  12. +78 −0 mythtv/libs/libmythtv/asirecorder.h
  13. +126 −0 mythtv/libs/libmythtv/asisignalmonitor.cpp
  14. +36 −0 mythtv/libs/libmythtv/asisignalmonitor.h
  15. +408 −0 mythtv/libs/libmythtv/asistreamhandler.cpp
  16. +97 −0 mythtv/libs/libmythtv/asistreamhandler.h
  17. +231 −3 mythtv/libs/libmythtv/cardutil.cpp
  18. +41 −9 mythtv/libs/libmythtv/cardutil.h
  19. +358 −156 mythtv/libs/libmythtv/channelbase.cpp
  20. +61 −46 mythtv/libs/libmythtv/channelbase.h
  21. +0 −35 mythtv/libs/libmythtv/channelchangemonitor.cpp
  22. +0 −21 mythtv/libs/libmythtv/channelchangemonitor.h
  23. +20 −1 mythtv/libs/libmythtv/channelscan/channelscan_sm.cpp
  24. +2 −0 mythtv/libs/libmythtv/channelscan/channelscan_sm.h
  25. +23 −8 mythtv/libs/libmythtv/channelscan/channelscanner.cpp
  26. +7 −39 mythtv/libs/libmythtv/channelscan/scanwizardconfig.cpp
  27. +2 −0 mythtv/libs/libmythtv/channelscan/scanwizardconfig.h
  28. +115 −1 mythtv/libs/libmythtv/channelsettings.cpp
  29. +24 −3 mythtv/libs/libmythtv/channelsettings.h
  30. +124 −1 mythtv/libs/libmythtv/channelutil.cpp
  31. +30 −0 mythtv/libs/libmythtv/channelutil.h
  32. +279 −68 mythtv/libs/libmythtv/dtvchannel.cpp
  33. +53 −21 mythtv/libs/libmythtv/dtvchannel.h
  34. +8 −5 mythtv/libs/libmythtv/dtvconfparserhelpers.cpp
  35. +1 −0 mythtv/libs/libmythtv/dtvconfparserhelpers.h
  36. +312 −39 mythtv/libs/libmythtv/dtvrecorder.cpp
  37. +79 −16 mythtv/libs/libmythtv/dtvrecorder.h
  38. +2 −10 mythtv/libs/libmythtv/dtvsignalmonitor.cpp
  39. +0 −1 mythtv/libs/libmythtv/dtvsignalmonitor.h
  40. +1 −6 mythtv/libs/libmythtv/dummychannel.h
  41. +12 −158 mythtv/libs/libmythtv/dvbchannel.cpp
  42. +0 −12 mythtv/libs/libmythtv/dvbchannel.h
  43. +46 −408 mythtv/libs/libmythtv/dvbrecorder.cpp
  44. +16 −121 mythtv/libs/libmythtv/dvbrecorder.h
  45. +2 −4 mythtv/libs/libmythtv/dvbsignalmonitor.cpp
  46. +2 −2 mythtv/libs/libmythtv/dvbsignalmonitor.h
  47. +85 −359 mythtv/libs/libmythtv/dvbstreamhandler.cpp
  48. +19 −75 mythtv/libs/libmythtv/dvbstreamhandler.h
  49. +12 −89 mythtv/libs/libmythtv/firewirechannel.cpp
  50. +9 −8 mythtv/libs/libmythtv/firewirechannel.h
  51. +27 −30 mythtv/libs/libmythtv/firewirerecorder.cpp
  52. +6 −8 mythtv/libs/libmythtv/firewirerecorder.h
  53. +35 −24 mythtv/libs/libmythtv/firewiresignalmonitor.cpp
  54. +9 −8 mythtv/libs/libmythtv/firewiresignalmonitor.h
  55. +10 −172 mythtv/libs/libmythtv/hdhrchannel.cpp
  56. +7 −14 mythtv/libs/libmythtv/hdhrchannel.h
  57. +34 −348 mythtv/libs/libmythtv/hdhrrecorder.cpp
  58. +6 −79 mythtv/libs/libmythtv/hdhrrecorder.h
  59. +1 −4 mythtv/libs/libmythtv/hdhrsignalmonitor.cpp
  60. +34 −330 mythtv/libs/libmythtv/hdhrstreamhandler.cpp
  61. +21 −60 mythtv/libs/libmythtv/hdhrstreamhandler.h
  62. +17 −17 mythtv/libs/libmythtv/importrecorder.cpp
  63. +0 −4 mythtv/libs/libmythtv/importrecorder.h
  64. +4 −7 mythtv/libs/libmythtv/iptvchannel.cpp
  65. +10 −6 mythtv/libs/libmythtv/iptvchannel.h
  66. +21 −42 mythtv/libs/libmythtv/iptvrecorder.cpp
  67. +4 −8 mythtv/libs/libmythtv/iptvrecorder.h
  68. +21 −21 mythtv/libs/libmythtv/iptvsignalmonitor.cpp
  69. +8 −7 mythtv/libs/libmythtv/iptvsignalmonitor.h
  70. +26 −4 mythtv/libs/libmythtv/libmythtv.pro
  71. +144 −382 mythtv/libs/libmythtv/mpegrecorder.cpp
  72. +8 −51 mythtv/libs/libmythtv/mpegrecorder.h
  73. +167 −23 mythtv/libs/libmythtv/recorderbase.cpp
  74. +21 −23 mythtv/libs/libmythtv/recorderbase.h
  75. +16 −6 mythtv/libs/libmythtv/recordinginfo.cpp
  76. +25 −0 mythtv/libs/libmythtv/recordingprofile.cpp
  77. +0 −35 mythtv/libs/libmythtv/ringbuffer.cpp
  78. +1 −0 mythtv/libs/libmythtv/scanwizard.cpp
  79. +30 −0 mythtv/libs/libmythtv/scriptsignalmonitor.h
  80. +51 −108 mythtv/libs/libmythtv/signalmonitor.cpp
  81. +14 −39 mythtv/libs/libmythtv/signalmonitor.h
  82. +12 −23 mythtv/libs/libmythtv/sourceutil.cpp
  83. +369 −0 mythtv/libs/libmythtv/streamhandler.cpp
  84. +123 −0 mythtv/libs/libmythtv/streamhandler.h
  85. +19 −0 mythtv/libs/libmythtv/tv.h
  86. +0 −19 mythtv/libs/libmythtv/tv_play.h
  87. +437 −536 mythtv/libs/libmythtv/tv_rec.cpp
  88. +16 −20 mythtv/libs/libmythtv/tv_rec.h
  89. +91 −263 mythtv/libs/libmythtv/v4lchannel.cpp
  90. +13 −21 mythtv/libs/libmythtv/v4lchannel.h
  91. +304 −0 mythtv/libs/libmythtv/v4lrecorder.cpp
  92. +83 −0 mythtv/libs/libmythtv/v4lrecorder.h
  93. +410 −0 mythtv/libs/libmythtv/vbi608extractor.cpp
  94. +57 −0 mythtv/libs/libmythtv/vbi608extractor.h
  95. +7 −1 mythtv/libs/libmythtv/vbitext/cc.h
  96. +8 −0 mythtv/libs/libmythtv/vbitext/dllist.h
  97. +8 −0 mythtv/libs/libmythtv/vbitext/hamm.h
  98. +8 −0 mythtv/libs/libmythtv/vbitext/lang.h
  99. +8 −0 mythtv/libs/libmythtv/vbitext/vbi.h
  100. +185 −15 mythtv/libs/libmythtv/videosource.cpp
  101. +38 −4 mythtv/libs/libmythtv/videosource.h
  102. +6 −1 mythtv/programs/mythbackend/main_helpers.h
  103. +2 −2 mythtv/programs/mythbackend/mainserver.h
  104. +739 −542 mythtv/programs/mythbackend/scheduler.cpp
  105. +20 −0 mythtv/programs/mythbackend/scheduler.h
  106. +2 −2 mythtv/programs/mythfrontend/playbackbox.cpp
  107. +41 −34 mythtv/programs/mythtranscode/mpeg2fix.cpp
  108. +10 −21 mythtv/programs/mythtranscode/mpeg2fix.h
  109. +26 −89 mythtv/programs/mythtv-setup/channeleditor.cpp
  110. +0 −3 mythtv/programs/mythtv-setup/channeleditor.h
  111. +6 −2 mythtv/programs/programs-libs.pro
View
11 mythtv/configure
@@ -115,6 +115,7 @@ Advanced options (experts only):
--dvb-path=HDRLOC location of directory containing
'linux/dvb/frontend.h', not the
directory with frontend.h [$dvb_path_default]
+ --disable-asi disable support for ASI recorder
--disable-x11 disable X11 support
--x11-path=X11LOC location of X11 include files [$x11_path_default]
--disable-xrandr disable X11 resolution switching
@@ -1344,6 +1345,7 @@ MYTHTV_CONFIG_LIST='
hdpvr
iptv
ivtv
+ asi
joystick_menu
libfftw3
libmpeg2external
@@ -1773,6 +1775,7 @@ v4l_deps="backend linux_videodev_h linux_videodev2_h"
vdpau_deps="opengl vdpau_vdpau_h vdpau_vdpau_x11_h"
xrandr_deps="x11"
xv_deps="x11"
+asi_deps="backend"
<<BLOCKQUOTE
# tests
@@ -1977,6 +1980,7 @@ enable hdhomerun
enable hdpvr
enable iptv
enable ivtv
+enable asi
enable lamemp3
enable libass
enable libxml2
@@ -3997,6 +4001,12 @@ int main(void) {
}
EOF
+enabled asi && check_cc <<EOF || disable asi
+#include <dveo/asi.h>
+#include <dveo/master.h>
+int main(void) { return 1; }
+EOF
+
# Check that all MythTV build "requirements" are met:
enabled lamemp3 && check_lib2 lame/lame.h lame_init -lmp3lame -lm ||
die "ERROR! You must have the Lame MP3 encoding library installed to compile MythTV."
@@ -4444,6 +4454,7 @@ if enabled backend; then
echo "DVB-S2 support ${fe_can_2g_modulation-no}"
echo "HDHomeRun support ${hdhomerun-no}"
echo "IPTV support ${iptv-no}"
+ echo "ASI support ${asi-no}"
fi
if enabled frontend; then
View
321 mythtv/libs/libmythtv/DeviceReadBuffer.cpp
@@ -4,9 +4,10 @@ using namespace std;
#include <QThread>
#include "DeviceReadBuffer.h"
#include "mythcorecontext.h"
+#include "mythbaseutil.h"
+#include "mythverbose.h"
#include "tspacket.h"
#include "compat.h"
-#include "mythverbose.h"
#ifndef USING_MINGW
#include <sys/poll.h>
@@ -18,17 +19,18 @@ using namespace std;
#define LOC QString("DevRdB(%1): ").arg(videodevice)
#define LOC_ERR QString("DevRdB(%1) Error: ").arg(videodevice)
-DeviceReadBuffer::DeviceReadBuffer(ReaderPausedCB *cb, bool use_poll)
+DeviceReadBuffer::DeviceReadBuffer(DeviceReaderCB *cb, bool use_poll)
: videodevice(QString::null), _stream_fd(-1),
- readerPausedCB(cb),
+ readerCB(cb),
// Data for managing the device ringbuffer
- run(false), running(false),
+ dorun(false), running(false),
eof(false), error(false),
request_pause(false), paused(false),
using_poll(use_poll), max_poll_wait(2500 /*ms*/),
size(0), used(0),
+ read_quanta(0),
dev_read_size(0), min_read(0),
buffer(NULL), readPtr(NULL),
@@ -38,6 +40,21 @@ DeviceReadBuffer::DeviceReadBuffer(ReaderPausedCB *cb, bool use_poll)
max_used(0), avg_used(0),
avg_cnt(0)
{
+ for (int i = 0; i < 2; i++)
+ {
+ wake_pipe[i] = -1;
+ wake_pipe_flags[i] = 0;
+ }
+
+#ifdef USING_MINGW
+#warning mingw DeviceReadBuffer::Poll
+ if (using_poll)
+ {
+ VERBOSE(VB_IMPORTANT, LOC_ERR +
+ "mingw DeviceReadBuffer::Poll is not implemented");
+ using_poll = false;
+ }
+#endif
}
DeviceReadBuffer::~DeviceReadBuffer()
@@ -46,7 +63,8 @@ DeviceReadBuffer::~DeviceReadBuffer()
delete[] buffer;
}
-bool DeviceReadBuffer::Setup(const QString &streamName, int streamfd)
+bool DeviceReadBuffer::Setup(const QString &streamName, int streamfd,
+ uint readQuanta, uint deviceBufferSize)
{
QMutexLocker locker(&lock);
@@ -56,7 +74,7 @@ bool DeviceReadBuffer::Setup(const QString &streamName, int streamfd)
videodevice = streamName;
_stream_fd = streamfd;
- // BEGIN HACK -- see #6897
+ // BEGIN HACK -- see #6897, remove after August 2009
max_poll_wait = (videodevice.contains("dvb")) ? 25000 : 2500;
// END HACK
@@ -66,21 +84,29 @@ bool DeviceReadBuffer::Setup(const QString &streamName, int streamfd)
request_pause = false;
paused = false;
- size = gCoreContext->GetNumSetting("HDRingbufferSize",
- 50 * TSPacket::kSize) * 1024;
+ read_quanta = (readQuanta) ? readQuanta : read_quanta;
+ size = gCoreContext->GetNumSetting(
+ "HDRingbufferSize", 50 * read_quanta) * 1024;
used = 0;
- dev_read_size = TSPacket::kSize * (using_poll ? 256 : 48);
- min_read = TSPacket::kSize * 4;
+ dev_read_size = read_quanta * (using_poll ? 256 : 48);
+ dev_read_size = (deviceBufferSize) ?
+ min(dev_read_size, (size_t)deviceBufferSize) : dev_read_size;
+ min_read = read_quanta * 4;
- buffer = new unsigned char[size + TSPacket::kSize];
+ buffer = new unsigned char[size + dev_read_size];
readPtr = buffer;
writePtr = buffer;
endPtr = buffer + size;
// Initialize buffer, if it exists
if (!buffer)
+ {
+ VERBOSE(VB_IMPORTANT, LOC +
+ QString("Failed to allocate buffer of size %1 = %2 + %3")
+ .arg(size+dev_read_size).arg(size).arg(dev_read_size));
return false;
- memset(buffer, 0xFF, size + TSPacket::kSize);
+ }
+ memset(buffer, 0xFF, size + read_quanta);
// Initialize statistics
max_used = 0;
@@ -95,32 +121,31 @@ bool DeviceReadBuffer::Setup(const QString &streamName, int streamfd)
void DeviceReadBuffer::Start(void)
{
- bool was_running;
+ VERBOSE(VB_RECORD, LOC + "Start() -- begin");
+ if (isRunning())
{
- QMutexLocker locker(&lock);
- was_running = running;
- error = false;
+ {
+ QMutexLocker locker(&lock);
+ dorun = false;
+ }
+ wait();
}
- if (was_running)
{
- VERBOSE(VB_IMPORTANT, LOC_ERR + "Start(): Already running.");
- SetRequestPause(false);
- return;
+ QMutexLocker locker(&lock);
+ error = false;
+ eof = false;
}
- thread.SetBuffer(this);
- thread.start();
+ start();
- if (!thread.isRunning())
- {
- VERBOSE(VB_IMPORTANT,
- LOC_ERR + QString("Start(): thread.run() failed.") + ENO);
+ VERBOSE(VB_RECORD, LOC + "Start() -- middle");
- QMutexLocker locker(&lock);
- error = true;
- }
+ while (!IsRunning())
+ usleep(5000);
+
+ VERBOSE(VB_RECORD, LOC + "Start() -- end");
}
void DeviceReadBuffer::Reset(const QString &streamName, int streamfd)
@@ -139,26 +164,24 @@ void DeviceReadBuffer::Reset(const QString &streamName, int streamfd)
void DeviceReadBuffer::Stop(void)
{
- bool was_running = IsRunning();
-
- if (!was_running)
- {
- VERBOSE(VB_IMPORTANT, LOC + "Stop(): Not running.");
- return;
- }
-
+ VERBOSE(VB_RECORD, LOC + "Stop() -- begin");
{
QMutexLocker locker(&lock);
- run = false;
+ dorun = false;
}
- thread.wait();
+ WakePoll();
+ VERBOSE(VB_RECORD, LOC + "Stop() -- middle");
+
+ wait();
+ VERBOSE(VB_RECORD, LOC + "Stop() -- end");
}
void DeviceReadBuffer::SetRequestPause(bool req)
{
QMutexLocker locker(&lock);
request_pause = req;
+ WakePoll();
}
void DeviceReadBuffer::SetPaused(bool val)
@@ -171,6 +194,37 @@ void DeviceReadBuffer::SetPaused(bool val)
unpauseWait.wakeAll();
}
+// The WakePoll code is copied from MythSocketThread::WakeReadyReadThread()
+void DeviceReadBuffer::WakePoll(void) const
+{
+ char buf[1];
+ buf[0] = '0';
+ ssize_t wret = 0;
+ while (running && (wret <= 0) && (wake_pipe[1] >= 0))
+ {
+ wret = ::write(wake_pipe[1], &buf, 1);
+ if ((wret < 0) && (EAGAIN != errno) && (EINTR != errno))
+ {
+ VERBOSE(VB_IMPORTANT, LOC_ERR + "WakePoll failed.");
+ ClosePipes();
+ break;
+ }
+ }
+}
+
+void DeviceReadBuffer::ClosePipes(void) const
+{
+ for (uint i = 0; i < 2; i++)
+ {
+ if (wake_pipe[i] >= 0)
+ {
+ ::close(wake_pipe[i]);
+ wake_pipe[i] = -1;
+ wake_pipe_flags[i] = 0;
+ }
+ }
+}
+
bool DeviceReadBuffer::IsPaused(void) const
{
QMutexLocker locker(&lock);
@@ -203,6 +257,18 @@ bool DeviceReadBuffer::IsPauseRequested(void) const
return request_pause;
}
+bool DeviceReadBuffer::IsErrored(void) const
+{
+ QMutexLocker locker(&lock);
+ return error;
+}
+
+bool DeviceReadBuffer::IsEOF(void) const
+{
+ QMutexLocker locker(&lock);
+ return eof;
+}
+
bool DeviceReadBuffer::IsRunning(void) const
{
QMutexLocker locker(&lock);
@@ -232,7 +298,7 @@ void DeviceReadBuffer::IncrWritePointer(uint len)
QMutexLocker locker(&lock);
used += len;
writePtr += len;
- writePtr = (writePtr == endPtr) ? buffer : writePtr;
+ writePtr = (writePtr >= endPtr) ? buffer + (writePtr - endPtr) : writePtr;
#if REPORT_RING_STATS
max_used = max(used, max_used);
avg_used = ((avg_used * avg_cnt) + used) / ++avg_cnt;
@@ -247,24 +313,19 @@ void DeviceReadBuffer::IncrReadPointer(uint len)
readPtr = (readPtr == endPtr) ? buffer : readPtr;
}
-void DRBThread::run(void)
-{
- if (!m_buffer)
- return;
-
- m_buffer->fill_ringbuffer();
-}
-
-void DeviceReadBuffer::fill_ringbuffer(void)
+void DeviceReadBuffer::run(void)
{
uint errcnt = 0;
lock.lock();
- run = true;
+ dorun = true;
running = true;
lock.unlock();
- while (run)
+ if (using_poll)
+ setup_pipe(wake_pipe, wake_pipe_flags);
+
+ while (dorun)
{
if (!HandlePausing())
continue;
@@ -288,27 +349,33 @@ void DeviceReadBuffer::fill_ringbuffer(void)
}
// Limit read size for faster return from read
- size_t read_size =
- min(dev_read_size, (size_t) WaitForUnused(TSPacket::kSize));
+ size_t unused = (size_t) WaitForUnused(read_quanta);
+ size_t read_size = min(dev_read_size, unused);
// if read_size > 0 do the read...
if (read_size)
{
ssize_t len = read(_stream_fd, writePtr, read_size);
- if (!CheckForErrors(len, errcnt))
+ if (!CheckForErrors(len, read_size, errcnt))
{
if (errcnt > 5)
break;
else
continue;
}
errcnt = 0;
+ // if we wrote past the official end of the buffer, copy to start
+ if (writePtr + len > endPtr)
+ memcpy(buffer, endPtr, writePtr + len - endPtr);
IncrWritePointer(len);
}
}
+ ClosePipes();
+
lock.lock();
running = false;
+ eof = true;
lock.unlock();
}
@@ -318,8 +385,8 @@ bool DeviceReadBuffer::HandlePausing(void)
{
SetPaused(true);
- if (readerPausedCB)
- readerPausedCB->ReaderPaused(_stream_fd);
+ if (readerCB)
+ readerCB->ReaderPaused(_stream_fd);
usleep(5000);
return false;
@@ -348,57 +415,112 @@ bool DeviceReadBuffer::Poll(void) const
MythTimer timer;
timer.start();
+ int poll_cnt = 1;
+ struct pollfd polls[2];
+ memset(polls, 0, sizeof(polls));
+
+ polls[0].fd = _stream_fd;
+ polls[0].events = POLLIN | POLLPRI;
+ polls[0].revents = 0;
+
+ if (wake_pipe[0] >= 0)
+ {
+ poll_cnt = 2;
+ polls[1].fd = wake_pipe[0];
+ polls[1].events = POLLIN;
+ polls[1].revents = 0;
+ }
+
while (true)
{
- struct pollfd polls;
- polls.fd = _stream_fd;
- polls.events = POLLIN;
- polls.revents = 0;
+ polls[0].revents = 0;
+ polls[1].revents = 0;
+ poll_cnt = (wake_pipe[0] >= 0) ? poll_cnt : 1;
- int ret = poll(&polls, 1 /*number of polls*/, 10 /*msec*/);
+ int timeout = max((int)max_poll_wait - timer.elapsed(), 10);
+ timeout = (1 == poll_cnt) ? 10 : timeout;
+ int ret = poll(polls, poll_cnt, timeout);
- if (polls.revents & (POLLHUP | POLLNVAL))
+ if (polls[0].revents & (POLLHUP | POLLNVAL))
{
VERBOSE(VB_IMPORTANT, LOC + "poll error");
error = true;
return true;
}
- if (!run || !IsOpen() || IsPauseRequested())
+ if (!dorun || !IsOpen() || IsPauseRequested())
{
retval = false;
break; // are we supposed to pause, stop, etc.
}
- if (ret > 0)
- break; // we have data to read :)
- else if (ret < 0)
+ if (polls[0].revents & POLLPRI)
{
- if ((EOVERFLOW == errno))
- break; // we have an error to handle
-
- if ((EAGAIN == errno) || (EINTR == errno))
- continue; // errors that tell you to try again
-
- usleep(2500 /*2.5 ms*/);
+ readerCB->PriorityEvent(polls[0].fd);
}
- else // ret == 0
+
+ if (polls[0].revents & POLLIN)
{
- if ((uint)timer.elapsed() > max_poll_wait)
+ if (ret > 0)
+ break; // we have data to read :)
+ else if (ret < 0)
+ {
+ if ((EOVERFLOW == errno))
+ break; // we have an error to handle
+
+ if ((EAGAIN == errno) || (EINTR == errno))
+ continue; // errors that tell you to try again
+
+ usleep(2500 /*2.5 ms*/);
+ }
+ else // ret == 0
{
- VERBOSE(VB_IMPORTANT, LOC_ERR + "Poll giving up");
- QMutexLocker locker(&lock);
- error = true;
- return true;
+ if ((uint)timer.elapsed() >= max_poll_wait)
+ {
+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Poll giving up 1");
+ QMutexLocker locker(&lock);
+ error = true;
+ return true;
+ }
}
}
+
+ // Clear out any pending pipe reads
+ if ((poll_cnt > 1) && (polls[1].revents & POLLIN))
+ {
+ char dummy[128];
+ int cnt = (wake_pipe_flags[0] & O_NONBLOCK) ? 128 : 1;
+ (void) ::read(wake_pipe[0], dummy, cnt);
+ }
+
+ if ((uint)timer.elapsed() >= max_poll_wait)
+ {
+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Poll giving up 2");
+ QMutexLocker locker(&lock);
+ error = true;
+ return true;
+ }
}
return retval;
#endif //!USING_MINGW
}
-bool DeviceReadBuffer::CheckForErrors(ssize_t len, uint &errcnt)
+bool DeviceReadBuffer::CheckForErrors(
+ ssize_t len, size_t requested_len, uint &errcnt)
{
+ if (len > (ssize_t)requested_len)
+ {
+ VERBOSE(VB_IMPORTANT, LOC_ERR +
+ "Driver is retruning bogus values on read");
+ if (++errcnt > 5)
+ {
+ VERBOSE(VB_RECORD, LOC + "Too many errors.");
+ QMutexLocker locker(&lock);
+ error = true;
+ }
+ return false;
+ }
+
#ifdef USING_MINGW
# ifdef _MSC_VER
# pragma message( "mingw DeviceReadBuffer::CheckForErrors" )
@@ -466,7 +588,7 @@ bool DeviceReadBuffer::CheckForErrors(ssize_t len, uint &errcnt)
*/
uint DeviceReadBuffer::Read(unsigned char *buf, const uint count)
{
- uint avail = WaitForUsed(min(count, (uint)min_read));
+ uint avail = WaitForUsed(min(count, (uint)min_read), 500);
size_t cnt = min(count, avail);
if (!cnt)
@@ -509,41 +631,44 @@ uint DeviceReadBuffer::Read(unsigned char *buf, const uint count)
uint DeviceReadBuffer::WaitForUnused(uint needed) const
{
size_t unused = GetUnused();
- size_t contig = GetContiguousUnused();
- if (contig > TSPacket::kSize)
+ if (unused > read_quanta)
{
while (unused < needed)
{
unused = GetUnused();
- if (IsPauseRequested() || !IsOpen() || !run)
+ if (IsPauseRequested() || !IsOpen() || !dorun)
return 0;
usleep(5000);
}
- if (IsPauseRequested() || !IsOpen() || !run)
+ if (IsPauseRequested() || !IsOpen() || !dorun)
return 0;
- contig = GetContiguousUnused();
+ unused = GetUnused();
}
- return min(contig, unused);
+ return unused;
}
-/** \fn DeviceReadBuffer::WaitForUsed(uint) const
+/** \fn DeviceReadBuffer::WaitForUsed(uint,uint) const
* \param needed Number of bytes we want to read
+ * \param max_wait Number of milliseconds to wait for the needed data
* \return bytes available for reading
*/
-uint DeviceReadBuffer::WaitForUsed(uint needed) const
+uint DeviceReadBuffer::WaitForUsed(uint needed, uint max_wait) const
{
- size_t avail = GetUsed();
- while ((needed > avail) && running)
+ QWaitCondition dataWait;
+
+ MythTimer timer;
+ timer.start();
+
+ QMutexLocker locker(&lock);
+ size_t avail = used;
+ while ((needed > avail) && running &&
+ !request_pause && !error && !eof &&
+ (timer.elapsed() < (int)max_wait))
{
- {
- QMutexLocker locker(&lock);
- avail = used;
- if (request_pause || error || eof)
- return 0;
- }
- usleep(5000);
+ dataWait.wait(locker.mutex(), 10);
+ avail = used;
}
return avail;
}
View
52 mythtv/libs/libmythtv/DeviceReadBuffer.h
@@ -11,27 +11,16 @@
#include <QString>
#include <QThread>
+#include "tspacket.h"
#include "util.h"
-class ReaderPausedCB
+class DeviceReaderCB
{
protected:
- virtual ~ReaderPausedCB() {}
+ virtual ~DeviceReaderCB() {}
public:
virtual void ReaderPaused(int fd) = 0;
-};
-
-class DeviceReadBuffer;
-
-class DRBThread : public QThread
-{
- Q_OBJECT
- public:
- DRBThread(void) : m_buffer(NULL) {};
- void SetBuffer( DeviceReadBuffer *buffer ) { m_buffer = buffer; };
- void run(void);
- private:
- DeviceReadBuffer *m_buffer;
+ virtual void PriorityEvent(int fd) = 0;
};
/** \class DeviceReadBuffer
@@ -41,15 +30,17 @@ class DRBThread : public QThread
* of long blocking conditions on writing to disk or accessing the
* database.
*/
-class DeviceReadBuffer
+class DeviceReadBuffer : protected QThread
{
- friend class DRBThread;
-
public:
- DeviceReadBuffer(ReaderPausedCB *callback, bool use_poll = true);
+ DeviceReadBuffer(DeviceReaderCB *callback,
+ bool use_poll = true);
~DeviceReadBuffer();
- bool Setup(const QString &streamName, int streamfd);
+ bool Setup(const QString &streamName,
+ int streamfd,
+ uint readQuanta = sizeof(TSPacket),
+ uint deviceBufferSize = 0);
void Start(void);
void Reset(const QString &streamName, int streamfd);
@@ -60,41 +51,45 @@ class DeviceReadBuffer
bool WaitForUnpause(unsigned long timeout);
bool WaitForPaused(unsigned long timeout);
- bool IsErrored(void) const { return error; }
- bool IsEOF(void) const { return eof; }
+ bool IsErrored(void) const;
+ bool IsEOF(void) const;
bool IsRunning(void) const;
uint Read(unsigned char *buf, uint count);
private:
- void fill_ringbuffer(void);
+ virtual void run(void); // QThread
void SetPaused(bool);
void IncrWritePointer(uint len);
void IncrReadPointer(uint len);
bool HandlePausing(void);
bool Poll(void) const;
+ void WakePoll(void) const;
uint WaitForUnused(uint bytes_needed) const;
- uint WaitForUsed (uint bytes_needed) const;
+ uint WaitForUsed (uint bytes_needed, uint max_wait /*ms*/) const;
bool IsPauseRequested(void) const;
bool IsOpen(void) const { return _stream_fd >= 0; }
+ void ClosePipes(void) const;
uint GetUnused(void) const;
uint GetUsed(void) const;
uint GetContiguousUnused(void) const;
- bool CheckForErrors(ssize_t read_len, uint &err_cnt);
+ bool CheckForErrors(ssize_t read_len, size_t requested_len, uint &err_cnt);
void ReportStats(void);
QString videodevice;
int _stream_fd;
+ mutable int wake_pipe[2];
+ mutable long wake_pipe_flags[2];
- ReaderPausedCB *readerPausedCB;
+ DeviceReaderCB *readerCB;
// Data for managing the device ringbuffer
mutable QMutex lock;
- bool run;
+ bool dorun;
bool running;
bool eof;
mutable bool error;
@@ -105,6 +100,7 @@ class DeviceReadBuffer
size_t size;
size_t used;
+ size_t read_quanta;
size_t dev_read_size;
size_t min_read;
unsigned char *buffer;
@@ -120,8 +116,6 @@ class DeviceReadBuffer
size_t avg_used;
size_t avg_cnt;
MythTimer lastReport;
-
- DRBThread thread;
};
#endif // _DEVICEREADBUFFER_H_
View
256 mythtv/libs/libmythtv/NuppelVideoRecorder.cpp
@@ -24,13 +24,14 @@ using namespace std;
#include "mythcontext.h"
#include "mythverbose.h"
#include "NuppelVideoRecorder.h"
-#include "vbitext/cc.h"
#include "channelbase.h"
#include "filtermanager.h"
#include "recordingprofile.h"
#include "tv_rec.h"
#include "tv_play.h"
#include "audioinput.h"
+#include "vbitext/cc.h"
+#include "vbitext/vbi.h"
#if HAVE_BIGENDIAN
extern "C" {
@@ -52,9 +53,6 @@ extern "C" {
#include "videodev_mjpeg.h"
#endif
-extern "C" {
-#include "vbitext/vbi.h"
-}
#else // USING_V4l
#define VT_WIDTH 0
#endif // USING_V4l
@@ -79,15 +77,9 @@ void NVRAudioThread::run(void)
m_parent->doAudioThread();
}
-void NVRVBIThread::run(void)
-{
- m_parent->doVbiThread();
-}
-
NuppelVideoRecorder::NuppelVideoRecorder(TVRec *rec, ChannelBase *channel) :
- RecorderBase(rec), audio_device(NULL),
- write_thread(NULL), audio_thread(NULL),
- vbi_thread(NULL)
+ V4LRecorder(rec), audio_device(NULL),
+ write_thread(NULL), audio_thread(NULL)
{
channelObj = channel;
@@ -353,7 +345,7 @@ void NuppelVideoRecorder::SetOption(const QString &opt, int value)
else if (opt == "volume")
volume = value;
else
- RecorderBase::SetOption(opt, value);
+ V4LRecorder::SetOption(opt, value);
}
void NuppelVideoRecorder::SetOptionsFromProfile(RecordingProfile *profile,
@@ -1000,6 +992,7 @@ void NuppelVideoRecorder::ResizeVideoBuffers(void)
void NuppelVideoRecorder::StopRecording(void)
{
encoding = false;
+ V4LRecorder::StopRecording();
}
void NuppelVideoRecorder::StreamAllocate(void)
@@ -1905,11 +1898,8 @@ bool NuppelVideoRecorder::SpawnChildren(void)
audio_thread = new NVRAudioThread(this);
audio_thread->start();
- if (vbimode)
- {
- vbi_thread = new NVRVBIThread(this);
- vbi_thread->start();
- }
+ if ((vbimode != VBIMode::None) && (OpenVBIDevice() >= 0))
+ vbi_thread = new VBIThread(this);
return true;
}
@@ -2471,14 +2461,7 @@ void NuppelVideoRecorder::doAudioThread(void)
}
#ifdef USING_V4L
-struct VBIData
-{
- NuppelVideoRecorder *nvr;
- vt_page teletextpage;
- bool foundteletextpage;
-};
-
-void NuppelVideoRecorder::FormatTeletextSubtitles(struct VBIData *vbidata)
+void NuppelVideoRecorder::FormatTT(struct VBIData *vbidata)
{
struct timeval tnow;
gettimeofday(&tnow, &tzone);
@@ -2683,198 +2666,6 @@ void NuppelVideoRecorder::AddTextData(unsigned char *buf, int len,
textbuffer[act]->freeToEncode = 1;
}
-#ifdef USING_V4L
-static void vbi_event(struct VBIData *data, struct vt_event *ev)
-{
- switch (ev->type)
- {
- case EV_PAGE:
- {
- struct vt_page *vtp = (struct vt_page *) ev->p1;
- if (vtp->flags & PG_SUBTITLE)
- {
- //printf("subtitle page %x.%x\n", vtp->pgno, vtp->subno);
- data->foundteletextpage = true;
- memcpy(&(data->teletextpage), vtp, sizeof(vt_page));
- }
- }
-
- case EV_HEADER:
- case EV_XPACKET:
- break;
- }
-}
-
-/*
-These are the default values for various VBI drivers
-// bttv
-vbi_format rate: 28636363
-samples_per_line: 2048
- starts: 10, 273
- counts: 16, 16
- flags: 0x0
-// cx88
-vbi_format rate: 28636363
-samples_per_line: 2048
- starts: 9, 272
- counts: 17, 17
- flags: 0x0
-*/
-
-void NuppelVideoRecorder::doVbiThread(void)
-{
- //VERBOSE(VB_IMPORTANT, LOC + "vbi begin");
- struct VBIData vbicallbackdata;
- struct vbi *pal_tt = NULL;
- struct cc *ntsc_cc = NULL;
- int vbifd = -1;
- char *ptr = NULL;
- char *ptr_end = NULL;
-
- QByteArray vbidev = vbidevice.toAscii();
- if (VBIMode::PAL_TT == vbimode)
- {
- pal_tt = vbi_open(vbidev.constData(), NULL, 99, -1);
- if (pal_tt)
- {
- vbifd = pal_tt->fd;
- vbicallbackdata.nvr = this;
- vbi_add_handler(pal_tt, (void*) vbi_event, &vbicallbackdata);
- }
- }
- else if (VBIMode::NTSC_CC == vbimode)
- {
- ntsc_cc = new struct cc;
- memset(ntsc_cc, 0, sizeof(struct cc));
- ntsc_cc->fd = open(vbidev.constData(), O_RDONLY|O_NONBLOCK);
- ntsc_cc->code1 = -1;
- ntsc_cc->code2 = -1;
-
- vbifd = ntsc_cc->fd;
- ptr = ntsc_cc->buffer;
-
- if (vbifd < 0)
- delete ntsc_cc;
- }
- else
- {
- VERBOSE(VB_IMPORTANT, LOC_ERR + "Invalid CC/Teletext mode");
- return;
- }
-
- if (vbifd < 0)
- {
- VERBOSE(VB_IMPORTANT, LOC_ERR +
- QString("Can't open vbi device: '%1'").arg(vbidevice));
- return;
- }
-
- if (VBIMode::NTSC_CC == vbimode)
- {
- // V4L v1 VBI ioctls
- struct vbi_format vfmt;
- memset(&vfmt, 0, sizeof(vbi_format));
- if (ioctl(vbifd, VIDIOCGVBIFMT, &vfmt) < 0)
- {
- VERBOSE(VB_IMPORTANT, LOC_ERR +
- "Failed to query vbi capabilities (v4l1)");
- cc_close(ntsc_cc);
- return;
- }
- VERBOSE(VB_RECORD, LOC + "vbi_format rate: "<<vfmt.sampling_rate
- <<"\n\t\t\tsamples_per_line: "<<vfmt.samples_per_line
- <<"\n\t\t\t starts: "
- <<vfmt.start[0]<<", "<<vfmt.start[1]
- <<"\n\t\t\t counts: "
- <<vfmt.count[0]<<", "<<vfmt.count[1]
- <<"\n\t\t\t flags: "
- <<QString("0x%1").arg(vfmt.flags));
- uint sz = vfmt.samples_per_line * (vfmt.count[0] + vfmt.count[1]);
- ntsc_cc->samples_per_line = vfmt.samples_per_line;
- ntsc_cc->start_line = vfmt.start[0];
- ntsc_cc->line_count = vfmt.count[0];
- ntsc_cc->scale0 = (vfmt.sampling_rate + 503488 / 2) / 503488;
- ntsc_cc->scale1 = (ntsc_cc->scale0 * 2 + 3) / 5; /* 40% */
- ptr_end = ntsc_cc->buffer + sz;
- if (sz > CC_VBIBUFSIZE)
- {
- VERBOSE(VB_IMPORTANT, LOC_ERR +
- "VBI format has too many samples per frame");
- cc_close(ntsc_cc);
- return;
- }
- }
-
- while (childrenLive)
- {
- {
- QMutexLocker locker(&pauseLock);
- if (request_pause)
- {
- unpauseWait.wait(&pauseLock, 100);
- continue;
- }
- }
-
- struct timeval tv;
- fd_set rdset;
-
- tv.tv_sec = 0;
- tv.tv_usec = 5000;
- FD_ZERO(&rdset);
- FD_SET(vbifd, &rdset);
-
- int nr = select(vbifd + 1, &rdset, 0, 0, &tv);
- if (nr < 0)
- VERBOSE(VB_IMPORTANT, LOC_ERR + "vbi select failed" + ENO);
-
- if (nr <= 0)
- continue; // either failed or timed out..
-
- if (VBIMode::PAL_TT == vbimode)
- {
- vbicallbackdata.foundteletextpage = false;
- vbi_handler(pal_tt, pal_tt->fd);
- if (vbicallbackdata.foundteletextpage)
- {
- // decode VBI as teletext subtitles
- FormatTeletextSubtitles(&vbicallbackdata);
- }
- }
- else if (VBIMode::NTSC_CC == vbimode)
- {
- int ret = read(vbifd, ptr, ptr_end - ptr);
- ptr = (ret > 0) ? ptr + ret : ptr;
- if ((ptr_end - ptr) == 0)
- {
- cc_decode(ntsc_cc);
- FormatCC(ntsc_cc);
- ptr = ntsc_cc->buffer;
- }
- else if (ret < 0)
- {
- VERBOSE(VB_IMPORTANT, LOC_ERR + "Reading VBI data" + ENO);
- }
- }
- }
- //VERBOSE(VB_RECORD, LOC + "vbi shutdown");
-
- if (pal_tt)
- {
- vbi_del_handler(pal_tt, (void*) vbi_event, &vbicallbackdata);
- vbi_close(pal_tt);
- }
-
- if (ntsc_cc)
- cc_close(ntsc_cc);
-
- //VERBOSE(VB_RECORD, LOC + "vbi end");
-}
-
-#else // USING_V4L
-void NuppelVideoRecorder::doVbiThread(void) { }
-#endif // USING_V4L
-
void NuppelVideoRecorder::doWriteThread(void)
{
while (childrenLive && !IsErrored())
@@ -3171,16 +2962,6 @@ void NuppelVideoRecorder::WriteVideo(VideoFrame *frame, bool skipsync,
if (freecount < 5)
raw = 1; // speed up the encode process
- if (raw == 1 || compressthis == 0)
- {
- if (ringBuffer->IsIOBound())
- {
- /* need to compress, the disk can't handle any more bandwidth*/
- raw=0;
- compressthis=1;
- }
- }
-
if (transcoding)
{
raw = 0;
@@ -3427,18 +3208,25 @@ void NuppelVideoRecorder::WriteText(unsigned char *buf, int len, int timecode,
frameheader.frametype = 'T'; // text frame
frameheader.timecode = timecode;
- if (vbimode == 1)
+ if (VBIMode::PAL_TT == vbimode)
{
frameheader.comptype = 'T'; // european teletext
- frameheader.packetlength = sizeof(int) + len;
-
+ frameheader.packetlength = len + 4;
WriteFrameheader(&frameheader);
- ringBuffer->Write(&pagenr, sizeof(int));
+ union page_t {
+ int32_t val32;
+ struct { int8_t a,b,c,d; } val8;
+ } v;
+ v.val32 = pagenr;
+ ringBuffer->Write(&v.val8.d, sizeof(int8_t));
+ ringBuffer->Write(&v.val8.c, sizeof(int8_t));
+ ringBuffer->Write(&v.val8.b, sizeof(int8_t));
+ ringBuffer->Write(&v.val8.a, sizeof(int8_t));
ringBuffer->Write(buf, len);
}
- else if (vbimode == 2)
+ else if (VBIMode::NTSC_CC == vbimode)
{
- frameheader.comptype = 'C'; // NTSC CC
+ frameheader.comptype = 'C'; // NTSC CC
frameheader.packetlength = len;
WriteFrameheader(&frameheader);
View
28 mythtv/libs/libmythtv/NuppelVideoRecorder.h
@@ -30,7 +30,7 @@ using namespace std;
#include <QThread>
// MythTV headers
-#include "recorderbase.h"
+#include "v4lrecorder.h"
#include "format.h"
#include "cc608decoder.h"
#include "filter.h"
@@ -39,8 +39,6 @@ using namespace std;
#include "mythtvexp.h"
struct video_audio;
-struct VBIData;
-struct cc;
class RTjpeg;
class RingBuffer;
class ChannelBase;
@@ -71,22 +69,10 @@ class NVRAudioThread : public QThread
NuppelVideoRecorder *m_parent;
};
-class NVRVBIThread : public QThread
-{
- Q_OBJECT
- public:
- NVRVBIThread(NuppelVideoRecorder *parent) : m_parent(parent) {}
- virtual ~NVRVBIThread() { wait(); m_parent = NULL; }
- virtual void run(void);
- private:
- NuppelVideoRecorder *m_parent;
-};
-
-class MTV_PUBLIC NuppelVideoRecorder : public RecorderBase, public CC608Input
+class MTV_PUBLIC NuppelVideoRecorder : public V4LRecorder, public CC608Input
{
friend class NVRWriteThread;
friend class NVRAudioThread;
- friend class NVRVBIThread;
public:
NuppelVideoRecorder(TVRec *rec, ChannelBase *channel);
~NuppelVideoRecorder();
@@ -146,7 +132,6 @@ class MTV_PUBLIC NuppelVideoRecorder : public RecorderBase, public CC608Input
protected:
void doWriteThread(void);
void doAudioThread(void);
- void doVbiThread(void);
private:
inline void WriteFrameheader(rtframeheader *fh);
@@ -172,10 +157,10 @@ class MTV_PUBLIC NuppelVideoRecorder : public RecorderBase, public CC608Input
void DoV4L2(void);
void DoMJPEG(void);
- void FormatTeletextSubtitles(struct VBIData *vbidata);
- void FormatCC(struct cc *cc);
- void AddTextData(unsigned char *buf, int len,
- int64_t timecode, char type);
+ virtual void FormatTT(struct VBIData*); // RecorderBase
+ virtual void FormatCC(struct cc*); // RecorderBase
+ virtual void AddTextData(unsigned char*,int,int64_t,char); // CC608Decoder
+
void UpdateResolutions(void);
bool encoding;
@@ -245,7 +230,6 @@ class MTV_PUBLIC NuppelVideoRecorder : public RecorderBase, public CC608Input
NVRWriteThread *write_thread;
NVRAudioThread *audio_thread;
- NVRVBIThread *vbi_thread;
bool recording;
bool errored;
View
531 mythtv/libs/libmythtv/ThreadedFileWriter.cpp
@@ -17,15 +17,28 @@
// MythTV headers
#include "ThreadedFileWriter.h"
-#include "compat.h"
#include "mythverbose.h"
+#include "mythtimer.h"
+#include "compat.h"
-#define LOC QString("TFW(%1): ").arg(fd)
-#define LOC_ERR QString("TFW(%1), Error: ").arg(fd)
+/// \brief Runs ThreadedFileWriter::DiskLoop(void)
+void TFWWriteThread::run(void)
+{
+#ifndef USING_MINGW
+ // don't exit program if file gets larger than quota limit..
+ signal(SIGXFSZ, SIG_IGN);
+#endif
+ m_parent->DiskLoop();
+}
-const uint ThreadedFileWriter::TFW_DEF_BUF_SIZE = 2*1024*1024;
-const uint ThreadedFileWriter::TFW_MAX_WRITE_SIZE = TFW_DEF_BUF_SIZE / 4;
-const uint ThreadedFileWriter::TFW_MIN_WRITE_SIZE = TFW_DEF_BUF_SIZE / 32;
+/// \brief Runs ThreadedFileWriter::SyncLoop(void)
+void TFWSyncThread::run(void)
+{
+ m_parent->SyncLoop();
+}
+
+const uint ThreadedFileWriter::kMaxBufferSize = 128 * 1024 * 1024;
+const uint ThreadedFileWriter::kMinWriteSize = 64 * 1024;
/** \class ThreadedFileWriter
* \brief This class supports the writing of recordings to disk.
@@ -38,82 +51,10 @@ const uint ThreadedFileWriter::TFW_MIN_WRITE_SIZE = TFW_DEF_BUF_SIZE / 32;
* to the stream.
*/
-/** \fn safe_write(int, const void*, uint, bool &ok)
- * \brief Writes data to disk
- *
- * This just uses the Standard C write() to write to disk.
- * We retry forever on EAGAIN errors, and three times on
- * any other error.
- *
- * \param fd File descriptor
- * \param data Pointer to data to write
- * \param sz Size of data to write in bytes
- */
-static uint safe_write(int fd, const void *data, uint sz, bool &ok)
-{
- int ret;
- uint tot = 0;
- uint errcnt = 0;
-
- while (tot < sz)
- {
- ret = write(fd, (char *)data + tot, sz - tot);
- if (ret < 0)
- {
- if (errno == EAGAIN)
- {
- VERBOSE(VB_IMPORTANT, LOC + "safe_write(): Got EAGAIN.");
- continue;
- }
- if (errno == ENOSPC)
- {
- VERBOSE(VB_IMPORTANT, LOC + "safe_write(): Got ENOSPC (No space left on device).");
- errcnt = 10;
- tot = 0;
- break;
- }
- errcnt++;
- VERBOSE(VB_IMPORTANT, LOC_ERR + "safe_write(): File I/O " +
- QString(" errcnt: %1").arg(errcnt) + ENO);
-
- if (errcnt == 3)
- break;
- }
- else
- {
- tot += ret;
- }
-
- if (tot < sz)
- {
- VERBOSE(VB_IMPORTANT, LOC + "safe_write(): funky usleep");
- usleep(1000);
- }
- }
- ok = (errcnt < 3);
- return tot;
-}
-
-#undef LOC
-#undef LOC_ERR
#define LOC QString("TFW(%1:%2): ").arg(filename).arg(fd)
+#define LOC_WARN QString("TFW(%1:%2), Warning: ").arg(filename).arg(fd)
#define LOC_ERR QString("TFW(%1:%2), Error: ").arg(filename).arg(fd)
-/// \brief Runs ThreadedFileWriter::DiskLoop(void)
-void TFWWriteThread::run(void)
-{
-#ifndef USING_MINGW
- signal(SIGXFSZ, SIG_IGN);
-#endif
- m_parent->DiskLoop();
-}
-
-/// \brief Runs ThreadedFileWriter::SyncLoop(void)
-void TFWSyncThread::run(void)
-{
- m_parent->SyncLoop();
-}
-
/** \fn ThreadedFileWriter::ThreadedFileWriter(const QString&,int,mode_t)
* \brief Creates a threaded file writer.
*/
@@ -122,16 +63,10 @@ ThreadedFileWriter::ThreadedFileWriter(const QString &fname,
// file stuff
filename(fname), flags(pflags),
mode(pmode), fd(-1),
- m_file_sync(0), m_file_wpos(0),
// state
- no_writes(false), flush(false),
- write_is_blocked(false), in_dtor(false),
- ignore_writes(false), tfw_min_write_size(0),
- // buffer position state
- rpos(0), wpos(0),
- written(0),
- // buffer
- buf(NULL), tfw_buf_size(0),
+ flush(false), in_dtor(false),
+ ignore_writes(false), tfw_min_write_size(kMinWriteSize),
+ totalBufferUse(0),
// threads
writeThread(NULL), syncThread(NULL)
{
@@ -162,17 +97,11 @@ bool ThreadedFileWriter::Open(void)
}
else
{
+ VERBOSE(VB_FILE, LOC + "Open() successful");
+
#ifdef USING_MINGW
_setmode(fd, _O_BINARY);
#endif
- buf = new char[TFW_DEF_BUF_SIZE + 1024];
- memset(buf, 0, TFW_DEF_BUF_SIZE + 64);
-
- m_file_sync = m_file_wpos = 0;
-
- tfw_buf_size = TFW_DEF_BUF_SIZE;
- tfw_min_write_size = TFW_MIN_WRITE_SIZE;
-
writeThread = new TFWWriteThread(this);
writeThread->start();
syncThread = new TFWSyncThread(this);
@@ -187,49 +116,51 @@ bool ThreadedFileWriter::Open(void)
*/
ThreadedFileWriter::~ThreadedFileWriter()
{
- no_writes = true;
-
- if (fd >= 0)
- {
- Flush();
- in_dtor = true; /* tells child threads to exit */
+ Flush();
- buflock.lock();
+ { /* tell child threads to exit */
+ QMutexLocker locker(&buflock);
+ in_dtor = true;
bufferSyncWait.wakeAll();
bufferHasData.wakeAll();
- buflock.unlock();
+ }
- syncThread->wait();
+ if (writeThread)
+ {
writeThread->wait();
+ delete writeThread;
+ writeThread = NULL;
+ }
- close(fd);
- fd = -1;
+ while (!writeBuffers.empty())
+ {
+ delete writeBuffers.front();
+ writeBuffers.pop_front();
}
- if (buf)
+ while (!emptyBuffers.empty())
{
- delete [] buf;
- buf = NULL;
+ delete emptyBuffers.front();
+ emptyBuffers.pop_front();
}
if (syncThread)
{
+ syncThread->wait();
delete syncThread;
syncThread = NULL;
}
- if (writeThread)
+ if (fd >= 0)
{
- delete writeThread;
- writeThread = NULL;
+ close(fd);
+ fd = -1;
}
}
/** \fn ThreadedFileWriter::Write(const void*, uint)
* \brief Writes data to the end of the write buffer
*
- * NOTE: This blocks while buffer is in use by the write to disk thread.
- *
* \param data pointer to data to write to disk
* \param count size of data in bytes
*/
@@ -238,83 +169,57 @@ uint ThreadedFileWriter::Write(const void *data, uint count)
if (count == 0)
return 0;
- if (count > tfw_buf_size)
- {
- VERBOSE(VB_IMPORTANT, LOC +
- QString("WARNING: count(%1), tfw_buf_size(%2)")
- .arg(count).arg(tfw_buf_size));
- }
+ QMutexLocker locker(&buflock);
- uint iobound_cnt = 0;
- uint remaining = count;
- char *wdata = (char *)data;
+ if (ignore_writes)
+ return count;
- while (remaining)
+ if (totalBufferUse + count > kMaxBufferSize)
{
- bool first = true;
-
- buflock.lock();
- while (BufFreePriv() == 0)
- {
- if (first)
- {
- ++iobound_cnt;
- VERBOSE(VB_IMPORTANT, LOC_ERR + "Write() -- IOBOUND begin " +
- QString("remaining(%1) free(%2) size(%3) cnt(%4)")
- .arg(remaining).arg(BufFreePriv())
- .arg(tfw_buf_size).arg(iobound_cnt));
- first = false;
- }
-
- bufferWroteData.wait(&buflock, 1000);
- }
- uint twpos = wpos;
- uint bytes = (BufFreePriv() < remaining) ? BufFreePriv() : remaining;
- buflock.unlock();
-
- if (!first)
- VERBOSE(VB_IMPORTANT, LOC_ERR + "Write() -- IOBOUND end");
+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Maximum buffer size exceeded."
+ "\n\t\t\tfile will be truncated, no further writing "
+ "will be done."
+ "\n\t\t\tThis generally indicates your disk performance "
+ "\n\t\t\tis insufficient to deal with the number of on-going "
+ "\n\t\t\trecordings, or you have a disk failure.");
+ ignore_writes = true;
+ return count;
+ }
- if (no_writes)
- return 0;
+ TFWBuffer *buf = NULL;
- if ((twpos + bytes) > tfw_buf_size)
+ if (!writeBuffers.empty() &&
+ (writeBuffers.back()->data.size() + count) < kMinWriteSize)
+ {
+ buf = writeBuffers.back();
+ writeBuffers.pop_back();
+ }
+ else
+ {
+ if (!emptyBuffers.empty())
{
- int first_chunk_size = tfw_buf_size - twpos;
- int second_chunk_size = bytes - first_chunk_size;
- memcpy(buf + twpos, wdata, first_chunk_size);
- memcpy(buf, wdata + first_chunk_size,
- second_chunk_size);
+ buf = emptyBuffers.front();
+ emptyBuffers.pop_front();
+ buf->data.clear();
}
else
{
- memcpy(buf + twpos, wdata, bytes);
+ buf = new TFWBuffer();
}
+ }
- buflock.lock();
- if (twpos == wpos)
- {
- wpos = (wpos + bytes) % tfw_buf_size;
- }
- else
- {
- VERBOSE(VB_IMPORTANT, LOC_ERR + "Programmer Error detected! "
- "wpos was changed from under the Write() function.");
- }
- bufferHasData.wakeAll();
- buflock.unlock();
+ totalBufferUse += count;
+ const char *cdata = (const char*) data;
+ buf->data.insert(buf->data.end(), cdata, cdata+count);
+ buf->lastUsed = QDateTime::currentDateTime();
- remaining -= bytes;
- wdata += bytes;
+ writeBuffers.push_back(buf);
- if (remaining)
- {
- buflock.lock();
- if (0 == BufFreePriv())
- bufferWroteData.wait(&buflock, 10000);
- buflock.unlock();
- }
- }
+ bufferHasData.wakeAll();
+
+ VERBOSE(VB_FILE|VB_EXTRA,
+ LOC + QString("Write(*, %1) total %2 cnt %3")
+ .arg(count,4).arg(totalBufferUse).arg(writeBuffers.size()));
return count;
}
@@ -332,8 +237,19 @@ uint ThreadedFileWriter::Write(const void *data, uint count)
*/
long long ThreadedFileWriter::Seek(long long pos, int whence)
{
- Flush();
-
+ QMutexLocker locker(&buflock);
+ flush = true;
+ while (!writeBuffers.empty())
+ {
+ bufferHasData.wakeAll();
+ if (!bufferEmpty.wait(locker.mutex(), 2000))
+ {
+ VERBOSE(VB_IMPORTANT, LOC +
+ QString("Taking a long time to flush.. buffer size %1")
+ .arg(totalBufferUse));
+ }
+ }
+ flush = false;
return lseek(fd, pos, whence);
}
@@ -344,10 +260,15 @@ void ThreadedFileWriter::Flush(void)
{
QMutexLocker locker(&buflock);
flush = true;
- while (BufUsedPriv() > 0)
+ while (!writeBuffers.empty())
{
+ bufferHasData.wakeAll();
if (!bufferEmpty.wait(locker.mutex(), 2000))
- VERBOSE(VB_IMPORTANT, LOC + "Taking a long time to flush..");
+ {
+ VERBOSE(VB_IMPORTANT, LOC +
+ QString("Taking a long time to flush.. buffer size %1")
+ .arg(totalBufferUse));
+ }
}
flush = false;
}
@@ -387,107 +308,156 @@ void ThreadedFileWriter::Sync(void)
}
}
-/** \fn ThreadedFileWriter::SetWriteBufferSize(uint)
- * \brief Sets the total size of the write buffer.
- * WARNING: This is not safe when another thread is writing to the buffer.
- */
-void ThreadedFileWriter::SetWriteBufferSize(uint newSize)
-{
- if (newSize <= 0)
- return;
-
- Flush();
-
- QMutexLocker locker(&buflock);
- delete [] buf;
- rpos = wpos = 0;
- buf = new char[newSize + 1024];
- memset(buf, 0, newSize + 64);
- tfw_buf_size = newSize;
-}
-
/** \fn ThreadedFileWriter::SetWriteBufferMinWriteSize(uint)
* \brief Sets the minumum number of bytes to write to disk in a single write.
* This is ignored during a Flush(void)
*/
void ThreadedFileWriter::SetWriteBufferMinWriteSize(uint newMinSize)
{
- if (newMinSize <= 0)
- return;
-
- tfw_min_write_size = newMinSize;
+ QMutexLocker locker(&buflock);
+ if (newMinSize > 0)
+ tfw_min_write_size = newMinSize;
+ bufferHasData.wakeAll();
}
/** \fn ThreadedFileWriter::SyncLoop(void)
* \brief The thread run method that calls Sync(void).
*/
void ThreadedFileWriter::SyncLoop(void)
{
+ QMutexLocker locker(&buflock);
while (!in_dtor)
{
+ locker.unlock();
+
Sync();
- buflock.lock();
- int mstimeout = (written > tfw_min_write_size) ? 1000 : 100;
- bufferSyncWait.wait(&buflock, mstimeout);
- buflock.unlock();
+ locker.relock();
+ bufferSyncWait.wait(&buflock, 1000);
}
}
/** \fn ThreadedFileWriter::DiskLoop(void)
- * \brief The thread run method that actually calls safe_write().
+ * \brief The thread run method that actually calls writes to disk.
*/
void ThreadedFileWriter::DiskLoop(void)
{
- uint size = 0;
- written = 0;
+ QMutexLocker locker(&buflock);
+
+ // Even if the bytes buffered is less than the minimum write
+ // size we do want to write to the OS buffers periodically.
+ // This timer makes sure we do.
+ MythTimer minWriteTimer;
+ minWriteTimer.start();
- while (!in_dtor || BufUsed() > 0)
+ while (!in_dtor)
{
- buflock.lock();
- size = BufUsedPriv();
+ if (ignore_writes)
+ {
+ while (!writeBuffers.empty())
+ {
+ delete writeBuffers.front();
+ writeBuffers.pop_front();
+ }
+ while (!emptyBuffers.empty())
+ {
+ delete emptyBuffers.front();
+ emptyBuffers.pop_front();
+ }
+ bufferEmpty.wakeAll();
+ bufferHasData.wait(locker.mutex());
+ continue;
+ }
- if (size == 0)
+ if (writeBuffers.empty())
{
- buflock.unlock();
bufferEmpty.wakeAll();
- buflock.lock();
+ bufferHasData.wait(locker.mutex(), 1000);
+ TrimEmptyBuffers();
+ continue;
}
- if (!size || (!in_dtor && !flush &&
- ((size < tfw_min_write_size) &&
- (written >= tfw_min_write_size))))
+ int mwte = minWriteTimer.elapsed();
+ if (!flush && (mwte < 250) && (totalBufferUse < kMinWriteSize))
{
- bufferHasData.wait(&buflock, 100);
- buflock.unlock();
+ bufferHasData.wait(locker.mutex(), 250 - mwte);
+ TrimEmptyBuffers();
continue;
}
- uint trpos = rpos;
- buflock.unlock();
- /* cap the max. write size. Prevents the situation where 90% of the
- buffer is valid, and we try to write all of it at once which
- takes a long time. During this time, the other thread fills up
- the 10% that was free... */
- size = (size > TFW_MAX_WRITE_SIZE) ? TFW_MAX_WRITE_SIZE : size;
+ TFWBuffer *buf = writeBuffers.front();
+ writeBuffers.pop_front();
+ totalBufferUse -= buf->data.size();
+ minWriteTimer.start();
- bool write_ok;
- if (ignore_writes)
- ;
- else if ((trpos + size) > tfw_buf_size)
+ //////////////////////////////////////////
+
+ const void *data = &(buf->data[0]);
+ uint sz = buf->data.size();
+
+ bool write_ok = true;
+ uint tot = 0;
+ uint errcnt = 0;
+
+ VERBOSE(VB_FILE|VB_EXTRA, LOC + QString("write(%1) cnt %2 total %3")
+ .arg(sz).arg(writeBuffers.size())
+ .arg(totalBufferUse));
+
+ MythTimer writeTimer;
+ writeTimer.start();
+
+ while ((tot < sz) && !in_dtor)
{
- int first_chunk_size = tfw_buf_size - trpos;
- int second_chunk_size = size - first_chunk_size;
- size = safe_write(fd, buf + trpos, first_chunk_size, write_ok);
- if ((int)size == first_chunk_size && write_ok)
- size += safe_write(fd, buf, second_chunk_size, write_ok);
+ locker.unlock();
+
+ int ret = write(fd, (char *)data + tot, sz - tot);
+
+ if (ret < 0)
+ {
+ if (EAGAIN == errno)
+ {
+ VERBOSE(VB_IMPORTANT, LOC + "Got EAGAIN.");
+ }
+ else
+ {
+ errcnt++;
+ VERBOSE(VB_IMPORTANT, LOC_ERR + "File I/O " +
+ QString(" errcnt: %1").arg(errcnt) + ENO);
+ }
+
+ if ((errcnt >= 3) || (ENOSPC == errno) || (EFBIG == errno))
+ {
+ locker.relock();
+ write_ok = false;
+ break;
+ }
+ }
+ else
+ {
+ tot += ret;
+ }
+
+ locker.relock();
+
+ if (!in_dtor)
+ bufferHasData.wait(locker.mutex(), 50);
}
- else
+
+ //////////////////////////////////////////
+
+ buf->lastUsed = QDateTime::currentDateTime();
+ emptyBuffers.push_back(buf);
+
+ if (writeTimer.elapsed() > 1000)
{
- size = safe_write(fd, buf + trpos, size, write_ok);
+ VERBOSE(VB_IMPORTANT, LOC_WARN +
+ QString("write(%1) cnt %2 total %3 -- "
+ "took a long time, %4 ms")
+ .arg(sz).arg(writeBuffers.size())
+ .arg(totalBufferUse).arg(writeTimer.elapsed()));
}
- if (!ignore_writes && !write_ok && ((EFBIG == errno) || (ENOSPC == errno)))
+ if (!write_ok && ((EFBIG == errno) || (ENOSPC == errno)))
{
QString msg;
switch (errno)
@@ -498,7 +468,8 @@ void ThreadedFileWriter::DiskLoop(void)
"\n\t\t\t"
"You must either change the process ulimits, configure"
"\n\t\t\t"
- "your operating system with \"Large File\" support, or use"
+ "your operating system with \"Large File\" support, "
+ "or use"
"\n\t\t\t"
"a filesystem which supports 64-bit or 128-bit files."
"\n\t\t\t"
@@ -508,67 +479,33 @@ void ThreadedFileWriter::DiskLoop(void)
msg =
"No space left on the device for file '%1'"
"\n\t\t\t"
- "file will be truncated, no further writing will be done.";
+ "file will be truncated, no further writing "
+ "will be done.";
break;
}
- VERBOSE(VB_IMPORTANT, msg.arg(filename));
+ VERBOSE(VB_IMPORTANT, LOC_ERR + msg.arg(filename));
ignore_writes = true;
}
-
- if (written <= tfw_min_write_size)
- {
- written += size;
- }
-
- buflock.lock();
- if (trpos == rpos)
- {
- rpos = (rpos + size) % tfw_buf_size;
- }
- else
- {
- VERBOSE(VB_IMPORTANT, LOC_ERR + "Programmer Error detected! "
- "rpos was changed from under the DiskLoop() function.");
- }
- m_file_wpos += size;
- buflock.unlock();
-
- bufferWroteData.wakeAll();
}
}
-/** \fn ThreadedFileWriter::BufUsedPriv(void) const
- * \brief Number of bytes queued for write by the write thread.
- */
-uint ThreadedFileWriter::BufUsedPriv(void) const
-{
- return (wpos >= rpos) ? wpos - rpos : tfw_buf_size - rpos + wpos;
-}
-
-/** \fn ThreadedFileWriter::BufFreePriv(void) const
- * \brief Number of bytes that can be written without blocking.
- */
-uint ThreadedFileWriter::BufFreePriv(void) const
-{
- return ((wpos >= rpos) ? (rpos + tfw_buf_size) : rpos) - wpos - 1;
-}
-
-/** \fn ThreadedFileWriter::BufUsed(void) const
- * \brief Number of bytes queued for write by the write thread. With locking.
- */
-uint ThreadedFileWriter::BufUsed(void) const
+void ThreadedFileWriter::TrimEmptyBuffers(void)
{
- QMutexLocker locker(&buflock);
- return (wpos >= rpos) ? wpos - rpos : tfw_buf_size - rpos + wpos;
-}
+ QDateTime cur = QDateTime::currentDateTime();
+ QDateTime cur_m_60 = cur.addSecs(-60);
-/**
- * \brief Number of bytes that can be written without blocking. With locking.
- */
-uint ThreadedFileWriter::BufFree(void) const
-{
- QMutexLocker locker(&buflock);
- return ((wpos >= rpos) ? (rpos + tfw_buf_size) : rpos) - wpos - 1;
+ QList<TFWBuffer*>::iterator it = emptyBuffers.begin();
+ while (it != emptyBuffers.end())
+ {
+ if (((*it)->lastUsed < cur_m_60) ||
+ ((*it)->data.capacity() > 3 * (*it)->data.size() &&
+ (*it)->data.capacity() > 64 * 1024))
+ {
+ delete *it;
+ it = emptyBuffers.erase(it);
+ continue;
+ }
+ ++it;
+ }
}
-
View
57 mythtv/libs/libmythtv/ThreadedFileWriter.h
@@ -2,7 +2,11 @@
#ifndef TFW_H_
#define TFW_H_
+#include <vector>
+using namespace std;
+
#include <QWaitCondition>
+#include <QDateTime>
#include <QString>
#include <QMutex>
#include <QThread>
@@ -47,48 +51,40 @@ class ThreadedFileWriter
long long Seek(long long pos, int whence);
uint Write(const void *data, uint count);
- void SetWriteBufferSize(uint newSize = TFW_DEF_BUF_SIZE);
- void SetWriteBufferMinWriteSize(uint newMinSize = TFW_MIN_WRITE_SIZE);
-
- uint BufUsed(void) const;
- uint BufFree(void) const;
+ void SetWriteBufferMinWriteSize(uint newMinSize = kMinWriteSize);
void Sync(void);
void Flush(void);
protected:
void DiskLoop(void);
void SyncLoop(void);
-
- uint BufUsedPriv(void) const;
- uint BufFreePriv(void) const;
+ void TrimEmptyBuffers(void);
private:
// file info
QString filename;
int flags;
mode_t mode;
int fd;
- uint64_t m_file_sync; ///< offset synced to disk
- uint64_t m_file_wpos; ///< offset written to disk
// state
- volatile bool no_writes;
- bool flush;
- bool write_is_blocked;
- volatile bool in_dtor;
- bool ignore_writes;
- long long tfw_min_write_size;
-
- // buffer position state
- volatile uint rpos; ///< points to end of data written to disk
- volatile uint wpos; ///< points to end of data added to buffer
- mutable QMutex buflock; ///< lock needed to update rpos and wpos
- long long written;
-
- // buffer
- char *buf;
- unsigned long tfw_buf_size;
+ bool flush; // protected by buflock
+ bool in_dtor; // protected by buflock
+ bool ignore_writes; // protected by buflock
+ uint tfw_min_write_size; // protected by buflock
+ uint totalBufferUse; // protected by buflock
+
+ // buffers
+ class TFWBuffer
+ {
+ public:
+ vector<char> data;
+ QDateTime lastUsed;
+ };
+ mutable QMutex buflock;
+ QList<TFWBuffer*> writeBuffers; // protected by buflock
+ QList<TFWBuffer*> emptyBuffers; // protected by buflock
// threads
TFWWriteThread *writeThread;
@@ -98,16 +94,11 @@ class ThreadedFileWriter
QWaitCondition bufferEmpty;
QWaitCondition bufferHasData;
QWaitCondition bufferSyncWait;
- QWaitCondition bufferWroteData;
- private:
// constants
- /// Default buffer size.
- static const uint TFW_DEF_BUF_SIZE;
- /// Maximum to write to disk in a single write.
- static const uint TFW_MAX_WRITE_SIZE;
+ static const uint kMaxBufferSize;
/// Minimum to write to disk in a single write, when not flushing buffer.
- static const uint TFW_MIN_WRITE_SIZE;
+ static const uint kMinWriteSize;
};
#endif
View
22 mythtv/libs/libmythtv/analogsignalmonitor.cpp
@@ -23,8 +23,13 @@ AnalogSignalMonitor::AnalogSignalMonitor(
int videofd = channel->GetFd();
if (videofd >= 0)
{
- m_usingv4l2 = CardUtil::hasV4L2(videofd);
- CardUtil::GetV4LInfo(videofd, m_card, m_driver, m_version);
+ uint32_t caps;
+ if (!CardUtil::GetV4LInfo(videofd, m_card, m_driver, m_version, caps))
+ {
+ videofd = -1;
+ return;
+ }
+ m_usingv4l2 = !!(caps & V4L2_CAP_VIDEO_CAPTURE);
VERBOSE(VB_RECORD, LOC + QString("card '%1' driver '%2' version '%3'")
.arg(m_card).arg(m_driver).arg(m_version));
}
@@ -118,16 +123,21 @@ bool AnalogSignalMonitor::handleHDPVR(int videofd)
void AnalogSignalMonitor::UpdateValues(void)
{
- if (!monitor_thread.isRunning() || exit)
+ SignalMonitor::UpdateValues();
+
+ {
+ QMutexLocker locker(&statusLock);
+ if (!scriptStatus.IsGood())
+ return;
+ }
+
+ if (!running || exit)
return;
int videofd = channel->GetFd();
if (videofd < 0)
return;
- if (!IsChannelTuned())
- return;
-
bool isLocked = false;
if (m_usingv4l2)
{
View
50 mythtv/libs/libmythtv/asichannel.cpp
@@ -0,0 +1,50 @@
+/** -*- Mode: c++ -*-
+ * Class ASIChannel
+ */
+
+// MythTV includes
+#include "mythverbose.h"
+#include "mpegtables.h"
+#include "asichannel.h"
+
+#define LOC QString("ASIChan(%1): ").arg(GetDevice())
+#define LOC_ERR QString("ASIChan(%1), Error: ").arg(GetDevice())
+
+ASIChannel::ASIChannel(TVRec *parent, const QString &device) :
+ DTVChannel(parent), m_device(device), m_isopen(false)
+{
+ m_tuner_types.push_back(DTVTunerType::kTunerTypeASI);
+}
+
+ASIChannel::~ASIChannel(void)
+{