diff --git a/YUViewLib/src/common/Functions.cpp b/YUViewLib/src/common/Functions.cpp index 07a7b7b0f..ce6d6ebdf 100644 --- a/YUViewLib/src/common/Functions.cpp +++ b/YUViewLib/src/common/Functions.cpp @@ -168,6 +168,16 @@ std::string toLower(std::string str) return str; } +ByteVector readData(std::istream &istream, const size_t nrBytes) +{ + ByteVector data; + data.resize(nrBytes); + istream.read(reinterpret_cast(data.data()), nrBytes); + const auto nrBytesActuallyRead = istream.gcount(); + data.resize(nrBytesActuallyRead); + return data; +} + std::optional toUnsigned(const std::string &text) { try diff --git a/YUViewLib/src/common/Functions.h b/YUViewLib/src/common/Functions.h index 667c7fe41..a06553ea6 100644 --- a/YUViewLib/src/common/Functions.h +++ b/YUViewLib/src/common/Functions.h @@ -34,6 +34,7 @@ #include +#include #include namespace functions @@ -64,6 +65,7 @@ QString formatDataSize(double size, bool isBits = false); QStringList toQStringList(const std::vector &stringVec); std::string toLower(std::string str); +ByteVector readData(std::istream &istream, const size_t nrBytes); inline std::string boolToString(bool b) { diff --git a/YUViewLib/src/ffmpeg/AVInputFormatWrapper.cpp b/YUViewLib/src/ffmpeg/AVInputFormatWrapper.cpp index 7362e3372..0193e9145 100644 --- a/YUViewLib/src/ffmpeg/AVInputFormatWrapper.cpp +++ b/YUViewLib/src/ffmpeg/AVInputFormatWrapper.cpp @@ -65,6 +65,31 @@ AVInputFormatWrapper::AVInputFormatWrapper(AVInputFormat *f, LibraryVersion v) this->update(); } +QString AVInputFormatWrapper::getName() const +{ + return this->name; +} + +QString AVInputFormatWrapper::getLongName() const +{ + return this->long_name; +} + +int AVInputFormatWrapper::getFlags() const +{ + return this->flags; +} + +QString AVInputFormatWrapper::getExtensions() const +{ + return this->extensions; +} + +QString AVInputFormatWrapper::getMimeType() const +{ + return this->mime_type; +} + void AVInputFormatWrapper::update() { if (this->fmt == nullptr) diff --git a/YUViewLib/src/ffmpeg/AVInputFormatWrapper.h b/YUViewLib/src/ffmpeg/AVInputFormatWrapper.h index ec1ebdf2c..c9fdda8d6 100644 --- a/YUViewLib/src/ffmpeg/AVInputFormatWrapper.h +++ b/YUViewLib/src/ffmpeg/AVInputFormatWrapper.h @@ -43,6 +43,12 @@ class AVInputFormatWrapper AVInputFormatWrapper(); AVInputFormatWrapper(AVInputFormat *f, LibraryVersion v); + QString getName() const; + QString getLongName() const; + int getFlags() const; + QString getExtensions() const; + QString getMimeType() const; + explicit operator bool() const { return fmt != nullptr; }; private: diff --git a/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp b/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp index 4e34b631b..6776e8d92 100644 --- a/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp +++ b/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -56,8 +57,18 @@ namespace auto startCode = QByteArrayLiteral("\x00\x00\x01"); +uint64_t getBoxSize(ByteVector::const_iterator iterator) +{ + uint64_t size = 0; + size += static_cast(*(iterator++)) << (8 * 3); + size += static_cast(*(iterator++)) << (8 * 2); + size += static_cast(*(iterator++)) << (8 * 1); + size += static_cast(*iterator); + return size; } +} // namespace + FileSourceFFmpegFile::FileSourceFFmpegFile() { connect(&this->fileWatcher, @@ -217,6 +228,55 @@ StringPairVec FileSourceFFmpegFile::getMetadata() return ff.getDictionaryEntries(this->formatCtx.getMetadata(), "", 0); } +ByteVector FileSourceFFmpegFile::getLhvCData() +{ + const auto inputFormat = this->formatCtx.getInputFormat(); + const auto isMp4 = inputFormat.getName().contains("mp4"); + if (!this->getVideoStreamCodecID().isHEVC() || !isMp4) + return {}; + + // This is a bit of a hack. The problem is that FFmpeg can currently not extract this for us. + // Maybe this will be added in the future. So the only option we have here is to manually extract + // the lhvC data from the mp4 file. In mp4, the boxes can be at the beginning or at the end of the + // file. + enum class SearchPosition + { + Beginning, + End + }; + + std::ifstream inputFile(this->fileName.toStdString(), std::ios::binary); + for (const auto searchPosition : {SearchPosition::Beginning, SearchPosition::End}) + { + constexpr auto NR_SEARCH_BYTES = 5120; + + if (searchPosition == SearchPosition::End) + inputFile.seekg(-NR_SEARCH_BYTES, std::ios_base::end); + + const auto rawFileData = functions::readData(inputFile, NR_SEARCH_BYTES); + if (rawFileData.empty()) + continue; + + const std::string searchString = "lhvC"; + auto lhvcPos = std::search( + rawFileData.begin(), rawFileData.end(), searchString.begin(), searchString.end()); + if (lhvcPos == rawFileData.end()) + continue; + + if (std::distance(rawFileData.begin(), lhvcPos) < 4) + continue; + + const auto boxSize = getBoxSize(lhvcPos - 4); + if (boxSize == 0 || boxSize > std::distance(lhvcPos, rawFileData.end())) + continue; + + // We just return the payload without the box size or the "lhvC" tag + return ByteVector(lhvcPos + 4, lhvcPos + boxSize - 4); + } + + return {}; +} + QList FileSourceFFmpegFile::getParameterSets() { if (!this->isFileOpened) @@ -484,6 +544,7 @@ void FileSourceFFmpegFile::openFileAndFindVideoStream(QString fileName) if (!this->ff.openInput(this->formatCtx, fileName)) return; + this->fileName = fileName; this->formatCtx.getInputFormat(); for (unsigned idx = 0; idx < this->formatCtx.getNbStreams(); idx++) diff --git a/YUViewLib/src/filesource/FileSourceFFmpegFile.h b/YUViewLib/src/filesource/FileSourceFFmpegFile.h index 0505def37..acc51d4cd 100644 --- a/YUViewLib/src/filesource/FileSourceFFmpegFile.h +++ b/YUViewLib/src/filesource/FileSourceFFmpegFile.h @@ -35,6 +35,7 @@ #include "FileSource.h" #include #include +#include #include #include #include