File indexing completed on 2024-12-01 04:21:26

0001 /*
0002     Copyright (C) 2007-2008 Tanguy Krotoff <tkrotoff@gmail.com>
0003     Copyright (C) 2008 Lukas Durfina <lukas.durfina@gmail.com>
0004     Copyright (C) 2009 Fathi Boudra <fabo@kde.org>
0005     Copyright (C) 2009-2011 vlc-phonon AUTHORS <kde-multimedia@kde.org>
0006 
0007     This library is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU Lesser General Public
0009     License as published by the Free Software Foundation; either
0010     version 2.1 of the License, or (at your option) any later version.
0011 
0012     This library is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015     Lesser General Public License for more details.
0016 
0017     You should have received a copy of the GNU Lesser General Public
0018     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 
0021 #include "streamreader.h"
0022 
0023 #include <QtCore/QMutexLocker>
0024 
0025 #include <phonon/streaminterface.h>
0026 
0027 #include "utils/debug.h"
0028 #include "media.h"
0029 #include "mediaobject.h"
0030 #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
0031 
0032 namespace Phonon {
0033 namespace VLC {
0034 
0035 #define BLOCKSIZE 32768
0036 
0037 StreamReader::StreamReader(MediaObject *parent)
0038     : QObject(parent)
0039     , m_pos(0)
0040     , m_size(0)
0041     , m_eos(false)
0042     , m_seekable(false)
0043     , m_unlocked(false)
0044     , m_mediaObject(parent)
0045 {
0046 }
0047 
0048 StreamReader::~StreamReader()
0049 {
0050 }
0051 
0052 void StreamReader::addToMedia(Media *media)
0053 {
0054     lock(); // Make sure we can lock in read().
0055 
0056     media->addOption(QLatin1String("imem-cat=4"));
0057     media->addOption(QLatin1String("imem-data="), INTPTR_PTR(this));
0058     media->addOption(QLatin1String("imem-get="), INTPTR_FUNC(readCallback));
0059     media->addOption(QLatin1String("imem-release="), INTPTR_FUNC(readDoneCallback));
0060     media->addOption(QLatin1String("imem-seek="), INTPTR_FUNC(seekCallback));
0061 
0062     // if stream has known size, we may pass it
0063     // imem module will use it and pass it to demux
0064     if (streamSize() > 0) {
0065         media->addOption(QString("imem-size=%1").arg(streamSize()));
0066     }
0067 }
0068 
0069 void StreamReader::lock()
0070 {
0071     QMutexLocker lock(&m_mutex);
0072     DEBUG_BLOCK;
0073     m_unlocked = false;
0074 }
0075 
0076 void StreamReader::unlock()
0077 {
0078     QMutexLocker lock(&m_mutex);
0079     DEBUG_BLOCK;
0080     m_unlocked = true;
0081     m_waitingForData.wakeAll();
0082 }
0083 
0084 int StreamReader::readCallback(void *data, const char *cookie,
0085                                int64_t *dts, int64_t *pts, unsigned *flags, // krazy:exclude=typedefs
0086                                size_t *bufferSize, void **buffer)
0087 {
0088     Q_UNUSED(cookie);
0089     Q_UNUSED(dts);
0090     Q_UNUSED(pts);
0091     Q_UNUSED(flags);
0092 
0093     StreamReader *that = static_cast<StreamReader *>(data);
0094     size_t length = BLOCKSIZE;
0095 
0096     *buffer = new char[length];
0097 
0098     int size = length;
0099     bool ret = that->read(that->currentPos(), &size, static_cast<char*>(*buffer));
0100 
0101     *bufferSize = static_cast<size_t>(size);
0102 
0103     return ret ? 0 : -1;
0104 }
0105 
0106 int StreamReader::readDoneCallback(void *data, const char *cookie,
0107                                    size_t bufferSize, void *buffer)
0108 {
0109     Q_UNUSED(data);
0110     Q_UNUSED(cookie);
0111     Q_UNUSED(bufferSize);
0112     delete[] static_cast<char *>(buffer);
0113     return 0;
0114 }
0115 
0116 int StreamReader::seekCallback(void *data, const uint64_t pos)
0117 {
0118     StreamReader *that = static_cast<StreamReader *>(data);
0119     if (static_cast<int64_t>(pos) > that->streamSize()) { // krazy:exclude=typedefs
0120         // attempt to seek past the end of our data.
0121         return -1;
0122     }
0123 
0124     that->setCurrentPos(pos);
0125     // this should return a true/false, but it doesn't, so assume success.
0126 
0127     return 0;
0128 }
0129 
0130 quint64 StreamReader::currentBufferSize() const
0131 {
0132     return m_buffer.size();
0133 }
0134 
0135 bool StreamReader::read(quint64 pos, int *length, char *buffer)
0136 {
0137     QMutexLocker lock(&m_mutex);
0138     DEBUG_BLOCK;
0139     bool ret = true;
0140 
0141     if (m_unlocked) {
0142         return ret;
0143     }
0144 
0145     if (currentPos() != pos) {
0146         if (!streamSeekable()) {
0147             return false;
0148         }
0149         setCurrentPos(pos);
0150     }
0151 
0152     if (m_buffer.capacity() < *length) {
0153         m_buffer.reserve(*length);
0154     }
0155 
0156     while (currentBufferSize() < static_cast<unsigned int>(*length)) {
0157         quint64 oldSize = currentBufferSize();
0158         needData();
0159 
0160         m_waitingForData.wait(&m_mutex);
0161 
0162         if (oldSize == currentBufferSize()) {
0163             if (m_eos && m_buffer.isEmpty()) {
0164                 return false;
0165             }
0166             // We didn't get any more data
0167             *length = static_cast<int>(oldSize);
0168             // If we have some data to return, why tell to reader that we failed?
0169             // Remember that length argument is more like maxSize not requiredSize
0170             ret = true;
0171         }
0172     }
0173 
0174     if (m_mediaObject->state() != Phonon::BufferingState &&
0175         m_mediaObject->state() != Phonon::LoadingState) {
0176         enoughData();
0177     }
0178 
0179     memcpy(buffer, m_buffer.data(), *length);
0180     m_pos += *length;
0181     // trim the buffer by the amount read
0182     m_buffer = m_buffer.mid(*length);
0183 
0184     return ret;
0185 }
0186 
0187 void StreamReader::endOfData()
0188 {
0189     m_eos = true;
0190     m_waitingForData.wakeAll();
0191 }
0192 
0193 void StreamReader::writeData(const QByteArray &data)
0194 {
0195     QMutexLocker lock(&m_mutex);
0196     DEBUG_BLOCK;
0197     m_buffer.append(data);
0198     m_waitingForData.wakeAll();
0199 }
0200 
0201 quint64 StreamReader::currentPos() const
0202 {
0203     return m_pos;
0204 }
0205 
0206 void StreamReader::setCurrentPos(qint64 pos)
0207 {
0208     QMutexLocker lock(&m_mutex);
0209     m_pos = pos;
0210     m_buffer.clear(); // Not optimal, but meh
0211 
0212     // Do not touch m_size here, it reflects the size of the stream not the size of the buffer,
0213     // and generally seeking does not change the size!
0214 
0215     seekStream(pos);
0216 }
0217 
0218 void StreamReader::setStreamSize(qint64 newSize)
0219 {
0220     m_size = newSize;
0221 }
0222 
0223 qint64 StreamReader::streamSize() const
0224 {
0225     return m_size;
0226 }
0227 
0228 void StreamReader::setStreamSeekable(bool seekable)
0229 {
0230     m_seekable = seekable;
0231     emit streamSeekableChanged(seekable);
0232 }
0233 
0234 bool StreamReader::streamSeekable() const
0235 {
0236     return m_seekable;
0237 }
0238 
0239 } // namespace VLC
0240 } // namespace Phonon
0241 
0242 #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM