File indexing completed on 2024-05-12 05:38:16

0001 /*
0002     SPDX-FileCopyrightText: 2007 Matthias Kretz <kretz@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "kiomediastream.h"
0008 #include "kiomediastream_p.h"
0009 
0010 #include <KIO/FileJob>
0011 #include <KLocalizedString>
0012 #include <KProtocolManager>
0013 
0014 #include "debug.h"
0015 
0016 namespace Phonon
0017 {
0018 KioMediaStream::KioMediaStream(const QUrl &url, QObject *parent)
0019     : AbstractMediaStream(parent)
0020     , d_ptr(new KioMediaStreamPrivate(url))
0021 {
0022     d_ptr->q_ptr = this;
0023     qCDebug(PLATFORM);
0024     reset();
0025 }
0026 
0027 void KioMediaStream::reset()
0028 {
0029     qCDebug(PLATFORM);
0030     Q_D(KioMediaStream);
0031     if (d->kiojob) {
0032         d->kiojob->disconnect(this);
0033         d->kiojob->kill();
0034 
0035         d->endOfDataSent = false;
0036         d->seeking = false;
0037         d->reading = false;
0038         d->open = false;
0039         d->seekPosition = 0;
0040     }
0041 
0042     if (KProtocolManager::supportsOpening(d->url)) {
0043         d->kiojob = KIO::open(d->url, QIODevice::ReadOnly);
0044         Q_ASSERT(d->kiojob);
0045         d->open = false;
0046         setStreamSeekable(true);
0047         connect(d->kiojob, SIGNAL(open(KIO::Job *)), this, SLOT(_k_bytestreamFileJobOpen(KIO::Job *)));
0048         connect(d->kiojob, SIGNAL(position(KIO::Job *, KIO::filesize_t)), this, SLOT(_k_bytestreamSeekDone(KIO::Job *, KIO::filesize_t)));
0049     } else {
0050         d->kiojob = KIO::get(d->url, KIO::NoReload, KIO::HideProgressInfo);
0051         Q_ASSERT(d->kiojob);
0052         setStreamSeekable(false);
0053         connect(d->kiojob, SIGNAL(totalSize(KJob *, qulonglong)), this, SLOT(_k_bytestreamTotalSize(KJob *, qulonglong)));
0054         d->kiojob->suspend();
0055     }
0056 
0057     d->kiojob->addMetaData(QStringLiteral("UserAgent"), QLatin1String("KDE Phonon"));
0058     connect(d->kiojob, SIGNAL(data(KIO::Job *, QByteArray)), this, SLOT(_k_bytestreamData(KIO::Job *, QByteArray)));
0059     connect(d->kiojob, SIGNAL(result(KJob *)), this, SLOT(_k_bytestreamResult(KJob *)));
0060 }
0061 
0062 KioMediaStream::~KioMediaStream()
0063 {
0064     qCDebug(PLATFORM);
0065     Q_D(KioMediaStream);
0066     if (d->kiojob) {
0067         d->kiojob->disconnect(this);
0068         KIO::FileJob *filejob = qobject_cast<KIO::FileJob *>(d->kiojob);
0069         if (filejob) {
0070             filejob->close();
0071         }
0072         d->kiojob->kill();
0073     }
0074     delete d_ptr;
0075 }
0076 
0077 void KioMediaStream::needData()
0078 {
0079     Q_D(KioMediaStream);
0080     if (!d->kiojob) {
0081         // no job => job is finished and endOfData was already sent
0082         return;
0083     }
0084     KIO::FileJob *filejob = qobject_cast<KIO::FileJob *>(d->kiojob);
0085     if (filejob) {
0086         // while d->seeking the backend won't get any data
0087         if (d->seeking || !d->open) {
0088             d->reading = true;
0089         } else if (!d->reading) {
0090             d->reading = true;
0091             QMetaObject::invokeMethod(this, "_k_read", Qt::QueuedConnection);
0092             // filejob->read(32768);
0093         }
0094     } else {
0095         // KIO::TransferJob
0096         d->kiojob->resume();
0097     }
0098 }
0099 
0100 void KioMediaStream::enoughData()
0101 {
0102     Q_D(KioMediaStream);
0103     qCDebug(PLATFORM);
0104     // Don't suspend when using a FileJob. The FileJob is controlled by calls to
0105     // FileJob::read()
0106     if (d->kiojob && !qobject_cast<KIO::FileJob *>(d->kiojob)) {
0107         if (!d->kiojob->isSuspended()) {
0108             d->kiojob->suspend();
0109         }
0110     } else {
0111         d->reading = false;
0112     }
0113 }
0114 
0115 void KioMediaStream::seekStream(qint64 position)
0116 {
0117     Q_D(KioMediaStream);
0118     if (!d->kiojob || d->endOfDataSent) {
0119         // no job => job is finished and endOfData was already sent
0120         qCDebug(PLATFORM) << "no job/job finished -> recreate it";
0121         reset();
0122     }
0123     Q_ASSERT(d->kiojob);
0124     qCDebug(PLATFORM) << position << " = " << qulonglong(position);
0125     d->seeking = true;
0126     d->seekPosition = position;
0127     if (d->open) {
0128         KIO::FileJob *filejob = qobject_cast<KIO::FileJob *>(d->kiojob);
0129         filejob->seek(position);
0130     }
0131 }
0132 
0133 void KioMediaStreamPrivate::_k_bytestreamData(KIO::Job *, const QByteArray &data)
0134 {
0135     Q_Q(KioMediaStream);
0136     Q_ASSERT(kiojob);
0137     if (q->streamSize() == 0) {
0138         q->setStreamSize(-1);
0139     }
0140     if (seeking) {
0141         // seek doesn't block, so don't send data to the backend until it signals us
0142         // that the seek is done
0143         qCDebug(PLATFORM) << "seeking: do nothing";
0144         return;
0145     }
0146 
0147     if (data.isEmpty()) {
0148         reading = false;
0149         if (!endOfDataSent) {
0150             qCDebug(PLATFORM) << "empty data: stopping the stream";
0151             endOfDataSent = true;
0152             q->endOfData();
0153         }
0154         return;
0155     }
0156 
0157     // qCDebug(PLATFORM) << "calling writeData on the Backend ByteStream " << data.size();
0158     q->writeData(data);
0159     if (reading) {
0160         Q_ASSERT(qobject_cast<KIO::FileJob *>(kiojob));
0161         QMetaObject::invokeMethod(q, "_k_read", Qt::QueuedConnection);
0162     }
0163 }
0164 
0165 void KioMediaStreamPrivate::_k_bytestreamResult(KJob *job)
0166 {
0167     Q_Q(KioMediaStream);
0168     Q_ASSERT(kiojob == job);
0169     if (job->error()) {
0170         QString kioErrorString = job->errorString();
0171         qCDebug(PLATFORM) << "KIO Job error: " << kioErrorString;
0172         QObject::disconnect(kiojob, SIGNAL(data(KIO::Job *, const QByteArray &)), q, SLOT(_k_bytestreamData(KIO::Job *, const QByteArray &)));
0173         QObject::disconnect(kiojob, SIGNAL(result(KJob *)), q, SLOT(_k_bytestreamResult(KJob *)));
0174         KIO::FileJob *filejob = qobject_cast<KIO::FileJob *>(kiojob);
0175         if (filejob) {
0176             QObject::disconnect(kiojob, SIGNAL(open(KIO::Job *)), q, SLOT(_k_bytestreamFileJobOpen(KIO::Job *)));
0177             QObject::disconnect(kiojob, SIGNAL(position(KIO::Job *, KIO::filesize_t)), q, SLOT(_k_bytestreamSeekDone(KIO::Job *, KIO::filesize_t)));
0178         } else {
0179             QObject::disconnect(kiojob, SIGNAL(totalSize(KJob *, qulonglong)), q, SLOT(_k_bytestreamTotalSize(KJob *, qulonglong)));
0180         }
0181         // go to ErrorState - NormalError
0182         q->error(NormalError, kioErrorString);
0183     } else if (seeking) {
0184         open = false;
0185         kiojob = nullptr;
0186         endOfDataSent = false;
0187         reading = false;
0188         q->reset();
0189         return;
0190     }
0191     open = false;
0192     kiojob = nullptr;
0193     qCDebug(PLATFORM) << "KIO Job is done (will delete itself) and d->kiojob reset to 0";
0194     endOfDataSent = true;
0195     q->endOfData();
0196     reading = false;
0197 }
0198 
0199 void KioMediaStreamPrivate::_k_bytestreamTotalSize(KJob *, qulonglong size)
0200 {
0201     Q_Q(KioMediaStream);
0202     qCDebug(PLATFORM) << size;
0203     q->setStreamSize(size > 0 ? size : -1);
0204 }
0205 
0206 void KioMediaStreamPrivate::_k_bytestreamFileJobOpen(KIO::Job *)
0207 {
0208     Q_Q(KioMediaStream);
0209     Q_ASSERT(kiojob);
0210     open = true;
0211     endOfDataSent = false;
0212     KIO::FileJob *filejob = static_cast<KIO::FileJob *>(kiojob);
0213     qCDebug(PLATFORM) << filejob->size();
0214     q->setStreamSize(filejob->size() > 0 ? filejob->size() : -1);
0215 
0216     if (seeking) {
0217         filejob->seek(seekPosition);
0218     } else if (reading) {
0219         // filejob->read(32768);
0220         QMetaObject::invokeMethod(q, "_k_read", Qt::QueuedConnection);
0221     }
0222 }
0223 
0224 void KioMediaStreamPrivate::_k_bytestreamSeekDone(KIO::Job *, KIO::filesize_t offset)
0225 {
0226     Q_ASSERT(kiojob);
0227     qCDebug(PLATFORM) << offset;
0228     seeking = false;
0229     endOfDataSent = false;
0230     if (reading) {
0231         Q_Q(KioMediaStream);
0232         Q_ASSERT(qobject_cast<KIO::FileJob *>(kiojob));
0233         QMetaObject::invokeMethod(q, "_k_read", Qt::QueuedConnection);
0234     }
0235 }
0236 
0237 void KioMediaStreamPrivate::_k_read()
0238 {
0239     KIO::FileJob *filejob = qobject_cast<KIO::FileJob *>(kiojob);
0240     Q_ASSERT(filejob);
0241     filejob->read(32768);
0242 }
0243 
0244 } // namespace Phonon
0245 
0246 #include "moc_kiomediastream.cpp"