diff --git a/src/Document/CRTPSubDocument.cc b/src/Document/CRTPSubDocument.cc new file mode 100644 index 0000000000000000000000000000000000000000..85983b5e5389a04d152ba1fe50efd10d09e387a7 --- /dev/null +++ b/src/Document/CRTPSubDocument.cc @@ -0,0 +1,73 @@ +#include "CRTPSubDocument.hh" + +using namespace Vivy; + +// AudioSubDocument implementation + +// Get the default stream index or -1 if not possible +int +AudioSubDocument::getDefaultStreamIndex() const noexcept +{ + if (auto ptr = getDefaultStream()) { + return ptr->getStreamIndex(); + } else { + return -1; + } +} + +// Get a pointer to the default stream, nullptr if not possible +AudioContext::StreamPtr +AudioSubDocument::getDefaultStream() const noexcept +{ + if (auto ptr = contextPtr->getDefaultStream().lock()) { + return ptr; + } else { + return nullptr; + } +} + +// Get the stream count, may be 0 +int +AudioSubDocument::getStreamCount() const noexcept +{ + return contextPtr->getStreamCount(); +} + +// Get the stream asked for, nullptr if no stream or if the index is invalid +AudioContext::StreamPtr +AudioSubDocument::getStream(int index) const noexcept +{ + if (auto ptr = contextPtr->getStream(index).lock()) { + return ptr; + } else { + return nullptr; + } +} + +// Init an audio sub-document from a file +void +AudioSubDocument::initFromPath(const QString &path) +{ + if (contextPtr) + qDebug() << "Replacing the audio contetx by a new one for file" << path; + contextPtr.reset(new AudioContext(path)); // May throw + + qDebug() << "Audio OK for" << path; +} + +// VideoSubDocument implementation + +// Init a video sub-document from a file +void +VideoSubDocument::initFromPath(const QString &) +{ +} + +// AssSubDocument implementation +void +AssSubDocument::initFromPath(const QString &path) +{ + Ass::AssFactory factory(path); + factory.getStyles(styles); + factory.getLines(lines); +} diff --git a/src/Document/CRTPSubDocument.hh b/src/Document/CRTPSubDocument.hh index c3494adc343600e547b4ef803c55227ed79658ec..29b1c620eceb0e0c442b238a2d43ac37dd67cd13 100644 --- a/src/Document/CRTPSubDocument.hh +++ b/src/Document/CRTPSubDocument.hh @@ -64,20 +64,19 @@ class AudioSubDocument final : public CRTPSubDocument<AudioDocumentType, AudioSu private: const QStringList &suffixList = Vivy::Utils::audioFileSuffix; - inline void initFromPath(const QString &path) - { - if (contextPtr) - qDebug() << "Replacing the audio contetx by a new one for file" << path; - contextPtr.reset(new AudioContext(path)); // May throw - - qDebug() << "Audio OK for" << path; - } + void initFromPath(const QString &); explicit AudioSubDocument() = default; friend CRTPSubDocument<AudioDocumentType, AudioSubDocument>; private: std::unique_ptr<AudioContext> contextPtr; + +public: + int getDefaultStreamIndex() const noexcept; + AudioContext::StreamPtr getDefaultStream() const noexcept; + int getStreamCount() const noexcept; + AudioContext::StreamPtr getStream(int index) const noexcept; }; // Video document @@ -85,9 +84,7 @@ class VideoSubDocument final : public CRTPSubDocument<VideoDocumentType, VideoSu private: const QStringList &suffixList = Vivy::Utils::videoFileSuffix; - inline void initFromPath([[maybe_unused]] const QString &path) - { - } + void initFromPath(const QString &); explicit VideoSubDocument() noexcept = default; friend CRTPSubDocument<VideoDocumentType, VideoSubDocument>; @@ -97,12 +94,7 @@ private: class AssSubDocument final : public CRTPSubDocument<AssDocumentType, AssSubDocument> { const QStringList &suffixList = Vivy::Utils::assFileSuffix; - inline void initFromPath(const QString &path) - { - Ass::AssFactory factory(path); - factory.getStyles(styles); - factory.getLines(lines); - } + void initFromPath(const QString &); explicit AssSubDocument() noexcept = default; friend CRTPSubDocument<AssDocumentType, AssSubDocument>; diff --git a/src/Lib/Audio.cc b/src/Lib/Audio.cc index f163e7b898275fb0e582006fbbd1097d59744fea..65c1cdc856d01a6d6a8023dc87ea1925fd751c78 100644 --- a/src/Lib/Audio.cc +++ b/src/Lib/Audio.cc @@ -39,6 +39,12 @@ AudioContext::AudioContext(const QString &path) } } + // Get the default stream + defaultStreamIndex = av_find_best_stream(formatPtr, AVMEDIA_TYPE_AUDIO, + -1, // Let AV find one stream + -1, // We don't want related streams + nullptr, 0); + qDebug() << "Opened audio context for" << path << "with duration" << formatPtr->duration; } @@ -67,6 +73,16 @@ AudioContext::getStream(int index) const noexcept return StreamWeakPtr{ spareNullSreamPtr }; } +// Get the default stream of this audio context. If no default audio stream is +// present you won't be able to lock the weak pointer. +AudioContext::StreamWeakPtr +AudioContext::getDefaultStream() const noexcept +{ + if (defaultStreamIndex < 0) + return StreamWeakPtr{ spareNullSreamPtr }; + return getStream(defaultStreamIndex); +} + QString AudioContext::getElementName() const noexcept { @@ -136,6 +152,12 @@ AudioContext::Stream::Stream(AVCodec *streamCodec, AVFormatContext *format, AVSt qDebug() << "[Stream] channels: " << codecParams->channels; } +AudioContext::Stream::~Stream() noexcept +{ + if (dataPtr) + free(dataPtr); +} + QJsonObject AudioContext::Stream::getProperties() const noexcept { @@ -153,6 +175,7 @@ AudioContext::Stream::decodeData() { if (isDecoded()) throw std::logic_error("audio stream is already resampled"); + qDebug() << "Launch decoding of stream" << streamIndexInAudioContext; AVPacket packet; av_init_packet(&packet); @@ -160,7 +183,7 @@ AudioContext::Stream::decodeData() // Iterate through frames while (av_read_frame(dataFormat, &packet) >= 0) { // Only decode audio - if (packet.stream_index == streamIndexInAudioContext) { + if (packet.stream_index != streamIndexInAudioContext) { av_packet_unref(&packet); continue; } @@ -208,9 +231,9 @@ AudioContext::Stream::decodeData() // Append resampled frames to data else [[likely]] { const size_t frame_count = static_cast<size_t>(frame_count_int); - dataRealPtr = (double *)realloc( - dataRealPtr, (dataSize + (size_t)dataFrame->nb_samples) * sizeof(double)); - memcpy(dataRealPtr + dataSize, buffer, frame_count * sizeof(double)); + dataPtr = reinterpret_cast<double *>( + realloc(dataPtr, (dataSize + (size_t)dataFrame->nb_samples) * sizeof(double))); + memcpy(dataPtr + dataSize, buffer, frame_count * sizeof(double)); dataSize += frame_count; } } @@ -218,15 +241,18 @@ AudioContext::Stream::decodeData() dataDeleter(buffer); av_packet_unref(&packet); } + + qDebug() << "Decoding data finished for stream" << streamIndexInAudioContext + << "dataPtr =" << dataPtr << "with dataSize =" << dataSize; } // Delete decoded data, clean up thing void AudioContext::Stream::cleanUpData() noexcept { - dataPtr.reset(); - dataRealPtr = nullptr; - dataSize = 0; + free(dataPtr); + dataPtr = nullptr; + dataSize = 0; } // Get the number of channels in the audio context stream @@ -276,7 +302,7 @@ AudioContext::Stream::getBitRate() const noexcept bool AudioContext::Stream::isDecoded() const noexcept { - return dataPtr != nullptr && dataRealPtr != nullptr; + return dataPtr != nullptr; } // Get the decoded data's size, will return 0 if the data is not decoded. Don't @@ -288,11 +314,30 @@ AudioContext::Stream::getDecodedDataSize() const noexcept } // Get the decoded data, safe to call even if the data is not decoded has the -// weak pointer will be marked as already deleted (use_count will be 0 and -// `(auto ptr = res.lock())` will be evaluated to false if inside an `if` -// statement). -AudioContext::Stream::DataWeakPtr +// pointer will be null. You must check it with an `if` statement. +double * AudioContext::Stream::getDecodedData() const noexcept { - return DataWeakPtr{ dataPtr }; + return dataPtr; +} + +// Get the chunk size of the decoded data +size_t +AudioContext::Stream::getDecodedChunkSize() const noexcept +{ + return 512; +} + +// Get the decalage of the decoded data +size_t +AudioContext::Stream::getDecodedDecalage() const noexcept +{ + constexpr size_t overlap = 128; // The overlap + return getDecodedChunkSize() - overlap; +} + +int +AudioContext::Stream::getStreamIndex() const noexcept +{ + return streamIndexInAudioContext; } diff --git a/src/Lib/Audio.hh b/src/Lib/Audio.hh index ab6e9a13b36d6fbf348bce4aa5efb0a574de447b..72b67d288ea24fe93cf160e9f244ee4b7f733a75 100644 --- a/src/Lib/Audio.hh +++ b/src/Lib/Audio.hh @@ -64,7 +64,7 @@ public: public: Stream(AVCodec *, AVFormatContext *, AVStream *, int index); - ~Stream() = default; + ~Stream() noexcept; // The non-owning view of the stream's data using DataWeakPtr = std::weak_ptr<double[]>; @@ -75,7 +75,9 @@ public: bool isDecoded() const noexcept; int getDataSampleRate() const noexcept; size_t getDecodedDataSize() const noexcept; - DataWeakPtr getDecodedData() const noexcept; + size_t getDecodedChunkSize() const noexcept; + size_t getDecodedDecalage() const noexcept; + double *getDecodedData() const noexcept; // Some getters int getChannels() const noexcept; @@ -84,6 +86,9 @@ public: AVCodecID getCodecId() const noexcept; qint64 getBitRate() const noexcept; + // Get the index from the audio context + int getStreamIndex() const noexcept; + QJsonObject getProperties() const noexcept; private: @@ -103,8 +108,7 @@ public: AVFormatContext *dataFormat; SwrContextPtr dataSwrContext{ swr_alloc(), swrContenxtDeleter }; AVFramePtr dataFrame{ av_frame_alloc(), avFrameDeleter }; - DataPtr dataPtr{ nullptr, dataDeleter }; // Holds dataRealPtr - double *dataRealPtr{ nullptr }; + double *dataPtr{ nullptr }; size_t dataSize{ 0 }; // Constants for the resampler @@ -125,6 +129,7 @@ public: int getStreamCount() const noexcept; StreamWeakPtr getStream(int) const noexcept; + StreamWeakPtr getDefaultStream() const noexcept; QString getElementName() const noexcept; QJsonDocument getProperties() const noexcept; @@ -143,6 +148,8 @@ private: QVector<uint> audioStreamIndexes{}; // Index all the audio streams of the file QMap<uint, StreamPtr> audioStreams{}; // THe audio streams of the file + int defaultStreamIndex{ -1 }; + // Spare always null shared pointer, to be used when the audioStream[i] was // not found. StreamPtr spareNullSreamPtr{ nullptr };