diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/multimedia/.prev_CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/multimedia/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/multimedia/audio/audio.pri | 4 | ||||
-rw-r--r-- | src/multimedia/audio/qsamplecache_p.cpp | 2 | ||||
-rw-r--r-- | src/multimedia/audio/qwavedecoder.cpp (renamed from src/multimedia/audio/qwavedecoder_p.cpp) | 221 | ||||
-rw-r--r-- | src/multimedia/audio/qwavedecoder.h (renamed from src/multimedia/audio/qwavedecoder_p.h) | 40 |
6 files changed, 213 insertions, 58 deletions
diff --git a/src/multimedia/.prev_CMakeLists.txt b/src/multimedia/.prev_CMakeLists.txt index b40414969..9c86565ed 100644 --- a/src/multimedia/.prev_CMakeLists.txt +++ b/src/multimedia/.prev_CMakeLists.txt @@ -19,7 +19,7 @@ qt_internal_add_module(Multimedia audio/qaudiosystem.cpp audio/qaudiosystem_p.h audio/qsamplecache_p.cpp audio/qsamplecache_p.h audio/qsoundeffect.cpp audio/qsoundeffect.h - audio/qwavedecoder_p.cpp audio/qwavedecoder_p.h + audio/qwavedecoder.cpp audio/qwavedecoder.h camera/qcamera.cpp camera/qcamera.h camera/qcamera_p.h camera/qcameraexposure.cpp camera/qcameraexposure.h camera/qcamerafocus.cpp camera/qcamerafocus.h diff --git a/src/multimedia/CMakeLists.txt b/src/multimedia/CMakeLists.txt index 1ecca6352..6ed2d10b9 100644 --- a/src/multimedia/CMakeLists.txt +++ b/src/multimedia/CMakeLists.txt @@ -23,7 +23,7 @@ qt_internal_add_module(Multimedia audio/qaudiosystem.cpp audio/qaudiosystem_p.h audio/qsamplecache_p.cpp audio/qsamplecache_p.h audio/qsoundeffect.cpp audio/qsoundeffect.h - audio/qwavedecoder_p.cpp audio/qwavedecoder_p.h + audio/qwavedecoder.cpp audio/qwavedecoder.h camera/qcamera.cpp camera/qcamera.h camera/qcamera_p.h camera/qcameraexposure.cpp camera/qcameraexposure.h camera/qcamerafocus.cpp camera/qcamerafocus.h diff --git a/src/multimedia/audio/audio.pri b/src/multimedia/audio/audio.pri index f180a806b..754504420 100644 --- a/src/multimedia/audio/audio.pri +++ b/src/multimedia/audio/audio.pri @@ -13,7 +13,7 @@ PUBLIC_HEADERS += \ PRIVATE_HEADERS += \ audio/qaudiobuffer_p.h \ audio/qaudiodeviceinfo_p.h \ - audio/qwavedecoder_p.h \ + audio/qwavedecoder.h \ audio/qsamplecache_p.h \ audio/qaudiohelpers_p.h \ audio/qaudiosystem_p.h \ @@ -26,7 +26,7 @@ SOURCES += \ audio/qaudioinput.cpp \ audio/qaudiosystem.cpp \ audio/qsoundeffect.cpp \ - audio/qwavedecoder_p.cpp \ + audio/qwavedecoder.cpp \ audio/qsamplecache_p.cpp \ audio/qaudiobuffer.cpp \ audio/qaudiodecoder.cpp \ diff --git a/src/multimedia/audio/qsamplecache_p.cpp b/src/multimedia/audio/qsamplecache_p.cpp index b293946cc..b2637fb64 100644 --- a/src/multimedia/audio/qsamplecache_p.cpp +++ b/src/multimedia/audio/qsamplecache_p.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qsamplecache_p.h" -#include "qwavedecoder_p.h" +#include "qwavedecoder.h" #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkReply> diff --git a/src/multimedia/audio/qwavedecoder_p.cpp b/src/multimedia/audio/qwavedecoder.cpp index f3c664cb4..d90322068 100644 --- a/src/multimedia/audio/qwavedecoder_p.cpp +++ b/src/multimedia/audio/qwavedecoder.cpp @@ -37,7 +37,7 @@ ** ****************************************************************************/ -#include "qwavedecoder_p.h" +#include "qwavedecoder.h" #include <QtCore/qtimer.h> #include <QtCore/qendian.h> @@ -76,45 +76,106 @@ void bswap4(char *data, qsizetype count) noexcept } -QWaveDecoder::QWaveDecoder(QIODevice *s, QObject *parent) +QWaveDecoder::QWaveDecoder(QIODevice *device, QObject *parent) : QIODevice(parent), - source(s) + device(device) { - open(QIODevice::ReadOnly | QIODevice::Unbuffered); +} - if (enoughDataAvailable()) - QTimer::singleShot(0, this, SLOT(handleData())); - else - connect(source, SIGNAL(readyRead()), SLOT(handleData())); +QWaveDecoder::QWaveDecoder(QIODevice *device, const QAudioFormat &format, QObject *parent) + : QIODevice(parent), + device(device), + format(format) +{ } QWaveDecoder::~QWaveDecoder() { } +bool QWaveDecoder::open(QIODevice::OpenMode mode) +{ + bool canOpen = false; + if (mode & QIODevice::ReadOnly && mode & ~QIODevice::WriteOnly) { + canOpen = QIODevice::open(mode | QIODevice::Unbuffered); + if (canOpen && enoughDataAvailable()) + handleData(); + else + connect(device, SIGNAL(readyRead()), SLOT(handleData())); + return canOpen; + } + + if (mode & QIODevice::WriteOnly) { + if (format.sampleFormat() != QAudioFormat::Int16) + return false; // data format is not supported + canOpen = QIODevice::open(mode); + if (canOpen && writeHeader()) + haveHeader = true; + return canOpen; + } + return QIODevice::open(mode); +} + +void QWaveDecoder::close() +{ + if (isOpen() && (openMode() & QIODevice::WriteOnly)) { + Q_ASSERT(dataSize < INT_MAX); + if (device->isOpen()) + Q_ASSERT(writeDataLength()); + else + qWarning() << "Failed to finalize output because output device was closed"; + } + QIODevice::close(); +} + +bool QWaveDecoder::seek(qint64 pos) +{ + return device->seek(pos); +} + +qint64 QWaveDecoder::pos() const +{ + return device->pos(); +} + QAudioFormat QWaveDecoder::audioFormat() const { return format; } +QIODevice* QWaveDecoder::getDevice() +{ + return device; +} + int QWaveDecoder::duration() const { - return size() * 1000 / (format.bytesPerFrame() * format.sampleRate()); + if (openMode() & QIODevice::WriteOnly) + return 0; + return dataSize * 1000 / (format.bytesPerFrame() * format.sampleRate()); } qint64 QWaveDecoder::size() const { - return haveFormat ? dataSize : 0; + if (openMode() & QIODevice::ReadOnly) + return haveFormat ? dataSize : 0; + else + return device->size(); } bool QWaveDecoder::isSequential() const { - return source->isSequential(); + return device->isSequential(); } qint64 QWaveDecoder::bytesAvailable() const { - return haveFormat ? source->bytesAvailable() : 0; + return haveFormat ? device->bytesAvailable() : 0; +} + +qint64 QWaveDecoder::headerLength() +{ + return HeaderLength; } qint64 QWaveDecoder::readData(char *data, qint64 maxlen) @@ -124,7 +185,7 @@ qint64 QWaveDecoder::readData(char *data, qint64 maxlen) qint64 nSamples = maxlen / format.bytesPerSample(); maxlen = nSamples * format.bytesPerFrame(); - source->read(data, maxlen); + device->read(data, maxlen); if (!byteSwap || format.bytesPerFrame() == 1) return maxlen; @@ -141,25 +202,100 @@ qint64 QWaveDecoder::readData(char *data, qint64 maxlen) break; } return maxlen; + } qint64 QWaveDecoder::writeData(const char *data, qint64 len) { - Q_UNUSED(data); - Q_UNUSED(len); + if (!haveHeader) + return 0; + qint64 written = device->write(data, len); + dataSize += written; + return written; +} + +bool QWaveDecoder::writeHeader() +{ + if (device->size() != 0) + return false; + +#ifndef Q_LITTLE_ENDIAN + // only implemented for LITTLE ENDIAN + return false; +#endif + + CombinedHeader header; + + memset(&header, 0, HeaderLength); + + // RIFF header + memcpy(header.riff.descriptor.id,"RIFF",4); + qToLittleEndian<quint32>(quint32(dataSize + HeaderLength - 8), + reinterpret_cast<unsigned char*>(&header.riff.descriptor.size)); + memcpy(header.riff.type, "WAVE",4); + + // WAVE header + memcpy(header.wave.descriptor.id,"fmt ",4); + qToLittleEndian<quint32>(quint32(16), + reinterpret_cast<unsigned char*>(&header.wave.descriptor.size)); + qToLittleEndian<quint16>(quint16(1), + reinterpret_cast<unsigned char*>(&header.wave.audioFormat)); + qToLittleEndian<quint16>(quint16(format.channelCount()), + reinterpret_cast<unsigned char*>(&header.wave.numChannels)); + qToLittleEndian<quint32>(quint32(format.sampleRate()), + reinterpret_cast<unsigned char*>(&header.wave.sampleRate)); + qToLittleEndian<quint32>(quint32(format.sampleRate() * format.bytesPerFrame()), + reinterpret_cast<unsigned char*>(&header.wave.byteRate)); + qToLittleEndian<quint16>(quint16(format.channelCount() * format.bytesPerSample()), + reinterpret_cast<unsigned char*>(&header.wave.blockAlign)); + qToLittleEndian<quint16>(quint16(format.bytesPerSample() * 8), + reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample)); + + // DATA header + memcpy(header.data.descriptor.id,"data",4); + qToLittleEndian<quint32>(quint32(dataSize), + reinterpret_cast<unsigned char*>(&header.data.descriptor.size)); + + return device->write(reinterpret_cast<const char *>(&header), HeaderLength); +} + +bool QWaveDecoder::writeDataLength() +{ +#ifndef Q_LITTLE_ENDIAN + // only implemented for LITTLE ENDIAN + return false; +#endif - return -1; + if (isSequential()) + return false; + + // seek to RIFF header size, see header.riff.descriptor.size above + if (!device->seek(4)) + return false; + + quint32 length = dataSize + HeaderLength - 8; + if (device->write(reinterpret_cast<const char *>(&length), 4) != 4) + return false; + + // seek to DATA header size, see header.data.descriptor.size above + if (!device->seek(40)) + return false; + + return device->write(reinterpret_cast<const char *>(&dataSize), 4); } void QWaveDecoder::parsingFailed() { - Q_ASSERT(source); - source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); + Q_ASSERT(device); + device->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); emit parsingError(); } void QWaveDecoder::handleData() { + if (openMode() == QIODevice::WriteOnly) + return; + // As a special "state", if we have junk to skip, we do if (junkToSkip > 0) { discardBytes(junkToSkip); // this also updates junkToSkip @@ -167,18 +303,18 @@ void QWaveDecoder::handleData() // If we couldn't skip all the junk, return if (junkToSkip > 0) { // We might have run out - if (source->atEnd()) + if (device->atEnd()) parsingFailed(); return; } } if (state == QWaveDecoder::InitialState) { - if (source->bytesAvailable() < qint64(sizeof(RIFFHeader))) + if (device->bytesAvailable() < qint64(sizeof(RIFFHeader))) return; RIFFHeader riff; - source->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader)); + device->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader)); // RIFF = little endian RIFF, RIFX = big endian RIFF if (((qstrncmp(riff.descriptor.id, "RIFF", 4) != 0) && (qstrncmp(riff.descriptor.id, "RIFX", 4) != 0)) @@ -198,11 +334,11 @@ void QWaveDecoder::handleData() peekChunk(&descriptor); quint32 rawChunkSize = descriptor.size + sizeof(chunk); - if (source->bytesAvailable() < qint64(rawChunkSize)) + if (device->bytesAvailable() < qint64(rawChunkSize)) return; WAVEHeader wave; - source->read(reinterpret_cast<char *>(&wave), sizeof(WAVEHeader)); + device->read(reinterpret_cast<char *>(&wave), sizeof(WAVEHeader)); if (rawChunkSize > sizeof(WAVEHeader)) discardBytes(rawChunkSize - sizeof(WAVEHeader)); @@ -241,10 +377,13 @@ void QWaveDecoder::handleData() break; case 16: fmt = QAudioFormat::Int16; + break; case 24: fmt = QAudioFormat::Unknown; + break; case 32: fmt = QAudioFormat::Int32; + break; } if (fmt == QAudioFormat::Unknown || rate == 0 || channels == 0) { parsingFailed(); @@ -252,8 +391,8 @@ void QWaveDecoder::handleData() } format.setSampleFormat(fmt); - format.setSampleRate(qFromBigEndian<quint32>(wave.sampleRate)); - format.setChannelCount(qFromBigEndian<quint16>(wave.numChannels)); + format.setSampleRate(/*qFromBigEndian<quint32>*/(wave.sampleRate)); + format.setChannelCount(/*qFromBigEndian<quint16>*/(wave.numChannels)); state = QWaveDecoder::WaitingForDataState; } @@ -261,19 +400,19 @@ void QWaveDecoder::handleData() if (state == QWaveDecoder::WaitingForDataState) { if (findChunk("data")) { - source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); + device->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); chunk descriptor; - source->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk)); + device->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk)); if (bigEndian) descriptor.size = qFromBigEndian<quint32>(descriptor.size); else descriptor.size = qFromLittleEndian<quint32>(descriptor.size); - dataSize = descriptor.size; + dataSize = descriptor.size; //means the data size from the data header, not the actual file size haveFormat = true; - connect(source, SIGNAL(readyRead()), SIGNAL(readyRead())); + connect(device, SIGNAL(readyRead()), SIGNAL(readyRead())); emit formatKnown(); return; @@ -281,7 +420,7 @@ void QWaveDecoder::handleData() } // If we hit the end without finding data, it's a parsing error - if (source->atEnd()) { + if (device->atEnd()) { parsingFailed(); } } @@ -299,7 +438,7 @@ bool QWaveDecoder::enoughDataAvailable() if (qstrncmp(descriptor.id, "RIFF", 4) == 0) descriptor.size = qFromLittleEndian<quint32>(descriptor.size); - if (source->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size)) + if (device->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size)) return false; return true; @@ -329,17 +468,19 @@ bool QWaveDecoder::findChunk(const char *chunkId) if (junkToSkip > 0) return false; - } while (source->bytesAvailable() > 0); + } while (device->bytesAvailable() > 0); return false; } bool QWaveDecoder::peekChunk(chunk *pChunk, bool handleEndianness) { - if (source->bytesAvailable() < qint64(sizeof(chunk))) + if (device->bytesAvailable() < qint64(sizeof(chunk))) + return false; + + if (!device->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk))) return false; - source->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk)); if (handleEndianness) { if (bigEndian) pChunk->size = qFromBigEndian<quint32>(pChunk->size); @@ -354,19 +495,19 @@ void QWaveDecoder::discardBytes(qint64 numBytes) // Discards a number of bytes // If the iodevice doesn't have this many bytes in it, // remember how much more junk we have to skip. - if (source->isSequential()) { - QByteArray r = source->read(qMin(numBytes, qint64(16384))); // uggh, wasted memory, limit to a max of 16k + if (device->isSequential()) { + QByteArray r = device->read(qMin(numBytes, qint64(16384))); // uggh, wasted memory, limit to a max of 16k if (r.size() < numBytes) junkToSkip = numBytes - r.size(); else junkToSkip = 0; } else { - quint64 origPos = source->pos(); - source->seek(source->pos() + numBytes); - junkToSkip = origPos + numBytes - source->pos(); + quint64 origPos = device->pos(); + device->seek(device->pos() + numBytes); + junkToSkip = origPos + numBytes - device->pos(); } } QT_END_NAMESPACE -#include "moc_qwavedecoder_p.cpp" +#include "moc_qwavedecoder.cpp" diff --git a/src/multimedia/audio/qwavedecoder_p.h b/src/multimedia/audio/qwavedecoder.h index 650aabbb6..8b8ef9266 100644 --- a/src/multimedia/audio/qwavedecoder_p.h +++ b/src/multimedia/audio/qwavedecoder.h @@ -40,17 +40,6 @@ #ifndef WAVEDECODER_H #define WAVEDECODER_H -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - #include <QtCore/qiodevice.h> #include <qaudioformat.h> @@ -64,12 +53,21 @@ class Q_AUTOTEST_EXPORT QWaveDecoder : public QIODevice Q_OBJECT public: - explicit QWaveDecoder(QIODevice *source, QObject *parent = nullptr); + explicit QWaveDecoder(QIODevice *device, QObject *parent = nullptr); + explicit QWaveDecoder(QIODevice *device, const QAudioFormat &format, + QObject *parent = nullptr); ~QWaveDecoder(); QAudioFormat audioFormat() const; + QIODevice* getDevice(); int duration() const; + static qint64 headerLength(); + bool open(QIODevice::OpenMode mode) override; + void close() override; + bool seek(qint64 pos) override; + qint64 pos() const override; + void setIODevice(QIODevice *device); qint64 size() const override; bool isSequential() const override; qint64 bytesAvailable() const override; @@ -85,6 +83,8 @@ private: qint64 readData(char *data, qint64 maxlen) override; qint64 writeData(const char *data, qint64 len) override; + bool writeHeader(); + bool writeDataLength(); bool enoughDataAvailable(); bool findChunk(const char *chunkId); void discardBytes(qint64 numBytes); @@ -119,10 +119,24 @@ private: quint16 bitsPerSample; }; + struct DATAHeader + { + chunk descriptor; + }; + + struct CombinedHeader + { + RIFFHeader riff; + WAVEHeader wave; + DATAHeader data; + }; + static const int HeaderLength = sizeof(CombinedHeader); + bool haveFormat = false; + bool haveHeader = false; qint64 dataSize = 0; + QIODevice *device = nullptr; QAudioFormat format; - QIODevice *source = nullptr; State state = InitialState; quint32 junkToSkip = 0; bool bigEndian = false; |