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 &currentBuffer = 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 &currentTime, 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"