/** \fn DVBStreamHandler::RunSR(void) * \brief Uses "Section" reader to read a DVB device for tables * * This currently only supports DVB streams, ATSC and the raw MPEG * streams used by some cable and satelite providers is not supported. */ void DVBStreamHandler::RunSR(void) { int buffer_size = 4192; // maximum size of Section we handle unsigned char *buffer = new unsigned char[buffer_size]; if (!buffer) { _error = true; return; } SetRunning(true, _needs_buffering, true); LOG(VB_RECORD, LOG_INFO, LOC + "RunSR(): begin"); while (_running_desired && !_error) { RetuneMonitor(); UpdateFiltersFromStreamData(); QMutexLocker read_locker(&_pid_lock); bool readSomething = false; PIDInfoMap::const_iterator fit = _pid_info.begin(); for (; fit != _pid_info.end(); ++fit) { int len = read((*fit)->filter_fd, buffer, buffer_size); if (len <= 0) continue; readSomething = true; const PESPacket pes = PESPacket::ViewData(buffer); const PSIPTable psip(pes); if (psip.SectionSyntaxIndicator()) { _listener_lock.lock(); StreamDataList::const_iterator sit = _stream_data_list.begin(); for (; sit != _stream_data_list.end(); ++sit) sit.key()->HandleTables(fit.key() /* pid */, psip); _listener_lock.unlock(); } } if (!readSomething) usleep(3000); } LOG(VB_RECORD, LOG_INFO, LOC + "RunSR(): " + "shutdown"); RemoveAllPIDFilters(); delete[] buffer; SetRunning(false, _needs_buffering, true); LOG(VB_RECORD, LOG_INFO, LOC + "RunSR(): " + "end"); }
/** \fn DVBStreamHandler::RunSR(void) * \brief Uses "Section" reader to read a DVB device for tables * * This currently only supports DVB streams, ATSC and the raw MPEG * streams used by some cable and satelite providers is not supported. */ void DVBStreamHandler::RunSR(void) { int buffer_size = 4192; // maximum size of Section we handle unsigned char *buffer = new unsigned char[buffer_size]; if (!buffer) return; VERBOSE(VB_RECORD, LOC + "RunSR(): begin"); while (IsRunning()) { RetuneMonitor(); UpdateFiltersFromStreamData(); QMutexLocker read_locker(&_pid_lock); bool readSomething = false; PIDInfoMap::const_iterator fit = _pid_info.begin(); for (; fit != _pid_info.end(); ++fit) { int len = read((*fit)->filter_fd, buffer, buffer_size); if (len <= 0) continue; readSomething = true; const PESPacket pes = PESPacket::ViewData(buffer); const PSIPTable psip(pes); if (psip.SectionSyntaxIndicator()) { _listener_lock.lock(); for (uint i = 0; i < _stream_data_list.size(); i++) { _stream_data_list[i]->HandleTables( fit.key() /* pid */, psip); } _listener_lock.unlock(); } } if (!readSomething) usleep(3000); } VERBOSE(VB_RECORD, LOC + "RunSR(): " + "shutdown"); RemoveAllPIDFilters(); delete[] buffer; VERBOSE(VB_RECORD, LOC + "RunSR(): " + "end"); }
/** \fn DVBStreamHandler::RunTS(void) * \brief Uses TS filtering devices to read a DVB device for tables & data * * This supports all types of MPEG based stream data, but is extreemely * slow with DVB over USB 1.0 devices which for efficiency reasons buffer * a stream until a full block transfer buffer full of the requested * tables is available. This takes a very long time when you are just * waiting for a PAT or PMT table, and the buffer is hundreds of packets * in size. */ void DVBStreamHandler::RunTS(void) { if (_needs_buffering) _device_read_buffer = new DeviceReadBuffer(this); int remainder = 0; int buffer_size = TSPacket::SIZE * 15000; unsigned char *buffer = new unsigned char[buffer_size]; if (!buffer) return; memset(buffer, 0, buffer_size); QByteArray dvr_dev_path = _dvr_dev_path.toAscii(); int dvr_fd; for (int tries = 1; ; ++tries) { dvr_fd = open(dvr_dev_path.constData(), O_RDONLY | O_NONBLOCK); if (dvr_fd >= 0) break; VERBOSE(VB_IMPORTANT, LOC_WARN + QString("Opening DVR device %1 failed : %2") .arg(_dvr_dev_path).arg(strerror(errno))); if (tries >= 20 || (errno != EBUSY && errno != EAGAIN)) { VERBOSE(VB_IMPORTANT, LOC + QString("Failed to open DVR device %1 : %2") .arg(_dvr_dev_path).arg(strerror(errno))); delete[] buffer; return; } usleep(50000); } bool _error = false; if (_device_read_buffer) { bool ok = _device_read_buffer->Setup(_dvb_dev, dvr_fd); if (!ok) { VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to allocate DRB buffer"); _error = true; delete[] buffer; return; } _device_read_buffer->Start(); } VERBOSE(VB_RECORD, LOC + "RunTS(): begin"); fd_set fd_select_set; FD_ZERO( &fd_select_set); FD_SET (dvr_fd, &fd_select_set); while (IsRunning() && !_error) { RetuneMonitor(); UpdateFiltersFromStreamData(); ssize_t len = 0; if (_device_read_buffer) { len = _device_read_buffer->Read( &(buffer[remainder]), buffer_size - remainder); // Check for DRB errors if (_device_read_buffer->IsErrored()) { VERBOSE(VB_IMPORTANT, LOC_ERR + "Device error detected"); _error = true; } if (_device_read_buffer->IsEOF()) { VERBOSE(VB_IMPORTANT, LOC_ERR + "Device EOF detected"); _error = true; } } else { // timeout gets reset by select, so we need to create new one struct timeval timeout = { 0, 50 /* ms */ * 1000 /* -> usec */ }; int ret = select(dvr_fd+1, &fd_select_set, NULL, NULL, &timeout); if (ret == -1 && errno != EINTR) { VERBOSE(VB_IMPORTANT, LOC_ERR + "select() failed" + ENO); } else { len = read(dvr_fd, &(buffer[remainder]), buffer_size - remainder); } } if ((0 == len) || (-1 == len)) { usleep(100); continue; } len += remainder; if (len < 10) // 10 bytes = 4 bytes TS header + 6 bytes PES header { remainder = len; continue; } _listener_lock.lock(); if (_stream_data_list.empty()) { _listener_lock.unlock(); continue; } for (uint i = 0; i < _stream_data_list.size(); i++) { remainder = _stream_data_list[i]->ProcessData(buffer, len); } _listener_lock.unlock(); if (remainder > 0 && (len > remainder)) // leftover bytes memmove(buffer, &(buffer[len - remainder]), remainder); } VERBOSE(VB_RECORD, LOC + "RunTS(): " + "shutdown"); RemoveAllPIDFilters(); if (_device_read_buffer) { if (_device_read_buffer->IsRunning()) _device_read_buffer->Stop(); delete _device_read_buffer; _device_read_buffer = NULL; } close(dvr_fd); delete[] buffer; VERBOSE(VB_RECORD, LOC + "RunTS(): " + "end"); SetRunning(false); }
void ExternalStreamHandler::run(void) { QString cmd; QString result; QString xon; QByteArray buffer; uint len; RunProlog(); LOG(VB_RECORD, LOG_INFO, LOC + "run(): begin"); SetRunning(true, true, false); if (m_poll_mode) xon = QString("SendBytes:%1").arg(PACKET_SIZE); else xon = "XON"; uint remainder = 0; while (_running_desired && !_error) { if (!IsOpen()) { if (!Open()) { LOG(VB_RECORD, LOG_WARNING, LOC + QString("TS not open yet: %1") .arg(m_error)); usleep(750000); continue; } } if (StreamingCount() == 0) { usleep(5000); continue; } UpdateFiltersFromStreamData(); ProcessCommand(xon, 1000, result); if (result.startsWith("ERR")) { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Aborting: %1 -> %2") .arg(xon).arg(result)); _error = true; } while ((len = m_IO->Read(buffer, PACKET_SIZE, 10)) > 0 || buffer.size() > 188*50) { if (m_IO->Error()) { m_error = m_IO->ErrorString(); _error = true; LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read from Extern recorder: %1") .arg(m_error)); break; } if (!_running_desired) break; if (!_listener_lock.tryLock()) continue; if (_stream_data_list.empty()) { LOG(VB_GENERAL, LOG_ERR, LOC + "_stream_data_list is empty"); _listener_lock.unlock(); continue; } if (!m_poll_mode) { ProcessCommand(QString("XOFF"), 50, result); if (result.startsWith("ERR")) { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Aborting: %1 -> %2") .arg(xon).arg(result)); _error = true; } } StreamDataList::const_iterator sit = _stream_data_list.begin(); for (; sit != _stream_data_list.end(); ++sit) remainder = sit.key()->ProcessData (reinterpret_cast<const uint8_t *> (buffer.constData()), buffer.size()); _listener_lock.unlock(); len = buffer.size(); if (m_replay) { m_replay_buffer += buffer.left(len - remainder); if (m_replay_buffer.size() > (5000 * PACKET_SIZE)) { m_replay_buffer.remove(0, len - remainder); LOG(VB_RECORD, LOG_WARNING, LOC + QString("Replay size truncated to %1 bytes") .arg(m_replay_buffer.size())); } } if (remainder > 0 && (len > remainder)) // leftover bytes buffer.remove(0, len - remainder); else buffer.clear(); } usleep(10); } LOG(VB_RECORD, LOG_INFO, LOC + "run(): " + "shutdown"); RemoveAllPIDFilters(); SetRunning(false, true, false); LOG(VB_RECORD, LOG_INFO, LOC + "run(): " + "end"); RunEpilog(); }
/** \fn HDHRStreamHandler::run(void) * \brief Reads HDHomeRun socket for tables & data */ void HDHRStreamHandler::run(void) { RunProlog(); /* Create TS socket. */ if (!hdhomerun_device_stream_start(_hdhomerun_device)) { LOG(VB_GENERAL, LOG_ERR, LOC + "Starting recording (set target failed). Aborting."); _error = true; RunEpilog(); return; } hdhomerun_device_stream_flush(_hdhomerun_device); SetRunning(true, false, false); LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): begin"); int remainder = 0; QTime last_update; while (_running_desired && !_error) { int elapsed = !last_update.isValid() ? -1 : last_update.elapsed(); elapsed = (elapsed < 0) ? 1000 : elapsed; if (elapsed > 100) { UpdateFiltersFromStreamData(); if (_tune_mode != hdhrTuneModeVChannel) UpdateFilters(); last_update.restart(); } size_t read_size = 64 * 1024; // read about 64KB read_size /= VIDEO_DATA_PACKET_SIZE; read_size *= VIDEO_DATA_PACKET_SIZE; size_t data_length; unsigned char *data_buffer = hdhomerun_device_stream_recv( _hdhomerun_device, read_size, &data_length); if (!data_buffer) { usleep(20000); continue; } // Assume data_length is a multiple of 188 (packet size) _listener_lock.lock(); if (_stream_data_list.empty()) { _listener_lock.unlock(); continue; } StreamDataList::const_iterator sit = _stream_data_list.begin(); for (; sit != _stream_data_list.end(); ++sit) remainder = sit.key()->ProcessData(data_buffer, data_length); _listener_lock.unlock(); if (remainder != 0) { LOG(VB_RECORD, LOG_INFO, LOC + QString("RunTS(): data_length = %1 remainder = %2") .arg(data_length).arg(remainder)); } } LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): " + "shutdown"); RemoveAllPIDFilters(); hdhomerun_device_stream_stop(_hdhomerun_device); LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): " + "end"); SetRunning(false, false, false); RunEpilog(); }
/** \fn HDHRStreamHandler::RunTS(void) * \brief Uses TS filtering devices to read a DVB device for tables & data * * This supports all types of MPEG based stream data, but is extreemely * slow with DVB over USB 1.0 devices which for efficiency reasons buffer * a stream until a full block transfer buffer full of the requested * tables is available. This takes a very long time when you are just * waiting for a PAT or PMT table, and the buffer is hundreds of packets * in size. */ void HDHRStreamHandler::RunTS(void) { int remainder = 0; /* Calculate buffer size */ uint buffersize = gCoreContext->GetNumSetting( "HDRingbufferSize", 50 * TSPacket::SIZE) * 1024; buffersize /= VIDEO_DATA_PACKET_SIZE; buffersize *= VIDEO_DATA_PACKET_SIZE; // Buffer should be at least about 1MB.. buffersize = max(49 * TSPacket::SIZE * 128, buffersize); /* Create TS socket. */ if (!hdhomerun_device_stream_start(_hdhomerun_device)) { VERBOSE(VB_IMPORTANT, LOC_ERR + "Starting recording (set target failed). Aborting."); return; } hdhomerun_device_stream_flush(_hdhomerun_device); bool _error = false; VERBOSE(VB_RECORD, LOC + "RunTS(): begin"); while (IsRunning() && !_error) { UpdateFiltersFromStreamData(); size_t read_size = 64 * 1024; // read about 64KB read_size /= VIDEO_DATA_PACKET_SIZE; read_size *= VIDEO_DATA_PACKET_SIZE; size_t data_length; unsigned char *data_buffer = hdhomerun_device_stream_recv( _hdhomerun_device, read_size, &data_length); if (!data_buffer) { usleep(5000); continue; } // Assume data_length is a multiple of 188 (packet size) _listener_lock.lock(); if (_stream_data_list.empty()) { _listener_lock.unlock(); continue; } for (uint i = 0; i < _stream_data_list.size(); i++) { remainder = _stream_data_list[i]->ProcessData( data_buffer, data_length); } _listener_lock.unlock(); if (remainder != 0) { VERBOSE(VB_RECORD, LOC + QString("RunTS(): data_length = %1 remainder = %2") .arg(data_length).arg(remainder)); } } VERBOSE(VB_RECORD, LOC + "RunTS(): " + "shutdown"); RemoveAllPIDFilters(); hdhomerun_device_stream_stop(_hdhomerun_device); VERBOSE(VB_RECORD, LOC + "RunTS(): " + "end"); SetRunning(false); }
void ASIStreamHandler::run(void) { RunProlog(); LOG(VB_RECORD, LOG_INFO, LOC + "run(): begin"); if (!Open()) { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open device %1 : %2") .arg(_device).arg(strerror(errno))); _error = true; return; } DeviceReadBuffer *drb = new DeviceReadBuffer(this, true, false); bool ok = drb->Setup(_device, _fd, _packet_size, _buf_size); if (!ok) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to allocate DRB buffer"); delete drb; drb = NULL; Close(); _error = true; RunEpilog(); return; } uint buffer_size = _packet_size * 15000; unsigned char *buffer = new unsigned char[buffer_size]; if (!buffer) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to allocate buffer"); delete drb; drb = NULL; Close(); _error = true; RunEpilog(); return; } memset(buffer, 0, buffer_size); SetRunning(true, true, false); drb->Start(); { QMutexLocker locker(&_start_stop_lock); _drb = drb; } int remainder = 0; while (_running_desired && !_error) { UpdateFiltersFromStreamData(); ssize_t len = 0; len = drb->Read( &(buffer[remainder]), buffer_size - remainder); if (!_running_desired) break; // Check for DRB errors if (drb->IsErrored()) { LOG(VB_GENERAL, LOG_ERR, LOC + "Device error detected"); _error = true; } if (drb->IsEOF()) { LOG(VB_GENERAL, LOG_ERR, LOC + "Device EOF detected"); _error = true; } if ((0 == len) || (-1 == len)) { usleep(100); continue; } len += remainder; if (len < 10) // 10 bytes = 4 bytes TS header + 6 bytes PES header { remainder = len; continue; } if (!_listener_lock.tryLock()) { remainder = len; continue; } if (_stream_data_list.empty()) { _listener_lock.unlock(); continue; } StreamDataList::const_iterator sit = _stream_data_list.begin(); for (; sit != _stream_data_list.end(); ++sit) remainder = sit.key()->ProcessData(buffer, len); if (_mpts != NULL) _mpts->Write(buffer, len - remainder); _listener_lock.unlock(); if (remainder > 0 && (len > remainder)) // leftover bytes memmove(buffer, &(buffer[len - remainder]), remainder); } LOG(VB_RECORD, LOG_INFO, LOC + "run(): " + "shutdown"); RemoveAllPIDFilters(); { QMutexLocker locker(&_start_stop_lock); _drb = NULL; } if (drb->IsRunning()) drb->Stop(); delete drb; delete[] buffer; Close(); LOG(VB_RECORD, LOG_INFO, LOC + "run(): " + "end"); SetRunning(false, true, false); RunEpilog(); }
/** \fn DVBStreamHandler::RunTS(void) * \brief Uses TS filtering devices to read a DVB device for tables & data * * This supports all types of MPEG based stream data, but is extreemely * slow with DVB over USB 1.0 devices which for efficiency reasons buffer * a stream until a full block transfer buffer full of the requested * tables is available. This takes a very long time when you are just * waiting for a PAT or PMT table, and the buffer is hundreds of packets * in size. */ void DVBStreamHandler::RunTS(void) { QByteArray dvr_dev_path = _dvr_dev_path.toAscii(); int dvr_fd; for (int tries = 1; ; ++tries) { dvr_fd = open(dvr_dev_path.constData(), O_RDONLY | O_NONBLOCK); if (dvr_fd >= 0) break; LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Opening DVR device %1 failed : %2") .arg(_dvr_dev_path).arg(strerror(errno))); if (tries >= 20 || (errno != EBUSY && errno != EAGAIN)) { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open DVR device %1 : %2") .arg(_dvr_dev_path).arg(strerror(errno))); _error = true; return; } usleep(50000); } int remainder = 0; int buffer_size = TSPacket::kSize * 15000; unsigned char *buffer = new unsigned char[buffer_size]; if (!buffer) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to allocate memory"); close(dvr_fd); _error = true; return; } memset(buffer, 0, buffer_size); DeviceReadBuffer *drb = NULL; if (_needs_buffering) { drb = new DeviceReadBuffer(this, true, false); if (!drb->Setup(_device, dvr_fd)) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to allocate DRB buffer"); delete drb; delete[] buffer; close(dvr_fd); _error = true; return; } drb->Start(); } { // SetRunning() + set _drb QMutexLocker locker(&_start_stop_lock); _running = true; _using_buffering = _needs_buffering; _using_section_reader = false; _drb = drb; } LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): begin"); fd_set fd_select_set; FD_ZERO( &fd_select_set); FD_SET (dvr_fd, &fd_select_set); while (_running_desired && !_error) { RetuneMonitor(); UpdateFiltersFromStreamData(); ssize_t len = 0; if (drb) { len = drb->Read( &(buffer[remainder]), buffer_size - remainder); // Check for DRB errors if (drb->IsErrored()) { LOG(VB_GENERAL, LOG_ERR, LOC + "Device error detected"); _error = true; } if (drb->IsEOF() && _running_desired) { LOG(VB_GENERAL, LOG_ERR, LOC + "Device EOF detected"); _error = true; } } else { // timeout gets reset by select, so we need to create new one struct timeval timeout = { 0, 50 /* ms */ * 1000 /* -> usec */ }; int ret = select(dvr_fd+1, &fd_select_set, NULL, NULL, &timeout); if (ret == -1 && errno != EINTR) { LOG(VB_GENERAL, LOG_ERR, LOC + "select() failed" + ENO); } else { len = read(dvr_fd, &(buffer[remainder]), buffer_size - remainder); } } if ((0 == len) || (-1 == len)) { usleep(100); continue; } len += remainder; if (len < 10) // 10 bytes = 4 bytes TS header + 6 bytes PES header { remainder = len; continue; } _listener_lock.lock(); if (_stream_data_list.empty()) { _listener_lock.unlock(); continue; } StreamDataList::const_iterator sit = _stream_data_list.begin(); for (; sit != _stream_data_list.end(); ++sit) remainder = sit.key()->ProcessData(buffer, len); _listener_lock.unlock(); if (remainder > 0 && (len > remainder)) // leftover bytes memmove(buffer, &(buffer[len - remainder]), remainder); } LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): " + "shutdown"); RemoveAllPIDFilters(); { QMutexLocker locker(&_start_stop_lock); _drb = NULL; } if (drb) { if (drb->IsRunning()) drb->Stop(); delete drb; } close(dvr_fd); delete[] buffer; LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): " + "end"); SetRunning(false, _needs_buffering, false); }
/** \fn HDHRStreamHandler::run(void) * \brief Reads HDHomeRun socket for tables & data */ void HDHRStreamHandler::run(void) { threadRegister("HDHRStreamHandler"); /* Create TS socket. */ if (!hdhomerun_device_stream_start(_hdhomerun_device)) { LOG(VB_GENERAL, LOG_ERR, LOC + "Starting recording (set target failed). Aborting."); _error = true; threadDeregister(); return; } hdhomerun_device_stream_flush(_hdhomerun_device); SetRunning(true, false, false); /* Calculate buffer size */ uint buffersize = gCoreContext->GetNumSetting( "HDRingbufferSize", 50 * TSPacket::kSize) * 1024; buffersize /= VIDEO_DATA_PACKET_SIZE; buffersize *= VIDEO_DATA_PACKET_SIZE; buffersize = max(49 * TSPacket::kSize * 128, buffersize); LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): begin"); int remainder = 0; while (_running_desired && !_error) { UpdateFiltersFromStreamData(); UpdateFilters(); size_t read_size = 64 * 1024; // read about 64KB read_size /= VIDEO_DATA_PACKET_SIZE; read_size *= VIDEO_DATA_PACKET_SIZE; size_t data_length; unsigned char *data_buffer = hdhomerun_device_stream_recv( _hdhomerun_device, read_size, &data_length); if (!data_buffer) { usleep(5000); continue; } // Assume data_length is a multiple of 188 (packet size) _listener_lock.lock(); if (_stream_data_list.empty()) { _listener_lock.unlock(); continue; } StreamDataList::const_iterator sit = _stream_data_list.begin(); for (; sit != _stream_data_list.end(); ++sit) remainder = sit.key()->ProcessData(data_buffer, data_length); _listener_lock.unlock(); if (remainder != 0) { LOG(VB_RECORD, LOG_INFO, LOC + QString("RunTS(): data_length = %1 remainder = %2") .arg(data_length).arg(remainder)); } } LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): " + "shutdown"); RemoveAllPIDFilters(); hdhomerun_device_stream_stop(_hdhomerun_device); LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): " + "end"); SetRunning(false, false, false); threadDeregister(); }