File indexing completed on 2024-05-12 04:52:15
0001 /* 0002 * dvbliveview.cpp 0003 * 0004 * Copyright (C) 2007-2011 Christoph Pfister <christophpfister@gmail.com> 0005 * 0006 * This program is free software; you can redistribute it and/or modify 0007 * it under the terms of the GNU General Public License as published by 0008 * the Free Software Foundation; either version 2 of the License, or 0009 * (at your option) any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License along 0017 * with this program; if not, write to the Free Software Foundation, Inc., 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 0019 */ 0020 0021 #include "../log.h" 0022 0023 #include <errno.h> 0024 #include <fcntl.h> 0025 #include <KMessageBox> 0026 #include <QDir> 0027 #include <QLocale> 0028 #include <QPainter> 0029 #include <QSet> 0030 #include <QSocketNotifier> 0031 #include <QStandardPaths> 0032 #include <sys/stat.h> // bsd compatibility 0033 #include <sys/types.h> // bsd compatibility 0034 #include <unistd.h> 0035 0036 #include "dvbdevice.h" 0037 #include "dvbliveview.h" 0038 #include "dvbliveview_p.h" 0039 #include "dvbmanager.h" 0040 0041 #if EAGAIN == EWOULDBLOCK 0042 #define IS_EAGAIN(e) (e == EAGAIN) 0043 #else 0044 #define IS_EAGAIN(e) (e == EAGAIN || e == EWOULDBLOCK) 0045 #endif 0046 0047 void DvbOsd::init(DvbManager *manager_, OsdLevel level_, const QString &channelName_, 0048 const QList<DvbSharedEpgEntry> &epgEntries) 0049 { 0050 manager = manager_; 0051 level = level_; 0052 channelName = channelName_; 0053 0054 if (epgEntries.size() < 1) { 0055 DvbEpgEntry empty; 0056 firstEntry = empty; 0057 secondEntry = empty; 0058 return; 0059 } 0060 0061 firstEntry = *epgEntries.at(0); 0062 0063 if (epgEntries.size() < 2) { 0064 DvbEpgEntry empty; 0065 secondEntry = empty; 0066 return; 0067 } 0068 secondEntry = *epgEntries.at(1); 0069 } 0070 0071 QPixmap DvbOsd::paintOsd(QRect &rect, const QFont &font, Qt::LayoutDirection) 0072 { 0073 QFont osdFont = font; 0074 osdFont.setPointSize(20); 0075 0076 QString timeString = QLocale().toString(QTime::currentTime()); 0077 QString entryString; 0078 QString lang(manager->currentEpgLanguage); 0079 int elapsedTime = 0; 0080 int totalTime = 0; 0081 0082 if (firstEntry.channel.isValid()) { 0083 entryString = QLocale().toString(firstEntry.begin.toLocalTime().time()) 0084 + QLatin1Char(' ') + firstEntry.title(lang); 0085 elapsedTime = firstEntry.begin.secsTo(QDateTime::currentDateTime()); 0086 totalTime = QTime(0, 0, 0).secsTo(firstEntry.duration); 0087 } 0088 0089 if ((level == ShortOsd) && secondEntry.channel.isValid()) { 0090 entryString = entryString + QLatin1Char('\n') + 0091 QLocale().toString(secondEntry.begin.toLocalTime().time()) + 0092 QLatin1Char(' ') + secondEntry.title(lang); 0093 } 0094 0095 int lineHeight = QFontMetrics(osdFont).height(); 0096 QRect headerRect(5, 0, rect.width() - 10, lineHeight); 0097 QRect entryRect; 0098 0099 if (level == ShortOsd) { 0100 entryRect = QRect(5, lineHeight + 9, rect.width() - 10, 2 * lineHeight); 0101 rect.setHeight(entryRect.bottom() + 1); 0102 } else { 0103 entryRect = QRect(5, lineHeight + 9, rect.width() - 10, lineHeight); 0104 } 0105 0106 QPixmap pixmap(rect.size()); 0107 0108 { 0109 QPainter painter(&pixmap); 0110 painter.fillRect(rect, Qt::black); 0111 painter.setFont(osdFont); 0112 painter.setPen(Qt::white); 0113 painter.drawText(headerRect, Qt::AlignLeft, channelName); 0114 painter.drawText(headerRect, Qt::AlignRight, timeString); 0115 0116 painter.fillRect(5, lineHeight + 2, rect.width() - 10, 5, Qt::gray); 0117 painter.fillRect(6, lineHeight + 3, rect.width() - 12, 3, Qt::black); 0118 0119 if ((elapsedTime > 0) && (elapsedTime <= totalTime)) { 0120 int width = (((rect.width() - 12) * elapsedTime + (totalTime / 2)) / 0121 totalTime); 0122 painter.fillRect(6, lineHeight + 3, width, 3, Qt::green); 0123 } 0124 0125 painter.drawText(entryRect, Qt::AlignLeft, entryString); 0126 0127 if (level == LongOsd) { 0128 QRect boundingRect = entryRect; 0129 0130 if (!firstEntry.subheading().isEmpty()) { 0131 painter.drawText(entryRect.x(), boundingRect.bottom() + 1, 0132 entryRect.width(), lineHeight, Qt::AlignLeft, 0133 firstEntry.subheading(), &boundingRect); 0134 } 0135 0136 if (!firstEntry.details().isEmpty() && firstEntry.details() != firstEntry.title(lang)) { 0137 painter.drawText(entryRect.x(), boundingRect.bottom() + 1, 0138 entryRect.width(), 0139 rect.height() - boundingRect.bottom() - 1, 0140 Qt::AlignLeft | Qt::TextWordWrap, firstEntry.details(), 0141 &boundingRect); 0142 } 0143 0144 if (boundingRect.bottom() < rect.bottom()) { 0145 rect.setBottom(boundingRect.bottom()); 0146 } 0147 } 0148 } 0149 0150 return pixmap; 0151 } 0152 0153 DvbLiveView::DvbLiveView(DvbManager *manager_, QObject *parent) : 0154 QObject(parent), manager(manager_), device(NULL), videoPid(-1), 0155 audioPid(-1), subtitlePid(-1), pausedTime(0) 0156 { 0157 mediaWidget = manager->getMediaWidget(); 0158 osdWidget = mediaWidget->getOsdWidget(); 0159 0160 internal = new DvbLiveViewInternal(this); 0161 internal->mediaWidget = mediaWidget; 0162 0163 connect(&internal->pmtFilter, SIGNAL(pmtSectionChanged(QByteArray)), 0164 this, SLOT(pmtSectionChanged(QByteArray))); 0165 connect(&patPmtTimer, SIGNAL(timeout()), this, SLOT(insertPatPmt())); 0166 connect(&osdTimer, SIGNAL(timeout()), this, SLOT(osdTimeout())); 0167 0168 connect(internal, SIGNAL(currentAudioStreamChanged(int)), 0169 this, SLOT(currentAudioStreamChanged(int))); 0170 connect(internal, SIGNAL(currentSubtitleChanged(int)), 0171 this, SLOT(currentSubtitleChanged(int))); 0172 connect(internal, SIGNAL(replay()), this, SLOT(replay())); 0173 connect(internal, SIGNAL(playbackFinished()), this, SLOT(playbackFinished())); 0174 connect(internal, SIGNAL(playbackStatusChanged(MediaWidget::PlaybackStatus)), 0175 this, SLOT(playbackStatusChanged(MediaWidget::PlaybackStatus))); 0176 connect(internal, SIGNAL(previous()), this, SIGNAL(previous())); 0177 connect(internal, SIGNAL(next()), this, SIGNAL(next())); 0178 } 0179 0180 DvbLiveView::~DvbLiveView() 0181 { 0182 } 0183 0184 void DvbLiveView::replay() 0185 { 0186 if (device == NULL) { 0187 device = manager->requestDevice(channel->source, channel->transponder, 0188 DvbManager::Shared); 0189 } 0190 0191 if (device == NULL) { 0192 channel = DvbSharedChannel(); 0193 mediaWidget->stop(); 0194 0195 if (manager->getRecordingModel()->hasActiveRecordings()) { 0196 KMessageBox::information(manager->getParentWidget(), 0197 i18nc("@info", "All devices are used for recordings.")); 0198 } else { 0199 KMessageBox::information(manager->getParentWidget(), 0200 i18nc("@info", 0201 "Didn't find a device with valid settings or access permissions are wrong.\n\n" 0202 "Please check the Configure Television window.")); 0203 } 0204 0205 return; 0206 } 0207 0208 internal->channelName = channel->name; 0209 internal->resetPipe(); 0210 mediaWidget->play(internal); 0211 0212 internal->pmtFilter.setProgramNumber(channel->serviceId); 0213 startDevice(); 0214 0215 internal->patGenerator.initPat(channel->transportStreamId, channel->serviceId, 0216 channel->pmtPid); 0217 videoPid = -1; 0218 audioPid = channel->audioPid; 0219 subtitlePid = -1; 0220 pmtSectionChanged(channel->pmtSectionData); 0221 patPmtTimer.start(500); 0222 0223 internal->buffer.reserve(87 * 188); 0224 QTimer::singleShot(2000, this, SLOT(showOsd())); 0225 } 0226 0227 void DvbLiveView::playbackFinished() 0228 { 0229 mediaWidget->play(internal); 0230 } 0231 0232 const DvbSharedChannel &DvbLiveView::getChannel() const 0233 { 0234 return channel; 0235 } 0236 0237 DvbDevice *DvbLiveView::getDevice() const 0238 { 0239 return device; 0240 } 0241 0242 void DvbLiveView::playChannel(const DvbSharedChannel &channel_) 0243 { 0244 DvbDevice *newDevice = NULL; 0245 0246 if ((channel.constData() != NULL) && (channel->source == channel_->source) && 0247 (channel->transponder.corresponds(channel_->transponder))) { 0248 newDevice = manager->requestDevice(channel->source, channel->transponder, 0249 DvbManager::Shared); 0250 } 0251 0252 playbackStatusChanged(MediaWidget::Idle); 0253 channel = channel_; 0254 device = newDevice; 0255 0256 replay(); 0257 } 0258 0259 void DvbLiveView::toggleOsd() 0260 { 0261 if (channel.constData() == NULL) { 0262 return; 0263 } 0264 0265 switch (internal->dvbOsd.level) { 0266 case DvbOsd::Off: 0267 internal->dvbOsd.init(manager, DvbOsd::ShortOsd, 0268 QString(QLatin1String("%1 - %2")).arg(channel->number).arg(channel->name), 0269 manager->getEpgModel()->getCurrentNext(channel)); 0270 osdWidget->showObject(&internal->dvbOsd, 2500); 0271 osdTimer.start(2500); 0272 break; 0273 case DvbOsd::ShortOsd: 0274 internal->dvbOsd.level = DvbOsd::LongOsd; 0275 osdWidget->showObject(&internal->dvbOsd, -1); 0276 osdTimer.stop(); 0277 break; 0278 case DvbOsd::LongOsd: 0279 internal->dvbOsd.level = DvbOsd::Off; 0280 osdWidget->hideObject(); 0281 osdTimer.stop(); 0282 break; 0283 } 0284 } 0285 0286 void DvbLiveView::pmtSectionChanged(const QByteArray &pmtSectionData) 0287 { 0288 internal->pmtSectionData = pmtSectionData; 0289 DvbPmtSection pmtSection(internal->pmtSectionData); 0290 DvbPmtParser pmtParser(pmtSection); 0291 videoPid = pmtParser.videoPid; 0292 0293 for (int i = 0;; ++i) { 0294 if (i == pmtParser.audioPids.size()) { 0295 if (i > 0) { 0296 audioPid = pmtParser.audioPids.at(0).first; 0297 } else { 0298 audioPid = -1; 0299 } 0300 0301 break; 0302 } 0303 0304 if (pmtParser.audioPids.at(i).first == audioPid) { 0305 break; 0306 } 0307 } 0308 0309 for (int i = 0;; ++i) { 0310 if (i == pmtParser.subtitlePids.size()) { 0311 subtitlePid = -1; 0312 break; 0313 } 0314 0315 if (pmtParser.subtitlePids.at(i).first == subtitlePid) { 0316 break; 0317 } 0318 } 0319 0320 updatePids(true); 0321 0322 if (channel->isScrambled) { 0323 device->startDescrambling(internal->pmtSectionData, this); 0324 } 0325 0326 if (internal->timeShiftFile.isOpen()) { 0327 return; 0328 } 0329 0330 internal->audioStreams.clear(); 0331 audioPids.clear(); 0332 0333 for (int i = 0; i < pmtParser.audioPids.size(); ++i) { 0334 const QPair<int, QString> &it = pmtParser.audioPids.at(i); 0335 0336 if (!it.second.isEmpty()) { 0337 internal->audioStreams.append(it.second); 0338 } else { 0339 internal->audioStreams.append(QString::number(it.first)); 0340 } 0341 0342 audioPids.append(it.first); 0343 } 0344 0345 internal->currentAudioStream = audioPids.indexOf(audioPid); 0346 mediaWidget->audioStreamsChanged(); 0347 0348 subtitlePids.clear(); 0349 0350 for (int i = 0; i < pmtParser.subtitlePids.size(); ++i) { 0351 const QPair<int, QString> &it = pmtParser.subtitlePids.at(i); 0352 0353 subtitlePids.append(it.first); 0354 } 0355 0356 internal->currentSubtitle = subtitlePids.indexOf(subtitlePid); 0357 mediaWidget->subtitlesChanged(); 0358 } 0359 0360 void DvbLiveView::insertPatPmt() 0361 { 0362 internal->buffer.append(internal->patGenerator.generatePackets()); 0363 internal->buffer.append(internal->pmtGenerator.generatePackets()); 0364 } 0365 0366 void DvbLiveView::deviceStateChanged() 0367 { 0368 switch (device->getDeviceState()) { 0369 case DvbDevice::DeviceReleased: 0370 stopDevice(); 0371 device = manager->requestDevice(channel->source, channel->transponder, 0372 DvbManager::Shared); 0373 0374 if (device != NULL) { 0375 startDevice(); 0376 } else { 0377 mediaWidget->stop(); 0378 osdWidget->showText(i18nc("message box", "No available device found."), 2500); 0379 } 0380 0381 break; 0382 case DvbDevice::DeviceIdle: 0383 case DvbDevice::DeviceRotorMoving: 0384 case DvbDevice::DeviceTuning: 0385 case DvbDevice::DeviceTuned: 0386 break; 0387 } 0388 } 0389 0390 void DvbLiveView::currentAudioStreamChanged(int currentAudioStream) 0391 { 0392 audioPid = -1; 0393 0394 if ((currentAudioStream >= 0) && (currentAudioStream < audioPids.size())) { 0395 audioPid = audioPids.at(currentAudioStream); 0396 } 0397 0398 updatePids(); 0399 } 0400 0401 void DvbLiveView::currentSubtitleChanged(int currentSubtitle) 0402 { 0403 subtitlePid = -1; 0404 0405 if ((currentSubtitle >= 0) && (currentSubtitle < subtitlePids.size())) { 0406 subtitlePid = subtitlePids.at(currentSubtitle); 0407 } 0408 0409 updatePids(); 0410 } 0411 0412 void DvbLiveView::playbackStatusChanged(MediaWidget::PlaybackStatus playbackStatus) 0413 { 0414 switch (playbackStatus) { 0415 case MediaWidget::Idle: 0416 if (device != NULL) { 0417 stopDevice(); 0418 manager->releaseDevice(device, DvbManager::Shared); 0419 device = NULL; 0420 } 0421 0422 pids.clear(); 0423 patPmtTimer.stop(); 0424 osdTimer.stop(); 0425 0426 internal->pmtSectionData.clear(); 0427 internal->patGenerator = DvbSectionGenerator(); 0428 internal->pmtGenerator = DvbSectionGenerator(); 0429 internal->buffer.clear(); 0430 internal->timeShiftFile.close(); 0431 internal->retryCounter = 0; 0432 internal->updateUrl(); 0433 internal->dvbOsd.init(manager, DvbOsd::Off, QString(), QList<DvbSharedEpgEntry>()); 0434 osdWidget->hideObject(); 0435 break; 0436 case MediaWidget::Playing: 0437 if (internal->timeShiftFile.isOpen()) { 0438 // FIXME 0439 mediaWidget->play(internal); 0440 mediaWidget->setPosition(pausedTime); 0441 } 0442 0443 break; 0444 case MediaWidget::Paused: 0445 pausedTime = mediaWidget->getPosition() - 10; 0446 if (pausedTime < 0) 0447 pausedTime = 0; 0448 if (internal->timeShiftFile.isOpen()) { 0449 break; 0450 } 0451 0452 internal->timeShiftFile.setFileName(manager->getTimeShiftFolder() + QLatin1String("/TimeShift-") + 0453 QDateTime::currentDateTime().toString(QLatin1String("yyyyMMddThhmmss")) + 0454 QLatin1String(".m2t")); 0455 0456 if (internal->timeShiftFile.exists() || 0457 !internal->timeShiftFile.open(QIODevice::WriteOnly)) { 0458 qCWarning(logDvb, "Cannot open file %s", qPrintable(internal->timeShiftFile.fileName())); 0459 internal->timeShiftFile.setFileName(QDir::homePath() + QLatin1String("/TimeShift-") + 0460 QDateTime::currentDateTime().toString(QLatin1String("yyyyMMddThhmmss")) + 0461 QLatin1String(".m2t")); 0462 0463 if (internal->timeShiftFile.exists() || 0464 !internal->timeShiftFile.open(QIODevice::WriteOnly)) { 0465 qCWarning(logDvb, "Cannot open file %s", qPrintable(internal->timeShiftFile.fileName())); 0466 mediaWidget->stop(); 0467 break; 0468 } 0469 } 0470 0471 updatePids(); 0472 0473 // Use either the timeshift or the standard file URL 0474 internal->updateUrl(); 0475 0476 // don't allow changes after starting time shift 0477 internal->audioStreams.clear(); 0478 internal->currentAudioStream = -1; 0479 mediaWidget->audioStreamsChanged(); 0480 internal->currentSubtitle = -1; 0481 mediaWidget->subtitlesChanged(); 0482 break; 0483 } 0484 } 0485 0486 void DvbLiveView::showOsd() 0487 { 0488 if (internal->dvbOsd.level == DvbOsd::Off) { 0489 toggleOsd(); 0490 } 0491 } 0492 0493 void DvbLiveView::osdTimeout() 0494 { 0495 internal->dvbOsd.level = DvbOsd::Off; 0496 osdTimer.stop(); 0497 } 0498 0499 void DvbLiveView::startDevice() 0500 { 0501 foreach (int pid, pids) { 0502 device->addPidFilter(pid, internal); 0503 } 0504 0505 device->addSectionFilter(channel->pmtPid, &internal->pmtFilter); 0506 connect(device, SIGNAL(stateChanged()), this, SLOT(deviceStateChanged())); 0507 0508 if (channel->isScrambled && !internal->pmtSectionData.isEmpty()) { 0509 device->startDescrambling(internal->pmtSectionData, this); 0510 } 0511 0512 manager->getEpgModel()->startEventFilter(device, channel); 0513 } 0514 0515 void DvbLiveView::stopDevice() 0516 { 0517 manager->getEpgModel()->stopEventFilter(device, channel); 0518 0519 if (channel->isScrambled && !internal->pmtSectionData.isEmpty()) { 0520 device->stopDescrambling(internal->pmtSectionData, this); 0521 } 0522 0523 foreach (int pid, pids) { 0524 device->removePidFilter(pid, internal); 0525 } 0526 0527 device->removeSectionFilter(channel->pmtPid, &internal->pmtFilter); 0528 disconnect(device, SIGNAL(stateChanged()), this, SLOT(deviceStateChanged())); 0529 } 0530 0531 void DvbLiveView::updatePids(bool forcePatPmtUpdate) 0532 { 0533 DvbPmtSection pmtSection(internal->pmtSectionData); 0534 DvbPmtParser pmtParser(pmtSection); 0535 QSet<int> newPids; 0536 int pcrPid = pmtSection.pcrPid(); 0537 bool updatePatPmt = forcePatPmtUpdate; 0538 bool isTimeShifting = internal->timeShiftFile.isOpen(); 0539 0540 if (videoPid != -1) { 0541 newPids.insert(videoPid); 0542 } 0543 0544 if (!isTimeShifting) { 0545 if (audioPid != -1) { 0546 newPids.insert(audioPid); 0547 } 0548 } else { 0549 for (int i = 0; i < pmtParser.audioPids.size(); ++i) { 0550 newPids.insert(pmtParser.audioPids.at(i).first); 0551 } 0552 } 0553 0554 for (int i = 0; i < pmtParser.subtitlePids.size(); ++i) { 0555 newPids.insert(pmtParser.subtitlePids.at(i).first); 0556 } 0557 0558 if (pmtParser.teletextPid != -1) { 0559 newPids.insert(pmtParser.teletextPid); 0560 } 0561 0562 /* check PCR PID is set */ 0563 if (pcrPid != 0x1fff) { 0564 /* Check not already in list */ 0565 if (!newPids.contains(pcrPid)) 0566 newPids.insert(pcrPid); 0567 } 0568 0569 for (int i = 0; i < pids.size(); ++i) { 0570 int pid = pids.at(i); 0571 0572 if (!newPids.remove(pid)) { 0573 device->removePidFilter(pid, internal); 0574 pids.removeAt(i); 0575 updatePatPmt = true; 0576 --i; 0577 } 0578 } 0579 0580 foreach (int pid, newPids) { 0581 device->addPidFilter(pid, internal); 0582 pids.append(pid); 0583 updatePatPmt = true; 0584 } 0585 0586 if (updatePatPmt) { 0587 internal->pmtGenerator.initPmt(channel->pmtPid, pmtSection, pids); 0588 insertPatPmt(); 0589 } 0590 } 0591 0592 DvbLiveViewInternal::DvbLiveViewInternal(QObject *parent) : 0593 QObject(parent), mediaWidget(NULL), emptyBuffer(true), timeshift(false), 0594 currentAudioStream(-1), currentSubtitle(-1), retryCounter(0), 0595 readFd(-1), writeFd(-1), notifier(NULL) 0596 { 0597 fileName = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QLatin1String("/dvbpipe.m2t"); 0598 QFile::remove(fileName); 0599 0600 updateUrl(); 0601 0602 if (mkfifo(QFile::encodeName(fileName).constData(), 0600) != 0) { 0603 qCWarning(logDvb, "Failed to open a fifo. Error: %d", errno); 0604 return; 0605 } 0606 0607 readFd = open(QFile::encodeName(fileName).constData(), O_RDONLY | O_NONBLOCK); 0608 0609 if (readFd < 0) { 0610 qCWarning(logDvb, "Failed to open fifo for read. Error: %d", errno); 0611 return; 0612 } 0613 0614 writeFd = open(QFile::encodeName(fileName).constData(), O_WRONLY | O_NONBLOCK); 0615 0616 if (writeFd < 0) { 0617 qCWarning(logDvb, "Failed to open fifo for write. Error: %d", errno); 0618 return; 0619 } 0620 0621 notifier = new QSocketNotifier(writeFd, QSocketNotifier::Write, this); 0622 notifier->setEnabled(false); 0623 connect(notifier, SIGNAL(activated(int)), this, SLOT(writeToPipe())); 0624 0625 emptyBuffer = true; 0626 } 0627 0628 DvbLiveViewInternal::~DvbLiveViewInternal() 0629 { 0630 if (writeFd >= 0) { 0631 close(writeFd); 0632 } 0633 0634 if (readFd >= 0) { 0635 close(readFd); 0636 } 0637 } 0638 0639 void DvbLiveViewInternal::resetPipe() 0640 { 0641 retryCounter = 0; 0642 notifier->setEnabled(false); 0643 0644 if (!buffers.isEmpty()) { 0645 buffer = buffers.at(0); 0646 buffers.clear(); 0647 } 0648 0649 if (readFd >= 0) { 0650 if (buffer.isEmpty()) { 0651 buffer.resize(87 * 188); 0652 } 0653 0654 while (read(readFd, buffer.data(), buffer.size()) > 0) { 0655 } 0656 } 0657 0658 emptyBuffer = true; 0659 buffer.clear(); 0660 } 0661 0662 void DvbLiveViewInternal::writeToPipe() 0663 { 0664 if (buffers.isEmpty()) { 0665 // disable notifier if the buffers are empty 0666 notifier->setEnabled(false); 0667 return; 0668 } 0669 0670 do { 0671 const QByteArray ¤tBuffer = buffers.at(0); 0672 int bytesWritten = int(write(writeFd, currentBuffer.constData(), currentBuffer.size())); 0673 0674 if (bytesWritten < 0) { 0675 // Some interrupt happened while writing. Retry. 0676 if (errno == EINTR) 0677 continue; 0678 0679 if (++retryCounter > 50) { 0680 // Too much failures. Warn the user 0681 qCWarning(logDvb, "Stream seems to be too havy to be displayed"); 0682 return; 0683 } 0684 0685 // EAGAIN may happen when the pipe is full. 0686 // That's a normal condition. No need to report. 0687 if (!IS_EAGAIN(errno)) 0688 qCWarning(logDvb, "Error %d while writing to pipe", errno); 0689 } else { 0690 retryCounter = 0; 0691 if (bytesWritten == currentBuffer.size()) { 0692 buffers.removeFirst(); 0693 continue; 0694 } 0695 if (bytesWritten > 0) 0696 buffers.first().remove(0, bytesWritten); 0697 } 0698 // If bytesWritten is less than buffer size, or returns an 0699 // error, there's no sense on wasting CPU time inside a loop 0700 break; 0701 } while (!buffers.isEmpty()); 0702 0703 if (!buffers.isEmpty()) { 0704 // Wait for a notification that writeFd is ready to write 0705 notifier->setEnabled(true); 0706 } 0707 } 0708 0709 void DvbLiveViewInternal::validateCurrentTotalTime(int ¤tTime, int &totalTime) const 0710 { 0711 if (emptyBuffer) 0712 return; 0713 0714 totalTime = startTime.msecsTo(QTime::currentTime()); 0715 0716 // Adjust it, if needed 0717 if (currentTime > totalTime) 0718 currentTime = totalTime -1; 0719 0720 } 0721 0722 0723 void DvbLiveViewInternal::processData(const char data[188]) 0724 { 0725 buffer.append(data, 188); 0726 0727 if (buffer.size() < (87 * 188)) { 0728 return; 0729 } 0730 0731 if (!timeShiftFile.isOpen()) { 0732 if (writeFd >= 0) { 0733 buffers.append(buffer); 0734 writeToPipe(); 0735 if (emptyBuffer) { 0736 startTime = QTime::currentTime(); 0737 emptyBuffer = false; 0738 } 0739 } 0740 } else { 0741 notifier->setEnabled(false); 0742 timeShiftFile.write(buffer); // FIXME avoid buffer reallocation 0743 if (emptyBuffer) { 0744 startTime = QTime::currentTime(); 0745 emptyBuffer = false; 0746 } 0747 } 0748 0749 buffer.clear(); 0750 buffer.reserve(87 * 188); 0751 } 0752 0753 #include "moc_dvbliveview_p.cpp" 0754 #include "moc_dvbliveview.cpp"