diff --git a/include/exiv2/asfvideo.hpp b/include/exiv2/asfvideo.hpp index 592eff50..7ca61e71 100644 --- a/include/exiv2/asfvideo.hpp +++ b/include/exiv2/asfvideo.hpp @@ -1,31 +1,7 @@ -// ***************************************************************** -*- C++ -*- -/* - * Copyright (C) 2004-2021 Exiv2 authors - * This program is part of the Exiv2 distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. - */ -/*! - @file asfvideo.hpp - @brief An Image subclass to support ASF video files - @author Abhinav Badola for GSoC 2012 - mail.abu.to@gmail.com - @date 08-Aug-12, AB: created - */ -#ifndef ASFVIDEO_HPP -#define ASFVIDEO_HPP +// SPDX-License-Identifier: GPL-2.0-or-later +// Spec : Advanced Systems Format (ASF) Specification : Revision 01.20.05 : +// https://exse.eyewated.com/fls/54b3ed95bbfb1a92.pdf +#pragma once // ***************************************************************************** #include "exiv2lib_export.h" @@ -72,10 +48,29 @@ class EXIV2API AsfVideo : public Image { [[nodiscard]] std::string mimeType() const override; //@} private: - static constexpr size_t ASF_TAG_SIZE = 0x4; - static constexpr size_t BUFF_MIN_SIZE = 0x8; - static constexpr size_t GUI_SIZE = 16; - static constexpr size_t GUID_SIZE = 37; + static constexpr size_t CODEC_TYPE_VIDEO = 1; + static constexpr size_t CODEC_TYPE_AUDIO = 2; + + class HeaderReader { + DataBuf IdBuf_; + uint64_t size_; + uint64_t remaining_size_; + + public: + explicit HeaderReader(BasicIo::UniquePtr& io); + + [[nodiscard]] uint64_t getSize() const { + return size_; + } + + [[nodiscard]] uint64_t getRemainingSize() const { + return remaining_size_; + } + + [[nodiscard]] DataBuf& getId() { + return IdBuf_; + } + }; protected: /*! @@ -83,15 +78,6 @@ class EXIV2API AsfVideo : public Image { position. Calls tagDecoder() or skips to next tag, if required. */ void decodeBlock(); - /*! - @brief Interpret tag information, and call the respective function - to save it in the respective XMP container. Decodes a Tag - Information and saves it in the respective XMP container, if - the block size is small. - @param tv Pointer to current tag, - @param size Size of the data block used to store Tag Information. - */ - void tagDecoder(uint64_t size); /*! @brief Interpret File_Properties tag information, and save it in the respective XMP container. @@ -112,27 +98,26 @@ class EXIV2API AsfVideo : public Image { in the respective XMP container. @param size Size of the data block used to store Tag Data. */ - void contentDescription(uint64_t size); + void contentDescription(); /*! @brief Interpret Extended_Stream_Properties tag information, and save it in the respective XMP container. - @param size Size of the data block used to store Tag Data. */ - void extendedStreamProperties(uint64_t size); + void extendedStreamProperties(); /*! @brief Interpret Header_Extension tag information, and save it in the respective XMP container. - @param size Size of the data block used to store Tag Data. */ - void headerExtension(uint64_t size); + void headerExtension(); /*! @brief Interpret Metadata, Extended_Content_Description, Metadata_Library tag information, and save it in the respective XMP container. - @param meta A default integer which helps to overload the function - for various Tags that have a similar method of decoding. */ - void metadataHandler(int meta = 1); + void extendedContentDescription(); + + void DegradableJPEGMedia(); + /*! @brief Calculates Aspect Ratio of a video, and stores it in the respective XMP container. @@ -140,12 +125,6 @@ class EXIV2API AsfVideo : public Image { void aspectRatio(); private: - //! Variable to check the end of metadata traversing. - bool continueTraversing_; - //! Variable which stores current position of the read pointer. - uint64_t localPosition_; - //! Variable which stores current stream being processsed. - int streamNumber_; //! Variable to store height and width of a video frame. uint64_t height_, width_; @@ -165,7 +144,4 @@ EXIV2API Image::UniquePtr newAsfInstance(BasicIo::UniquePtr io, bool create); //! Check if the file iIo is a Windows Asf Video. EXIV2API bool isAsfType(BasicIo& iIo, bool advance); - } // namespace Exiv2 - -#endif // #ifndef ASFVIDEO_HPP_ diff --git a/include/exiv2/riffvideo.hpp b/include/exiv2/riffvideo.hpp index 5d73e780..3c6c2605 100644 --- a/include/exiv2/riffvideo.hpp +++ b/include/exiv2/riffvideo.hpp @@ -1,34 +1,10 @@ -// ***************************************************************** -*- C++ -*- -/* - * Copyright (C) 2004-2021 Exiv2 authors - * This program is part of the Exiv2 distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. - */ -#ifndef RIFFVIDEO_HPP_ -#define RIFFVIDEO_HPP_ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once -// ***************************************************************************** -#include "exiv2lib_export.h" - -// included header files #include "exif.hpp" +#include "exiv2lib_export.h" #include "image.hpp" -// ***************************************************************************** -// namespace extensions namespace Exiv2 { // ***************************************************************************** @@ -57,7 +33,6 @@ class EXIV2API RiffVideo : public Image { //! @name Manipulators //@{ - void printStructure(std::ostream& out, PrintStructureOption option, size_t depth) override; void readMetadata() override; void writeMetadata() override; //@} @@ -65,128 +40,140 @@ class EXIV2API RiffVideo : public Image { //! @name Accessors //@{ [[nodiscard]] std::string mimeType() const override; - [[nodiscard]] static const char* printAudioEncoding(uint64_t i); //@} protected: + class HeaderReader { + std::string id_ = ""; + uint64_t size_ = 0; + + public: + explicit HeaderReader(BasicIo::UniquePtr& io); + + [[nodiscard]] uint64_t getSize() const { + return size_; + } + + [[nodiscard]] std::string& getId() { + return id_; + } + }; + + void readList(HeaderReader& header_); + + void readChunk(HeaderReader& header_); + + void decodeBlocks(); + + private: + bool equal(const std::string& str1, const std::string& str2); + /*! - @brief Check for a valid tag and decode the block at the current IO - position. Calls tagDecoder() or skips to next tag, if required. - */ - void decodeBlock(); - /*! - @brief Interpret tag information, and call the respective function - to save it in the respective XMP container. Decodes a Tag - Information and saves it in the respective XMP container, if - the block size is small. - @param buf Data buffer which cotains tag ID. - @param size Size of the data block used to store Tag Information. - */ - void tagDecoder(Exiv2::DataBuf& buf, size_t size); - /*! - @brief Interpret Junk tag information, and save it - in the respective XMP container. - @param size Size of the data block used to store Tag Information. - */ - void junkHandler(size_t size); - /*! - @brief Interpret Stream tag information, and save it - in the respective XMP container. - @param size Size of the data block used to store Tag Information. - */ - void streamHandler(size_t size); + @brief Interpret MainAVIHeader (avih) structure, and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void readAviHeader(); + /*! - @brief Interpret Stream Format tag information, and save it - in the respective XMP container. - @param size Size of the data block used to store Tag Information. - */ - void streamFormatHandler(size_t size); + @brief Interpret stream header list element (strh), and save it in the respective XMP container. + */ + void readStreamHeader(); + /*! - @brief Interpret Riff Header tag information, and save it - in the respective XMP container. - @param size Size of the data block used to store Tag Information. - */ - void aviHeaderTagsHandler(size_t size); + @brief Interpret stream header list element (strf), and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void readStreamFormat(uint64_t size_); + /*! - @brief Interpret Riff List tag information, and save it - in the respective XMP container. - @param size Size of the data block used to store Tag Information. - */ - void listHandler(size_t size); + @brief Interpret Additional header data (strd), and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void readStreamData(uint64_t size_); + /*! - @brief Interpret Riff Stream Data tag information, and save it - in the respective XMP container. - @param size Size of the data block used to store Tag Information. - */ - void streamDataTagHandler(size_t size); + @brief Interpret stream header list element (strn) , and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void StreamName(uint64_t size_); /*! - @brief Interpret INFO tag information, and save it - in the respective XMP container. - */ - void infoTagsHandler(); + @brief Interpret INFO List Chunk, and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void readInfoListChunk(uint64_t size_); + /*! - @brief Interpret Nikon Tags related to Video information, and - save it in the respective XMP container. - */ - void nikonTagsHandler(); + @brief Interpret Riff Stream Data tag information, and save it in the respective XMP container. + The Movi - Lists contain Video, Audio, Subtitle and (secondary) index data. Those can be grouped into rec - Lists. + @param size Size of the data block used to store Tag Information. + */ + void readMoviList(uint64_t size_); /*! - @brief Interpret OpenDML tag information, and save it - in the respective XMP container. - */ - void odmlTagsHandler(); - //! @brief Skips Particular Blocks of Metadata List. - void skipListData(); + @brief Interpret Video Properties Header chunk, and save it in the respective XMP container. + The video properties header identifies video signal properties associated with a digital video stream in an AVI file + @param size Size of the data block used to store Tag Information. + */ + void readVPRPChunk(uint64_t size_); /*! - @brief Interprets DateTimeOriginal tag or stream name tag - information, and save it in the respective XMP container. - @param size Size of the data block used to store Tag Information. - @param i parameter used to overload function - */ - void dateTimeOriginal(size_t size, int i = 0); + @brief Interpret Riff INdex Chunk, and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void readIndexChunk(uint64_t size_); /*! - @brief Calculates Sample Rate of a particular stream. - @param buf Data buffer with the dividend. - @param divisor The Divisor required to calculate sample rate. - @return Return the sample rate of the stream. - */ - [[nodiscard]] static double returnSampleRate(Exiv2::DataBuf& buf, size_t divisor = 1); + @brief Interpret Riff Stream Chunk, and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void readDataChunk(uint64_t size_); /*! - @brief Calculates Aspect Ratio of a video, and stores it in the - respective XMP container. - @param width Width of the video. - @param height Height of the video. - */ - void fillAspectRatio(size_t width = 1, size_t height = 1); + @brief Interpret Junk Chunk and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void readJunk(uint64_t size_); + + std::string getStreamType(uint32_t stream); /*! - @brief Calculates Duration of a video, and stores it in the - respective XMP container. - @param frame_rate Frame rate of the video. - @param frame_count Total number of frames present in the video. - */ + @brief Calculates Duration of a video, and stores it in the respective XMP container. + @param frame_rate Frame rate of the video. + @param frame_count Total number of frames present in the video. + */ void fillDuration(double frame_rate, size_t frame_count); - [[nodiscard]] static bool equalsRiffTag(Exiv2::DataBuf& buf, const char* str); - - static void copyTagValue(DataBuf& buf_dest, DataBuf& buf_src, size_t index = RIFF_TAG_SIZE); + /*! + @brief Calculates Aspect Ratio of a video, and stores it in the respective XMP container. + @param width Width of the video. + @param height Height of the video. + */ + void fillAspectRatio(size_t width, size_t height); + + static constexpr auto CHUNK_HEADER_ICCP = "ICCP"; + static constexpr auto CHUNK_HEADER_EXIF = "EXIF"; + static constexpr auto CHUNK_HEADER_XMP = "XMP "; + + /* Chunk header names */ + static constexpr auto CHUNK_ID_MOVI = "MOVI"; + static constexpr auto CHUNK_ID_DATA = "DATA"; + static constexpr auto CHUNK_ID_HDRL = "HDRL"; + static constexpr auto CHUNK_ID_STRL = "STRL"; + static constexpr auto CHUNK_ID_LIST = "LIST"; + static constexpr auto CHUNK_ID_JUNK = "JUNK"; + static constexpr auto CHUNK_ID_AVIH = "AVIH"; + static constexpr auto CHUNK_ID_STRH = "STRH"; + static constexpr auto CHUNK_ID_STRF = "STRF"; + static constexpr auto CHUNK_ID_FMT = "FMT "; + static constexpr auto CHUNK_ID_STRN = "STRN"; + static constexpr auto CHUNK_ID_STRD = "STRD"; + static constexpr auto CHUNK_ID_IDIT = "IDIT"; + static constexpr auto CHUNK_ID_INFO = "INFO"; + static constexpr auto CHUNK_ID_NCDT = "NCDT"; + static constexpr auto CHUNK_ID_ODML = "ODML"; + static constexpr auto CHUNK_ID_VPRP = "VPRP"; + static constexpr auto CHUNK_ID_IDX1 = "IDX1"; - private: - static constexpr size_t RIFF_TAG_SIZE = 0x4; - static constexpr auto RIFF_CHUNK_HEADER_ICCP = "ICCP"; - static constexpr auto RIFF_CHUNK_HEADER_EXIF = "EXIF"; - static constexpr auto RIFF_CHUNK_HEADER_XMP = "XMP "; - //! Variable to check the end of metadata traversing. - bool continueTraversing_; - //! Variable which stores current stream being processsed. int streamType_; }; // Class RiffVideo -// ***************************************************************************** -// template, inline and free functions - -// These could be static private functions on Image subclasses but then -// ImageFactory needs to be made a friend. -/*! +/* @brief Create a new RiffVideo instance and return an auto-pointer to it. Caller owns the returned object and the auto-pointer ensures that it will be deleted. @@ -197,5 +184,3 @@ EXIV2API Image::UniquePtr newRiffInstance(BasicIo::UniquePtr io, bool create); EXIV2API bool isRiffType(BasicIo& iIo, bool advance); } // namespace Exiv2 - -#endif // RIFFVIDEO_HPP_ diff --git a/src/asfvideo.cpp b/src/asfvideo.cpp index fbf3158e..f6cee7b8 100644 --- a/src/asfvideo.cpp +++ b/src/asfvideo.cpp @@ -1,62 +1,27 @@ -// ***************************************************************** -*- C++ -*- -/* - * Copyright (C) 2004-2021 Exiv2 authors - * This program is part of the Exiv2 distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. - */ -/* - File: asfvideo.cpp - Author(s): Abhinav Badola for GSoC 2012 (AB) - History: 08-Aug-12, AB: created - Credits: See header file - */ -// ***************************************************************************** +// SPDX-License-Identifier: GPL-2.0-or-later // included header files -#include -#include "config.h" - #include "asfvideo.hpp" +#include #include "basicio.hpp" -#include "convert.hpp" +#include "config.h" +#include "enforce.hpp" #include "error.hpp" #include "futils.hpp" #include "helper_functions.hpp" -#include "tags.hpp" -#include "tags_int.hpp" -#include "types.hpp" - -// + standard includes -#include -#include -#include - // ***************************************************************************** // class member definitions namespace Exiv2::Internal { /*! - TagVocabulary Look-up list for ASF Type Video Files - Associates the GUID of a TagVocabulary with its TagVocabulary Name(i.e. Human Readable Form) + Look-up list for ASF Type Video Files + Associates the GUID with its Name(i.e. Human Readable Form) Tags have been diferentiated into Various Categories. - The categories have been listed above the TagVocabulary Groups + The categories have been listed above Groups see : - https://fr.wikipedia.org/wiki/Advanced_Systems_Format - https://exse.eyewated.com/fls/54b3ed95bbfb1a92.pdf */ -constexpr const TagVocabulary GUIDReferenceTags[] = { +const std::map GUIDReferenceTags = { /// Top-level ASF object GUIDS {"75B22630-668E-11CF-A6D9-00AA0062CE6C", "Header"}, {"75B22636-668E-11CF-A6D9-00AA0062CE6C", "Data"}, @@ -148,63 +113,56 @@ constexpr const TagVocabulary GUIDReferenceTags[] = { {"6698B84E-0AFA-4330-AEB2-1C0A98D7A44D", "Payload_Extension_System_Encryption_Sample_ID"}, {"00E1AF06-7BEC-11D1-A582-00C04FC29CFB", "Payload_Extension_System_Degradable_JPEG"}}; -constexpr const TagDetails filePropertiesTags[] = {{7, "Xmp.video.FileLength"}, {6, "Xmp.video.CreationDate"}, - {5, "Xmp.video.DataPackets"}, {4, "Xmp.video.Duration"}, - {3, "Xmp.video.SendDuration"}, {2, "Xmp.video.Preroll"}, - {1, "Xmp.video.MaxBitRate"}}; - -constexpr const TagDetails contentDescriptionTags[] = {{0, "Xmp.video.Title"}, - {1, "Xmp.video.Author"}, - {2, "Xmp.video.Copyright"}, - {3, "Xmp.video.Description"}, - {4, "Xmp.video.Rating"}}; - -/*! - @brief Function used to check equality of two Tags (ignores case). - @param str1 char* Pointer to First TagVocabulary - @param str2 char* Pointer to Second TagVocabulary - @return Returns true if both are equal. - */ -bool compareTag(const char* str1, const char* str2) { - if (strlen(str1) != strlen(str2)) - return false; - - for (uint64_t i = 0; i < strlen(str1); ++i) - if (tolower(str1[i]) != tolower(str2[i])) - return false; - - return true; -} - /*! @brief Function used to calculate GUID, Tags comprises of 16 bytes. The Buffer contains the TagVocabulary in Binary Form. The information is then parsed into a character array GUID. + https://fr.wikipedia.org/wiki/Globally_unique_identifier */ -void getGUID(const byte buf[], char GUID[]) { - int i; - for (i = 0; i < 4; ++i) { - GUID[(3 - i) * 2] = Util::returnHEX(buf[i] / 0x10); - GUID[(3 - i) * 2 + 1] = Util::returnHEX(buf[i] % 0x10); - } - for (i = 4; i < 6; ++i) { - GUID[(9 - i) * 2 + 1] = Util::returnHEX(buf[i] / 0x10); - GUID[(9 - i) * 2 + 2] = Util::returnHEX(buf[i] % 0x10); - } - for (i = 6; i < 8; ++i) { - GUID[(14 - i) * 2] = Util::returnHEX(buf[i] / 0x10); - GUID[(14 - i) * 2 + 1] = Util::returnHEX(buf[i] % 0x10); - } - for (i = 8; i < 10; ++i) { - GUID[i * 2 + 3] = Util::returnHEX(buf[i] / 0x10); - GUID[i * 2 + 4] = Util::returnHEX(buf[i] % 0x10); - } - for (i = 10; i < 16; ++i) { - GUID[i * 2 + 4] = Util::returnHEX(buf[i] / 0x10); - GUID[i * 2 + 5] = Util::returnHEX(buf[i] % 0x10); + +std::string getGUID(DataBuf& buf) { + std::string GUID(36, '-'); + if (buf.size() >= 16) { + GUID.at(0) = returnHex(buf.data()[3] / 0x10); + GUID.at(1) = returnHex(buf.data()[3] % 0x10); + GUID.at(2) = returnHex(buf.data()[2] / 0x10); + GUID.at(3) = returnHex(buf.data()[2] % 0x10); + GUID.at(4) = returnHex(buf.data()[1] / 0x10); + GUID.at(5) = returnHex(buf.data()[1] % 0x10); + GUID.at(6) = returnHex(buf.data()[0] / 0x10); + GUID.at(7) = returnHex(buf.data()[0] % 0x10); + + GUID.at(9) = returnHex(buf.data()[5] / 0x10); + GUID.at(10) = returnHex(buf.data()[5] % 0x10); + GUID.at(11) = returnHex(buf.data()[4] / 0x10); + GUID.at(12) = returnHex(buf.data()[4] % 0x10); + + GUID.at(14) = returnHex(buf.data()[7] / 0x10); + GUID.at(15) = returnHex(buf.data()[7] % 0x10); + GUID.at(16) = returnHex(buf.data()[6] / 0x10); + GUID.at(17) = returnHex(buf.data()[6] % 0x10); + + GUID.at(19) = returnHex(buf.data()[8] / 0x10); + GUID.at(20) = returnHex(buf.data()[8] % 0x10); + GUID.at(21) = returnHex(buf.data()[9] / 0x10); + GUID.at(22) = returnHex(buf.data()[9] % 0x10); + + GUID.at(24) = returnHex(buf.data()[10] / 0x10); + GUID.at(25) = returnHex(buf.data()[10] % 0x10); + GUID.at(26) = returnHex(buf.data()[11] / 0x10); + GUID.at(27) = returnHex(buf.data()[11] % 0x10); + GUID.at(28) = returnHex(buf.data()[12] / 0x10); + GUID.at(29) = returnHex(buf.data()[12] % 0x10); + GUID.at(30) = returnHex(buf.data()[13] / 0x10); + GUID.at(31) = returnHex(buf.data()[13] % 0x10); + GUID.at(32) = returnHex(buf.data()[14] / 0x10); + GUID.at(33) = returnHex(buf.data()[14] % 0x10); + GUID.at(34) = returnHex(buf.data()[15] / 0x10); + GUID.at(35) = returnHex(buf.data()[15] % 0x10); } - GUID[36] = '\0'; - GUID[8] = GUID[13] = GUID[18] = GUID[23] = '-'; + + // Example of output 399595EC-8667-4E2D-8FDB-98814CE76C1E + return GUID; } /*! @@ -248,407 +206,267 @@ void AsfVideo::readMetadata() { IoCloser closer(*io_); clearMetadata(); - continueTraversing_ = true; io_->seek(0, BasicIo::beg); height_ = width_ = 1; xmpData()["Xmp.video.FileSize"] = io_->size() / 1048576.; - xmpData()["Xmp.video.FileName"] = io_->path(); xmpData()["Xmp.video.MimeType"] = mimeType(); - while (continueTraversing_) - decodeBlock(); + decodeBlock(); aspectRatio(); } // AsfVideo::readMetadata -void AsfVideo::decodeBlock() { - DataBuf buf(BUFF_MIN_SIZE + 1); - uint64_t size = 0; - uint64_t cur_pos = io_->tell(); - - byte guidBuf[GUI_SIZE]; - io_->read(guidBuf, GUI_SIZE); +AsfVideo::HeaderReader::HeaderReader(BasicIo::UniquePtr& io) : IdBuf_(GUID) { + if (io->size() >= io->tell() + GUID + QWORD) { + IdBuf_ = io->read(GUID); - if (io_->eof()) { - continueTraversing_ = false; - return; + size_ = readQWORDTag(io); + if (size_ >= GUID + QWORD) + remaining_size_ = size_ - GUID - QWORD; } +} - char GUID[GUID_SIZE] = ""; // the getGUID function write the GUID[36], - - io_->read(buf.data(), BUFF_MIN_SIZE); - size = Util::getUint64_t(buf); - - getGUID(guidBuf, GUID); - auto tv = Exiv2::find(GUIDReferenceTags, GUID); - if (tv) { - auto tagDecoder = [&](const Internal::TagVocabulary* tv, uint64_t size) { - uint64_t cur_pos = io_->tell(); - DataBuf buf(1000); - unsigned long count = 0, tempLength = 0; - Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::xmpSeq); - - if (compareTag(exvGettext(tv->label_), "Header")) { - localPosition_ = 0; - io_->read(buf.data(), 4); - io_->read(buf.data(), 2); - - while (localPosition_ < cur_pos + size) - decodeBlock(); +void AsfVideo::decodeBlock() { + HeaderReader header(io_); + std::string guid = getGUID(header.getId()); + + auto tv = GUIDReferenceTags.find(guid); + if (tv != GUIDReferenceTags.end()) { + if (tv->second == "Header") { + DataBuf nbHeadersBuf(DWORD + 1); + io_->read(nbHeadersBuf.data(), DWORD); + + uint32_t nb_headers = Exiv2::getULong(nbHeadersBuf.data(), littleEndian); + io_->seekOrThrow(io_->tell() + BYTE * 2, BasicIo::beg, + ErrorCode::kerFailedToReadImageData); // skip two reserved tags + for (uint32_t i = 0; i < nb_headers; i++) { + HeaderReader others(io_); + auto guid = getGUID(others.getId()); + auto tag = GUIDReferenceTags.find(guid); + if (tag != GUIDReferenceTags.end()) { + if (tag->second == "File_Properties") + fileProperties(); + else if (tag->second == "Stream_Properties") + streamProperties(); + else if (tag->second == "Header_Extension") + headerExtension(); + else if (tag->second == "Codec_List") + codecList(); + else if (tag->second == "Extended_Content_Description") + extendedContentDescription(); + else if (tag->second == "Content_Description") + contentDescription(); + else if (tag->second == "Extended_Stream_Properties") + extendedStreamProperties(); + else if (tag->second == "Degradable_JPEG_Media") { + DegradableJPEGMedia(); + } else + io_->seekOrThrow(io_->tell() + others.getRemainingSize(), BasicIo::beg, + ErrorCode::kerFailedToReadImageData); + } else + io_->seekOrThrow(io_->tell() + others.getRemainingSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData); } + } else + io_->seekOrThrow(io_->tell() + header.getRemainingSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData); - else if (compareTag(exvGettext(tv->label_), "File_Properties")) - fileProperties(); - - else if (compareTag(exvGettext(tv->label_), "Stream_Properties")) - streamProperties(); - - else if (compareTag(exvGettext(tv->label_), "Metadata")) - metadataHandler(1); - - else if (compareTag(exvGettext(tv->label_), "Extended_Content_Description")) - metadataHandler(2); - - else if (compareTag(exvGettext(tv->label_), "Metadata_Library")) - metadataHandler(3); - - else if (compareTag(exvGettext(tv->label_), "Codec_List")) - codecList(); - - else if (compareTag(exvGettext(tv->label_), "Content_Description")) - contentDescription(size); - - else if (compareTag(exvGettext(tv->label_), "Extended_Stream_Properties")) - extendedStreamProperties(size); - - else if (compareTag(exvGettext(tv->label_), "Header_Extension")) { - localPosition_ = 0; - headerExtension(size); - } + } else + io_->seekOrThrow(io_->tell() + header.getRemainingSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} // AsfVideo::decodeBlock - else if (compareTag(exvGettext(tv->label_), "Language_List")) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 2); - count = Exiv2::getUShort(buf.data(), littleEndian); +void AsfVideo::extendedStreamProperties() { + xmpData()["Xmp.video.StartTimecode"] = readQWORDTag(io_); // Start Time + xmpData()["Xmp.video.EndTimecode"] = readWORDTag(io_); // End Time + + io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Data Bitrate + io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Buffer Size + io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Initial Buffer Fullness + io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Alternate Data Bitrate + io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Alternate Buffer Size + io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Alternate Initial Buffer Fullness + io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Maximum Object Size + io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Flags Buffer Size + io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Flags Stream Number + io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Stream Language ID Index + + xmpData()["Xmp.video.FrameRate"] = readWORDTag(io_); // Average Time Per Frame + uint16_t stream_name_count = readWORDTag(io_); + uint16_t payload_ext_sys_count = readWORDTag(io_); + + for (uint16_t i = 0; i < stream_name_count; i++) { + io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Language ID Index + uint16_t stream_length = readWORDTag(io_); + if (stream_length) + io_->seek(io_->tell() + stream_length, BasicIo::beg); // ignore Stream name + } - while (count--) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 1); - tempLength = static_cast(buf.data()[0]); + for (uint16_t i = 0; i < payload_ext_sys_count; i++) { + io_->seek(io_->tell() + GUID, BasicIo::beg); // ignore Extension System ID + io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Extension Data Size + uint16_t ext_sys_info_length = readWORDTag(io_); + if (ext_sys_info_length) + io_->seek(io_->tell() + ext_sys_info_length, BasicIo::beg); // ignore Extension System Info + } +} // AsfVideo::extendedStreamProperties - io_->read(buf.data(), tempLength); - v->read(Util::toString16(buf)); - } - xmpData().add(Exiv2::XmpKey("Xmp.video.TrackLang"), v.get()); - } +void AsfVideo::DegradableJPEGMedia() { + uint32_t width = readDWORDTag(io_); + width_ = width; + xmpData_["Xmp.video.Width"] = width; - io_->seek(cur_pos + size, BasicIo::beg); - localPosition_ = io_->tell(); - }; // AsfVideo::tagDecoder + uint32_t height = readDWORDTag(io_); + height_ = height; + xmpData_["Xmp.video.Height"] = height; - tagDecoder(tv, size - 24); - } else - io_->seek(cur_pos + size, BasicIo::beg); + io_->seek(io_->tell() + WORD * 3 /*3 Reserved*/, BasicIo::beg); - localPosition_ = io_->tell(); -} // AsfVideo::decodeBlock + uint32_t interchange_data_length = readWORDTag(io_); + io_->seek(io_->tell() + interchange_data_length /*Interchange data*/, BasicIo::beg); +} +void AsfVideo::streamProperties() { + DataBuf streamTypedBuf = io_->read(GUID); -void AsfVideo::extendedStreamProperties(uint64_t size) { - uint64_t cur_pos = io_->tell(), avgTimePerFrame = 0; - DataBuf buf(BUFF_MIN_SIZE); - static int previousStream; - io_->seek(cur_pos + 48, BasicIo::beg); + auto stream_type = getGUID(streamTypedBuf); - io_->read(buf.data(), 2); - streamNumber_ = Exiv2::getUShort(buf.data(), littleEndian); + enum streamTypeInfo { Audio = 1, Video = 2 }; + int stream = 0; - io_->read(buf.data(), 2); - io_->read(buf.data(), BUFF_MIN_SIZE); - avgTimePerFrame = Util::getUint64_t(buf); + auto tag_stream_type = GUIDReferenceTags.find(stream_type); + if (tag_stream_type != GUIDReferenceTags.end()) { + if (tag_stream_type->second == "Audio_Media") + stream = Audio; + else if (tag_stream_type->second == "Video_Media") + stream = Video; - if (previousStream < streamNumber_ && avgTimePerFrame != 0) - xmpData()["Xmp.video.FrameRate"] = 10000000. / avgTimePerFrame; + io_->seek(io_->tell() + GUID, BasicIo::beg); // ignore Error Correction Type - previousStream = streamNumber_; - io_->seek(cur_pos + size, BasicIo::beg); -} // AsfVideo::extendedStreamProperties + uint64_t time_offset = readQWORDTag(io_); + if (stream == Video) + xmpData()["Xmp.video.TimeOffset"] = time_offset; + else if (stream == Audio) + xmpData()["Xmp.audio.TimeOffset"] = time_offset; -void AsfVideo::contentDescription(uint64_t size) { - const size_t pos = io_->tell(); - size_t length[5]; - for (size_t& i : length) { - byte buf[2]; - io_->read(buf, 2); - if (io_->error() || io_->eof()) - throw Error(ErrorCode::kerFailedToReadImageData); - i = getUShort(buf, littleEndian); - } - for (int i = 0; i < 5; ++i) { - DataBuf buf(length[i]); - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), length[i]); - if (io_->error() || io_->eof()) - throw Error(ErrorCode::kerFailedToReadImageData); - auto td = Exiv2::find(contentDescriptionTags, i); - assert(td); - std::string str(reinterpret_cast(buf.data()), length[i]); - if (convertStringCharset(str, "UCS-2LE", "UTF-8")) { - xmpData()[td->label_] = str; - } else { - xmpData()[td->label_] = Util::toString16(buf); - } - } - if (io_->seek(pos + size, BasicIo::beg)) - throw Error(ErrorCode::kerFailedToReadImageData); -} // AsfVideo::contentDescription + auto specific_data_length = readDWORDTag(io_); + auto correction_data_length = readDWORDTag(io_); -void AsfVideo::streamProperties() { - DataBuf buf(20); - byte guidBuf[GUI_SIZE]; - int stream = 0; - enum streamTypeInfo { Audio = 1, Video = 2 }; - io_->read(guidBuf, GUI_SIZE); - char streamType[GUID_SIZE] = ""; - - getGUID(guidBuf, streamType); - auto tv = Exiv2::find(GUIDReferenceTags, streamType); - io_->read(guidBuf, GUI_SIZE); - - if (compareTag(exvGettext(tv->label_), "Audio_Media")) - stream = Audio; - else if (compareTag(exvGettext(tv->label_), "Video_Media")) - stream = Video; - - io_->read(buf.data(), BUFF_MIN_SIZE); - if (stream == Video) - xmpData()["Xmp.video.TimeOffset"] = Util::getUint64_t(buf); - else if (stream == Audio) - xmpData()["Xmp.audio.TimeOffset"] = Util::getUint64_t(buf); - - io_->read(buf.data(), BUFF_MIN_SIZE); - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 1); - streamNumber_ = static_cast(buf.data()[0]) & 127; - - io_->read(buf.data(), 5); - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 2); - size_t temp = Exiv2::getUShort(buf.data(), littleEndian); - - if (stream == 2) { - xmpData()["Xmp.video.Width"] = temp; - width_ = temp; - } else if (stream == Audio) { - // todo xmpData()["Xmp.audio.Codec"] + io_->seek(io_->tell() + WORD /*Flags*/ + DWORD /*Reserved*/ + specific_data_length + correction_data_length, + BasicIo::beg); } - io_->read(buf.data(), 2); - temp = Exiv2::getUShort(buf.data(), littleEndian); - if (stream == Audio) - xmpData()["Xmp.audio.ChannelType"] = temp; - - io_->read(buf.data(), 4); - temp = Exiv2::getULong(buf.data(), littleEndian); - - if (stream == Video) { - xmpData()["Xmp.video.Height"] = temp; - height_ = temp; - } else if (stream == Audio) { - xmpData()["Xmp.audio.SampleRate"] = temp; - } } // AsfVideo::streamProperties void AsfVideo::codecList() { - DataBuf buf(200); - io_->read(buf.data(), GUI_SIZE); - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 4); - int codecCount = Exiv2::getULong(buf.data(), littleEndian), descLength = 0, codecType = 0; - - while (codecCount--) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 2); - codecType = Exiv2::getUShort(buf.data(), littleEndian); - - io_->read(buf.data(), 2); - descLength = Exiv2::getUShort(buf.data(), littleEndian) * 2; - - io_->read(buf.data(), descLength); - if (codecType == 1) - xmpData()["Xmp.video.Codec"] = Util::toString16(buf); - else if (codecType == 2) - xmpData()["Xmp.audio.Compressor"] = Util::toString16(buf); - - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 2); - descLength = Exiv2::getUShort(buf.data(), littleEndian) * 2; - - io_->read(buf.data(), descLength); - - if (codecType == 1) - xmpData()["Xmp.video.CodecDescription"] = Util::toString16(buf); - else if (codecType == 2) - xmpData()["Xmp.audio.CodecDescription"] = Util::toString16(buf); - - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 2); - descLength = Exiv2::getUShort(buf.data(), littleEndian); - - io_->read(buf.data(), descLength); + io_->seek(io_->tell() + GUID /*reserved*/, BasicIo::beg); + auto entries_count = readDWORDTag(io_); + for (uint32_t i = 0; i < entries_count; i++) { + uint16_t codec_type = readWORDTag(io_) * 2; + std::string codec = (codec_type == 1) ? "Xmp.video" : "Xmp.audio"; + + uint16_t codec_name_length = readWORDTag(io_) * 2; + if (codec_name_length) + xmpData()[codec + std::string(".CodecName")] = readStringWcharTag(io_, codec_name_length); + + uint16_t codec_desc_length = readWORDTag(io_); + if (codec_desc_length) + xmpData()[codec + std::string(".CodecDescription")] = readStringWcharTag(io_, codec_desc_length); + + uint16_t codec_info_length = readWORDTag(io_); + Internal::enforce(codec_info_length && codec_info_length + io_->tell() < io_->size(), + Exiv2::ErrorCode::kerCorruptedMetadata); + xmpData()[codec + std::string(".CodecInfo")] = readStringTag(io_, codec_info_length); } } // AsfVideo::codecList -void AsfVideo::headerExtension(uint64_t size) { - uint64_t cur_pos = io_->tell(); - DataBuf buf(20); - io_->read(buf.data(), 18); - buf.data()[4] = '\0'; - io_->read(buf.data(), 4); - - while (localPosition_ < cur_pos + size) - decodeBlock(); - - io_->seek(cur_pos + size, BasicIo::beg); +void AsfVideo::headerExtension() { + io_->seek(io_->tell() + GUID /*reserved1*/ + WORD /*Reserved2*/, BasicIo::beg); + auto header_ext_data_length = readDWORDTag(io_); + io_->seek(io_->tell() + header_ext_data_length, BasicIo::beg); } // AsfVideo::headerExtension -void AsfVideo::metadataHandler(int meta) { - DataBuf buf(5000); - io_->read(buf.data(), 2); - uint16_t recordCount = Exiv2::getUShort(buf.data(), littleEndian), nameLength = 0, dataLength = 0, dataType = 0; - Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::xmpSeq); - byte guidBuf[GUI_SIZE]; - char fileID[GUID_SIZE] = ""; - - while (recordCount--) { - std::memset(buf.data(), 0x0, buf.size()); - - if (meta == 1 || meta == 3) { - io_->read(buf.data(), 4); - io_->read(buf.data(), 2); - nameLength = Exiv2::getUShort(buf.data(), littleEndian); - io_->read(buf.data(), 2); - dataType = Exiv2::getUShort(buf.data(), littleEndian); - io_->read(buf.data(), 4); - dataLength = Exiv2::getULong(buf.data(), littleEndian); - - if (nameLength > 5000) { -#ifndef SUPPRESS_WARNINGS - EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 " - << " entries considered invalid; not read.\n"; -#endif - io_->seek(io_->tell() + nameLength, BasicIo::beg); - } else { - io_->read(buf.data(), nameLength); - } - - v->read(Util::toString16(buf)); - if (dataType == 6) { - io_->read(guidBuf, GUI_SIZE); - getGUID(guidBuf, fileID); - } else { - // Sanity check with an "unreasonably" large number - if (dataLength > 5000) { -#ifndef SUPPRESS_WARNINGS - EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 " - << " entries considered invalid; not read.\n"; -#endif - io_->seek(io_->tell() + dataLength, BasicIo::beg); - } else - io_->read(buf.data(), dataLength); +void AsfVideo::extendedContentDescription() { + uint16_t content_descriptor_count = readWORDTag(io_); + std::string value; + + for (uint16_t i = 0; i < content_descriptor_count; i++) { + uint16_t descriptor_name_length = readWORDTag(io_); + if (descriptor_name_length) + value += readStringWcharTag(io_, descriptor_name_length); // Descriptor Name + + uint16_t descriptor_value_data_type = readWORDTag(io_); + uint16_t descriptor_value_length = readWORDTag(io_); + if (descriptor_value_length) { + // Descriptor Value + switch (descriptor_value_data_type) { + case 0 /*Unicode string */: + value += std::string(": ") + readStringWcharTag(io_, descriptor_value_length); + break; + case 1 /*BYTE array */: + value += std::string(": ") + readStringTag(io_, descriptor_value_length); + break; + case 2 /*BOOL*/: + value += std::string(": ") + std::to_string(readWORDTag(io_)); + break; + case 3 /*DWORD */: + value += std::string(": ") + std::to_string(readDWORDTag(io_)); + break; + case 4 /*QWORD */: + value += std::string(": ") + std::to_string(readQWORDTag(io_)); + break; + case 5 /*WORD*/: + value += std::string(": ") + std::to_string(readWORDTag(io_)); + ; + break; } } + value += std::string(", "); + } - else if (meta == 2) { - io_->read(buf.data(), 2); - nameLength = Exiv2::getUShort(buf.data(), littleEndian); - - if (nameLength > 5000) { -#ifndef SUPPRESS_WARNINGS - EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 " - << " entries considered invalid; not read.\n"; -#endif - io_->seek(io_->tell() + nameLength, BasicIo::beg); - } else { - io_->read(buf.data(), nameLength); - } - - v->read(Util::toString16(buf)); - - io_->read(buf.data(), 2); - dataType = Exiv2::getUShort(buf.data(), littleEndian); - - io_->read(buf.data(), 2); - dataLength = Exiv2::getUShort(buf.data(), littleEndian); - - // Sanity check with an "unreasonably" large number - if (dataLength > 5000) { -#ifndef SUPPRESS_WARNINGS - EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 " - << " entries considered invalid; not read.\n"; -#endif - io_->seek(io_->tell() + dataLength, BasicIo::beg); - } else - io_->read(buf.data(), dataLength); - } + xmpData()["Xmp.video.ExtendedContentDescription"] = value; +} // AsfVideo::extendedContentDescription - if (dataType == 0) { // Unicode String - v->read(Util::toString16(buf)); - } else if (dataType == 2 || dataType == 5) { // 16-bit Unsigned Integer - v->read(Exiv2::toString(Exiv2::getUShort(buf.data(), littleEndian))); - } else if (dataType == 3) { // 32-bit Unsigned Integer - v->read(Exiv2::toString(Exiv2::getULong(buf.data(), littleEndian))); - } else if (dataType == 4) { // 64-bit Unsigned Integer - v->read(Exiv2::toString(Util::getUint64_t(buf))); - } else if (dataType == 6) { // 128-bit GUID - v->read(Exiv2::toString(fileID)); - } else { // Byte array - v->read(Exiv2::toString(buf.data())); - } - } +void AsfVideo::contentDescription() { + uint16_t title_length = readWORDTag(io_); + uint16_t author_length = readWORDTag(io_); + uint16_t copyright_length = readWORDTag(io_); + uint16_t desc_length = readWORDTag(io_); + uint16_t rating_length = readWORDTag(io_); - if (meta == 1) { - xmpData().add(Exiv2::XmpKey("Xmp.video.Metadata"), v.get()); - } else if (meta == 2) { - xmpData().add(Exiv2::XmpKey("Xmp.video.ExtendedContentDescription"), v.get()); - } else { - xmpData().add(Exiv2::XmpKey("Xmp.video.MetadataLibrary"), v.get()); - } -} // AsfVideo::metadataHandler + if (title_length) + xmpData()["Xmp.video.Title"] = readStringWcharTag(io_, title_length); -void AsfVideo::fileProperties() { - DataBuf buf(BUFF_MIN_SIZE); + if (author_length) + xmpData()["Xmp.video.Author"] = readStringWcharTag(io_, author_length); - byte guidBuf[GUI_SIZE]; - io_->read(guidBuf, GUI_SIZE); - char fileID[GUID_SIZE] = ""; - int count = 7; - getGUID(guidBuf, fileID); - xmpData()["Xmp.video.FileID"] = fileID; + if (copyright_length) + xmpData()["Xmp.video.Copyright"] = readStringWcharTag(io_, copyright_length); - const TagDetails* td; + if (desc_length) + xmpData()["Xmp.video.Description"] = readStringWcharTag(io_, desc_length); - while (count--) { - td = Exiv2::find(filePropertiesTags, (count + 1)); - io_->read(buf.data(), BUFF_MIN_SIZE); + if (rating_length) + xmpData()["Xmp.video.Rating"] = readStringWcharTag(io_, rating_length); - if (count == 0) { - buf.data()[4] = '\0'; - io_->read(buf.data(), 4); - io_->read(buf.data(), 4); - } +} // AsfVideo::extendedContentDescription - if (count == 3 || count == 2) { - xmpData()[exvGettext(td->label_)] = Util::getUint64_t(buf) / 10000; - } else { - xmpData()[exvGettext(td->label_)] = Util::getUint64_t(buf); - } - } +void AsfVideo::fileProperties() { + DataBuf FileIddBuf = io_->read(GUID); + xmpData()["Xmp.video.FileID"] = getGUID(FileIddBuf); + xmpData()["Xmp.video.FileLength"] = readQWORDTag(io_); + xmpData()["Xmp.video.CreationDate"] = readQWORDTag(io_); + xmpData()["Xmp.video.DataPackets"] = readQWORDTag(io_); + xmpData()["Xmp.video.duration"] = readQWORDTag(io_); + xmpData()["Xmp.video.SendDuration"] = readQWORDTag(io_); + xmpData()["Xmp.video.Preroll"] = readQWORDTag(io_); + + io_->seek(io_->tell() + DWORD + DWORD + DWORD, BasicIo::beg); + xmpData()["Xmp.video.MaxBitRate"] = readDWORDTag(io_); } // AsfVideo::fileProperties void AsfVideo::aspectRatio() { // TODO - Make a better unified method to handle all cases of Aspect Ratio + if (!height_) + return; double aspectRatio = static_cast(width_) / height_; aspectRatio = floor(aspectRatio * 10) / 10; xmpData()["Xmp.video.AspectRatio"] = aspectRatio; @@ -692,9 +510,8 @@ Image::UniquePtr newAsfInstance(BasicIo::UniquePtr io, bool /*create*/) { } bool isAsfType(BasicIo& iIo, bool advance) { - const int32_t len = 16; - byte buf[len]; - iIo.read(buf, len); + byte buf[GUID]; + iIo.read(buf, GUID); if (iIo.error() || iIo.eof()) { return false; diff --git a/src/helper_functions.cpp b/src/helper_functions.cpp index a53f7471..a2825d5e 100644 --- a/src/helper_functions.cpp +++ b/src/helper_functions.cpp @@ -3,7 +3,10 @@ #include "helper_functions.hpp" #include +#include #include +#include +#include "enforce.hpp" std::string string_from_unterminated(const char* data, size_t data_length) { if (data_length == 0) { @@ -13,35 +16,53 @@ std::string string_from_unterminated(const char* data, size_t data_length) { return {data, StringLength}; } -namespace Util { -char returnHEX(int n) { +namespace Exiv2 { +char returnHex(int n) { if (n >= 0 && n <= 9) return static_cast(n + 48); return static_cast(n + 55); } -std::string toString16(Exiv2::DataBuf& buf) { - std::ostringstream os; - char t; +std::string utf16ToUtf8(const std::wstring& wstr) { + using convert_typeX = std::codecvt_utf8; + std::wstring_convert converterX; - for (size_t i = 0; i <= buf.size(); i += 2) { - t = buf.data()[i] + 16 * buf.data()[i + 1]; - if (t == 0) { - if (i) - os << '\0'; - break; - } - os << t; - } - return os.str(); + std::string str = converterX.to_bytes(wstr); + str.erase(std::remove(str.begin(), str.end(), '\0'), str.end()); + return str; } -uint64_t getUint64_t(Exiv2::DataBuf& buf) { - uint64_t temp = 0; +uint64_t readQWORDTag(BasicIo::UniquePtr& io) { + Internal::enforce(QWORD <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata); + DataBuf FieldBuf = io->read(QWORD); + return FieldBuf.read_uint64(0, littleEndian); +} - for (int i = 0; i < 8; ++i) { - temp = temp + static_cast(buf.data()[i] * (pow(static_cast(256), i))); - } - return temp; +uint32_t readDWORDTag(BasicIo::UniquePtr& io) { + Internal::enforce(DWORD <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata); + DataBuf FieldBuf = io->read(DWORD); + return FieldBuf.read_uint32(0, littleEndian); +} + +uint16_t readWORDTag(BasicIo::UniquePtr& io) { + Internal::enforce(WORD <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata); + DataBuf FieldBuf = io->read(WORD); + return FieldBuf.read_uint16(0, littleEndian); } -} // namespace Util + +std::string readStringWcharTag(BasicIo::UniquePtr& io, size_t length) { + Internal::enforce(length <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata); + DataBuf FieldBuf(length + 1); + io->readOrThrow(FieldBuf.data(), length, ErrorCode::kerFailedToReadImageData); + std::wstring wst(FieldBuf.begin(), FieldBuf.end()); + return utf16ToUtf8(wst); +} + +std::string readStringTag(BasicIo::UniquePtr& io, size_t length) { + Internal::enforce(length <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata); + DataBuf FieldBuf(length + 1); + io->readOrThrow(FieldBuf.data(), length, ErrorCode::kerFailedToReadImageData); + return Exiv2::toString(FieldBuf.data()).substr(0, length); +} + +} // namespace Exiv2 diff --git a/src/helper_functions.hpp b/src/helper_functions.hpp index 34562ee9..bc070df2 100644 --- a/src/helper_functions.hpp +++ b/src/helper_functions.hpp @@ -4,6 +4,7 @@ #define HELPER_FUNCTIONS_HPP #include +#include "basicio.hpp" #include "types.hpp" /*! @brief Convert a (potentially not null terminated) array into a @@ -22,24 +23,42 @@ */ std::string string_from_unterminated(const char* data, size_t data_length); -namespace Util { +namespace Exiv2 { /*! @brief Function used to convert a decimal number to its Hexadecimal equivalent, then parsed into a character @param n Integer which is to be parsed as Hexadecimal character @return Return a Hexadecimal number, in character */ -char returnHEX(int n); +char returnHex(int n); + +static constexpr size_t BYTE = 0x1; +static constexpr size_t WCHAR = 0x2; +static constexpr size_t WORD = 0X2; +static constexpr size_t DWORD = 0x4; +static constexpr size_t QWORD = 0x8; +static constexpr size_t GUID = 0x10; + +// @brief /*! - @brief Function used to read data from data buffer, reads 16-bit character - array and stores it in std::string object. - @param buf Exiv2 data buffer, which stores the information - @return Returns std::string object . - */ -std::string toString16(Exiv2::DataBuf& buf); + @brief The function utf16ToUtf8 takes a wide string wstr as input and converts it to a narrow string + The conversion is performed using the std::wstring_convert class template and a std::codecvt_utf8 facet, which + implements conversion between UTF-8 and wide characters. + @param wstr : wide string + @return Returns std::string object +*/ +std::string utf16ToUtf8(const std::wstring& wstr); + +[[nodiscard]] uint64_t readQWORDTag(Exiv2::BasicIo::UniquePtr& io); + +[[nodiscard]] uint32_t readDWORDTag(Exiv2::BasicIo::UniquePtr& io); + +[[nodiscard]] uint16_t readWORDTag(Exiv2::BasicIo::UniquePtr& io); + +[[nodiscard]] std::string readStringWcharTag(Exiv2::BasicIo::UniquePtr& io, size_t length); + +[[nodiscard]] std::string readStringTag(Exiv2::BasicIo::UniquePtr& io, size_t length = DWORD); -//! Function used to convert buffer data into 64-bit Integer, information stored in littleEndian format -uint64_t getUint64_t(Exiv2::DataBuf& buf); -} // namespace Util +} // namespace Exiv2 #endif // HELPER_FUNCTIONS_HPP diff --git a/src/matroskavideo.cpp b/src/matroskavideo.cpp index 5caf17e5..2b382dca 100644 --- a/src/matroskavideo.cpp +++ b/src/matroskavideo.cpp @@ -600,7 +600,6 @@ void MatroskaVideo::readMetadata() { continueTraversing_ = true; height_ = width_ = 1; - xmpData_["Xmp.video.FileName"] = io_->path(); xmpData_["Xmp.video.FileSize"] = io_->size() / bytesMB; xmpData_["Xmp.video.MimeType"] = mimeType(); diff --git a/src/riffvideo.cpp b/src/riffvideo.cpp index 8d57011d..61abf6d0 100644 --- a/src/riffvideo.cpp +++ b/src/riffvideo.cpp @@ -1,154 +1,106 @@ // ***************************************************************** -*- C++ -*- -/* - * Copyright (C) 2004-2021 Exiv2 authors - * This program is part of the Exiv2 distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * asize_t with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. +/* Spec: + * https://learn.microsoft.com/fr-fr/windows/win32/directshow/avi-riff-file-reference + * https://cdn.hackaday.io/files/274271173436768/avi.pdf + * http://abcavi.kibi.ru/docs/odml1.pdf */ // ***************************************************************************** -// included header files -#include "config.h" -#include "basicio.hpp" +// included header files +#include "riffvideo.hpp" +#include "enforce.hpp" #include "error.hpp" #include "futils.hpp" -#include "image_int.hpp" -#include "riffvideo.hpp" -#include "tags.hpp" -#include "tags_int.hpp" -#include "tiffimage_int.hpp" -#include "types.hpp" -// + standard includes -#include +#include "helper_functions.hpp" -// ***************************************************************************** -// class member definitions namespace Exiv2::Internal { -/*! - @brief Dummy TIFF header structure. - */ -class DummyTiffHeader : public TiffHeaderBase { - public: - //! @name Creators - //@{ - //! Default constructor - explicit DummyTiffHeader(ByteOrder byteOrder); - //@} - - //! @name Manipulators - //@{ - //! Dummy read function. Does nothing and returns true. - bool read(const byte* pData, size_t size) override; - //@} - -}; // class TiffHeader - -DummyTiffHeader::DummyTiffHeader(ByteOrder byteOrder) : TiffHeaderBase(42, 0, byteOrder, 0) { -} - -bool DummyTiffHeader::read(const byte* /*pData*/, size_t /*size*/) { - return true; -} - -constexpr TagVocabulary infoTags[] = {{"AGES", "Xmp.video.Rated"}, - {"CMNT", "Xmp.video.Comment"}, - {"CODE", "Xmp.video.EncodedBy"}, - {"COMM", "Xmp.video.Comment"}, - {"DIRC", "Xmp.video.Director"}, - {"DISP", "Xmp.audio.SchemeTitle"}, - {"DTIM", "Xmp.video.DateTimeOriginal"}, - {"GENR", "Xmp.video.Genre"}, - {"IARL", "Xmp.video.ArchivalLocation"}, - {"IART", "Xmp.video.Artist"}, - {"IAS1", "Xmp.video.Edit1"}, - {"IAS2", "Xmp.video.Edit2"}, - {"IAS3", "Xmp.video.Edit3"}, - {"IAS4", "Xmp.video.Edit4"}, - {"IAS5", "Xmp.video.Edit5"}, - {"IAS6", "Xmp.video.Edit6"}, - {"IAS7", "Xmp.video.Edit7"}, - {"IAS8", "Xmp.video.Edit8"}, - {"IAS9", "Xmp.video.Edit9"}, - {"IBSU", "Xmp.video.BaseURL"}, - {"ICAS", "Xmp.audio.DefaultStream"}, - {"ICDS", "Xmp.video.CostumeDesigner"}, - {"ICMS", "Xmp.video.Commissioned"}, - {"ICMT", "Xmp.video.Comment"}, - {"ICNM", "Xmp.video.Cinematographer"}, - {"ICNT", "Xmp.video.Country"}, - {"ICOP", "Xmp.video.Copyright"}, - {"ICRD", "Xmp.video.DateTimeDigitized"}, - {"ICRP", "Xmp.video.Cropped"}, - {"IDIM", "Xmp.video.Dimensions"}, - {"IDPI", "Xmp.video.DotsPerInch"}, - {"IDST", "Xmp.video.DistributedBy"}, - {"IEDT", "Xmp.video.EditedBy"}, - {"IENC", "Xmp.video.EncodedBy"}, - {"IENG", "Xmp.video.Engineer"}, - {"IGNR", "Xmp.video.Genre"}, - {"IKEY", "Xmp.video.PerformerKeywords"}, - {"ILGT", "Xmp.video.Lightness"}, - {"ILGU", "Xmp.video.LogoURL"}, - {"ILIU", "Xmp.video.LogoIconURL"}, - {"ILNG", "Xmp.video.Language"}, - {"IMBI", "Xmp.video.InfoBannerImage"}, - {"IMBU", "Xmp.video.InfoBannerURL"}, - {"IMED", "Xmp.video.Medium"}, - {"IMIT", "Xmp.video.InfoText"}, - {"IMIU", "Xmp.video.InfoURL"}, - {"IMUS", "Xmp.video.MusicBy"}, - {"INAM", "Xmp.video.Title"}, - {"IPDS", "Xmp.video.ProductionDesigner"}, - {"IPLT", "Xmp.video.NumOfColors"}, - {"IPRD", "Xmp.video.Product"}, - {"IPRO", "Xmp.video.ProducedBy"}, - {"IRIP", "Xmp.video.RippedBy"}, - {"IRTD", "Xmp.video.Rating"}, - {"ISBJ", "Xmp.video.Subject"}, - {"ISFT", "Xmp.video.Software"}, - {"ISGN", "Xmp.video.SecondaryGenre"}, - {"ISHP", "Xmp.video.Sharpness"}, - {"ISRC", "Xmp.video.Source"}, - {"ISRF", "Xmp.video.SourceForm"}, - {"ISTD", "Xmp.video.ProductionStudio"}, - {"ISTR", "Xmp.video.Starring"}, - {"ITCH", "Xmp.video.Technician"}, - {"IWMU", "Xmp.video.WatermarkURL"}, - {"IWRI", "Xmp.video.WrittenBy"}, - {"LANG", "Xmp.video.Language"}, - {"LOCA", "Xmp.video.LocationInfo"}, - {"PRT1", "Xmp.video.Part"}, - {"PRT2", "Xmp.video.NumOfParts"}, - {"RATE", "Xmp.video.Rate"}, - {"STAR", "Xmp.video.Starring"}, - {"STAT", "Xmp.video.Statistics"}, - {"TAPE", "Xmp.video.TapeName"}, - {"TCDO", "Xmp.video.EndTimecode"}, - {"TCOD", "Xmp.video.StartTimecode"}, - {"TITL", "Xmp.video.Title"}, - {"TLEN", "Xmp.video.Length"}, - {"TORG", "Xmp.video.Organization"}, - {"TRCK", "Xmp.video.TrackNumber"}, - {"TURL", "Xmp.video.URL"}, - {"TVER", "Xmp.video.SoftwareVersion"}, - {"VMAJ", "Xmp.video.VegasVersionMajor"}, - {"VMIN", "Xmp.video.VegasVersionMinor"}, - {"YEAR", "Xmp.video.Year"}}; - -constexpr TagDetails audioEncodingValues[] = { +const std::map infoTags = {{"AGES", "Xmp.video.Rated"}, + {"CMNT", "Xmp.video.Comment"}, + {"CODE", "Xmp.video.EncodedBy"}, + {"COMM", "Xmp.video.Comment"}, + {"DIRC", "Xmp.video.Director"}, + {"DISP", "Xmp.audio.SchemeTitle"}, + {"DTIM", "Xmp.video.DateTimeOriginal"}, + {"GENR", "Xmp.video.Genre"}, + {"IARL", "Xmp.video.ArchivalLocation"}, + {"IART", "Xmp.video.Artist"}, + {"IAS1", "Xmp.video.Edit1"}, + {"IAS2", "Xmp.video.Edit2"}, + {"IAS3", "Xmp.video.Edit3"}, + {"IAS4", "Xmp.video.Edit4"}, + {"IAS5", "Xmp.video.Edit5"}, + {"IAS6", "Xmp.video.Edit6"}, + {"IAS7", "Xmp.video.Edit7"}, + {"IAS8", "Xmp.video.Edit8"}, + {"IAS9", "Xmp.video.Edit9"}, + {"IBSU", "Xmp.video.BaseURL"}, + {"ICAS", "Xmp.audio.DefaultStream"}, + {"ICDS", "Xmp.video.CostumeDesigner"}, + {"ICMS", "Xmp.video.Commissioned"}, + {"ICMT", "Xmp.video.Comment"}, + {"ICNM", "Xmp.video.Cinematographer"}, + {"ICNT", "Xmp.video.Country"}, + {"ICOP", "Xmp.video.Copyright"}, + {"ICRD", "Xmp.video.DateTimeDigitized"}, + {"ICRP", "Xmp.video.Cropped"}, + {"IDIM", "Xmp.video.Dimensions"}, + {"IDPI", "Xmp.video.DotsPerInch"}, + {"IDST", "Xmp.video.DistributedBy"}, + {"IEDT", "Xmp.video.EditedBy"}, + {"IENC", "Xmp.video.EncodedBy"}, + {"IENG", "Xmp.video.Engineer"}, + {"IGNR", "Xmp.video.Genre"}, + {"IKEY", "Xmp.video.PerformerKeywords"}, + {"ILGT", "Xmp.video.Lightness"}, + {"ILGU", "Xmp.video.LogoURL"}, + {"ILIU", "Xmp.video.LogoIconURL"}, + {"ILNG", "Xmp.video.Language"}, + {"IMBI", "Xmp.video.InfoBannerImage"}, + {"IMBU", "Xmp.video.InfoBannerURL"}, + {"IMED", "Xmp.video.Medium"}, + {"IMIT", "Xmp.video.InfoText"}, + {"IMIU", "Xmp.video.InfoURL"}, + {"IMUS", "Xmp.video.MusicBy"}, + {"INAM", "Xmp.video.Title"}, + {"IPDS", "Xmp.video.ProductionDesigner"}, + {"IPLT", "Xmp.video.NumOfColors"}, + {"IPRD", "Xmp.video.Product"}, + {"IPRO", "Xmp.video.ProducedBy"}, + {"IRIP", "Xmp.video.RippedBy"}, + {"IRTD", "Xmp.video.Rating"}, + {"ISBJ", "Xmp.video.Subject"}, + {"ISFT", "Xmp.video.Software"}, + {"ISGN", "Xmp.video.SecondaryGenre"}, + {"ISHP", "Xmp.video.Sharpness"}, + {"ISRC", "Xmp.video.Source"}, + {"ISRF", "Xmp.video.SourceForm"}, + {"ISTD", "Xmp.video.ProductionStudio"}, + {"ISTR", "Xmp.video.Starring"}, + {"ITCH", "Xmp.video.Technician"}, + {"IWMU", "Xmp.video.WatermarkURL"}, + {"IWRI", "Xmp.video.WrittenBy"}, + {"LANG", "Xmp.video.Language"}, + {"LOCA", "Xmp.video.LocationInfo"}, + {"PRT1", "Xmp.video.Part"}, + {"PRT2", "Xmp.video.NumOfParts"}, + {"RATE", "Xmp.video.Rate"}, + {"STAR", "Xmp.video.Starring"}, + {"STAT", "Xmp.video.Statistics"}, + {"TAPE", "Xmp.video.TapeName"}, + {"TCDO", "Xmp.video.EndTimecode"}, + {"TCOD", "Xmp.video.StartTimecode"}, + {"TITL", "Xmp.video.Title"}, + {"TLEN", "Xmp.video.Length"}, + {"TORG", "Xmp.video.Organization"}, + {"TRCK", "Xmp.video.TrackNumber"}, + {"TURL", "Xmp.video.URL"}, + {"TVER", "Xmp.video.SoftwareVersion"}, + {"VMAJ", "Xmp.video.VegasVersionMajor"}, + {"VMIN", "Xmp.video.VegasVersionMinor"}, + {"YEAR", "Xmp.video.Year"}}; + +const std::map audioEncodingValues = { {0x1, "Microsoft PCM"}, {0x2, "Microsoft ADPCM"}, {0x3, "Microsoft IEEE float"}, @@ -393,36 +345,13 @@ constexpr TagDetails audioEncodingValues[] = { {0xfffe, "Extensible"}, {0xffff, "Development"}}; -constexpr TagDetails nikonAVITags[] = {{0x0003, "Xmp.video.Make"}, - {0x0004, "Xmp.video.Model"}, - {0x0005, "Xmp.video.Software"}, - {0x0006, "Xmp.video.Equipment"}, - {0x0007, "Xmp.video.Orientation"}, - {0x0008, "Xmp.video.ExposureTime"}, - {0x0009, "Xmp.video.FNumber"}, - {0x000a, "Xmp.video.ExposureCompensation"}, - {0x000b, "Xmp.video.MaxApertureValue"}, - {0x000c, "Xmp.video.MeteringMode"}, - {0x000f, "Xmp.video.FocalLength"}, - {0x0010, "Xmp.video.XResolution"}, - {0x0011, "Xmp.video.YResolution"}, - {0x0012, "Xmp.video.ResolutionUnit"}, - {0x0013, "Xmp.video.DateTimeOriginal"}, - {0x0014, "Xmp.video.DateTimeDigitized"}, - {0x0016, "Xmp.video.duration"}, - {0x0018, "Xmp.video.FocusMode"}, - {0x001b, "Xmp.video.DigitalZoomRatio"}, - {0x001d, "Xmp.video.ColorMode"}, - {0x001e, "Xmp.video.Sharpness"}, - {0x001f, "Xmp.video.WhiteBalance"}, - {0x0020, "Xmp.video.ColorNoiseReduction"}}; - -enum streamTypeInfo { Audio = 1, MIDI, Text, Video }; - } // namespace Exiv2::Internal +// ***************************************************************************** +// class member definitions namespace Exiv2 { -using namespace Exiv2::Internal; + +enum streamTypeInfo { Audio = 1, MIDI, Text, Video }; RiffVideo::RiffVideo(BasicIo::UniquePtr io) : Image(ImageType::riff, mdNone, std::move(io)) { } // RiffVideo::RiffVideo @@ -431,84 +360,6 @@ std::string RiffVideo::mimeType() const { return "video/riff"; } -/*! - @brief Function used to check equality of a Tags with a - particular string (ignores case while comparing). - @param buf Data buffer that will contain Tag to compare - @param str char* Pointer to string - @return Returns true if the buffer value is equal to string. - */ -bool RiffVideo::equalsRiffTag(Exiv2::DataBuf& buf, const char* str) { - for (size_t i = 0; i < RIFF_TAG_SIZE; i++) - if (toupper(buf.data()[i]) != str[i]) - return false; - return true; -} - -void RiffVideo::printStructure(std::ostream& out, PrintStructureOption option, size_t depth) { - if (io_->open() != 0) { - throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError()); - } - // Ensure this is the correct image type - if (!isRiffType(*io_, true)) { - if (io_->error() || io_->eof()) - throw Error(ErrorCode::kerFailedToReadImageData); - throw Error(ErrorCode::kerNotAnImage, "RIFF"); - } - - bool bPrint = option == kpsBasic || option == kpsRecursive; - if (bPrint || option == kpsXMP || option == kpsIccProfile || option == kpsIptcErase) { - byte data[RIFF_TAG_SIZE * 2]; - io_->read(data, RIFF_TAG_SIZE * 2); - uint64_t filesize = Exiv2::getULong(data + RIFF_TAG_SIZE, littleEndian); - DataBuf chunkId(5); - - if (bPrint) { - out << Internal::indent(depth) << "STRUCTURE OF RIFF FILE: " << io().path() << std::endl; - out << Internal::indent(depth) << Internal::stringFormat(" Chunk | Length | Offset | Payload") - << std::endl; - } - - const uint64_t bufMaxSize = 200; - io_->seek(0, BasicIo::beg); // rewind - while (!io_->eof() && static_cast(io_->tell()) < filesize) { - auto offset = static_cast(io_->tell()); - byte size_buff[RIFF_TAG_SIZE]; - io_->read(chunkId.data(), RIFF_TAG_SIZE); - io_->read(size_buff, RIFF_TAG_SIZE); - uint32_t size = Exiv2::getULong(size_buff, littleEndian); - if (size > bufMaxSize) { - io_->seek(size, BasicIo::cur); - continue; - } - DataBuf payload(offset ? size : RIFF_TAG_SIZE); // header is different from chunks - io_->read(payload.data(), payload.size()); - - if (bPrint) { - out << Internal::indent(depth) - << Internal::stringFormat(" %s | %12u | %12u | ", reinterpret_cast(chunkId.data()), size, - static_cast(offset)) - << Internal::binaryToString(makeSlice(payload, 0, payload.size() > 32 ? 32 : payload.size())) << std::endl; - } - - if (equalsRiffTag(chunkId, RIFF_CHUNK_HEADER_EXIF) && option == kpsRecursive) { - // create MemIo object with the payload, then print the structure - MemIo p(payload.c_data(), payload.size()); - printTiffStructure(p, out, option, depth); - } - - bool bPrintPayload = (equalsRiffTag(chunkId, RIFF_CHUNK_HEADER_XMP) && option == kpsXMP) || - (equalsRiffTag(chunkId, RIFF_CHUNK_HEADER_ICCP) && option == kpsIccProfile); - if (bPrintPayload) { - out.write(reinterpret_cast(payload.data()), payload.size()); - } - - if (offset && io_->tell() % 2) - io_->seek(+1, BasicIo::cur); // skip padding byte on sub-chunks - } - } -} // RiffVideo::printStructure - void RiffVideo::writeMetadata() { } // RiffVideo::writeMetadata @@ -525,660 +376,389 @@ void RiffVideo::readMetadata() { IoCloser closer(*io_); clearMetadata(); - continueTraversing_ = true; - xmpData_["Xmp.video.FileSize"] = io_->size() / 1048576.; - xmpData_["Xmp.video.FileName"] = io_->path(); + xmpData_["Xmp.video.FileSize"] = io_->size(); xmpData_["Xmp.video.MimeType"] = mimeType(); - DataBuf buf(RIFF_TAG_SIZE + 1); - - io_->read(buf.data(), RIFF_TAG_SIZE); - xmpData_["Xmp.video.Container"] = buf.data(); + HeaderReader header(io_); + xmpData_["Xmp.video.Container"] = header.getId(); - io_->read(buf.data(), RIFF_TAG_SIZE); - io_->read(buf.data(), RIFF_TAG_SIZE); - xmpData_["Xmp.video.FileType"] = buf.data(); + xmpData_["Xmp.video.FileType"] = readStringTag(io_); - while (continueTraversing_) - decodeBlock(); + decodeBlocks(); } // RiffVideo::readMetadata -void RiffVideo::decodeBlock() { - DataBuf buf(RIFF_TAG_SIZE + 1); - DataBuf buf2(RIFF_TAG_SIZE + 1); +RiffVideo::HeaderReader::HeaderReader(BasicIo::UniquePtr& io) { + Internal::enforce(io->size() > io->tell() + DWORD + DWORD, Exiv2::ErrorCode::kerCorruptedMetadata); + id_ = readStringTag(io); + size_ = readDWORDTag(io); +} - io_->read(buf2.data(), RIFF_TAG_SIZE); +bool RiffVideo::equal(const std::string& str1, const std::string& str2) { + if (str1.size() != str2.size()) + return false; + for (size_t i = 0; i < str1.size(); i++) + if (toupper(str1[i]) != str2[i]) + return false; + return true; +} - if (io_->eof() || equalsRiffTag(buf2, "MOVI") || equalsRiffTag(buf2, "DATA")) { - continueTraversing_ = false; - return; - } - if (equalsRiffTag(buf2, "HDRL") || equalsRiffTag(buf2, "STRL")) { - decodeBlock(); - } else { - io_->read(buf.data(), RIFF_TAG_SIZE); - size_t size = Exiv2::getULong(buf.data(), littleEndian); +void RiffVideo::readList(HeaderReader& header_) { + std::string chunk_type = readStringTag(io_); - tagDecoder(buf2, size); - } -} // RiffVideo::decodeBlock +#ifdef EXIV2_DEBUG_MESSAGES + EXV_INFO << "-> Reading list : id= " << header_.getId() << " type= " << chunk_type << " size= " << header_.getSize() + << "(" << io_->tell() << "/" << io_->size() << ")" << std::endl; +#endif -void RiffVideo::tagDecoder(Exiv2::DataBuf& buf, size_t size) { - uint64_t cur_pos = io_->tell(); - static bool listFlag = false, listEnd = false; - - if (equalsRiffTag(buf, "LIST")) { - listFlag = true; - listEnd = false; - - while (static_cast(io_->tell()) < cur_pos + size) - decodeBlock(); - - listEnd = true; - io_->seek(cur_pos + size, BasicIo::beg); - } else if (equalsRiffTag(buf, "JUNK") && listEnd) { - junkHandler(size); - } else if (equalsRiffTag(buf, "AVIH")) { - listFlag = false; - aviHeaderTagsHandler(size); - } else if (equalsRiffTag(buf, "STRH")) { - listFlag = false; - streamHandler(size); - } else if (equalsRiffTag(buf, "STRF") || equalsRiffTag(buf, "FMT ")) { - listFlag = false; - if (equalsRiffTag(buf, "FMT ")) - streamType_ = Audio; - streamFormatHandler(size); - } else if (equalsRiffTag(buf, "STRN")) { - listFlag = false; - dateTimeOriginal(size, 1); - } else if (equalsRiffTag(buf, "STRD")) { - listFlag = false; - streamDataTagHandler(size); - } else if (equalsRiffTag(buf, "IDIT")) { - listFlag = false; - dateTimeOriginal(size); - } else if (equalsRiffTag(buf, "INFO")) { - listFlag = false; - infoTagsHandler(); - } else if (equalsRiffTag(buf, "NCDT")) { - listFlag = false; - nikonTagsHandler(); - } else if (equalsRiffTag(buf, "ODML")) { - listFlag = false; - odmlTagsHandler(); - } else if (listFlag) { - // std::cout<<"|unprocessed|"<seek(cur_pos + size, BasicIo::beg); + if (equal(chunk_type, CHUNK_ID_INFO)) + readInfoListChunk(header_.getSize()); + else if (equal(chunk_type, CHUNK_ID_MOVI)) { + readMoviList(header_.getSize()); } -} // RiffVideo::tagDecoder - -void RiffVideo::streamDataTagHandler(size_t size) { - const size_t bufMinSize = 20000; - DataBuf buf(bufMinSize); - uint64_t cur_pos = io_->tell(); - - io_->read(buf.data(), 8); +} - if (equalsRiffTag(buf, "AVIF")) { - if (size < RIFF_TAG_SIZE) { -#ifndef SUPPRESS_WARNINGS - EXV_ERROR << " Exif Tags found in this RIFF file are not of valid size ." - << " Entries considered invalid. Not Processed.\n"; +void RiffVideo::readChunk(HeaderReader& header_) { +#ifdef EXIV2_DEBUG_MESSAGES + if (header_.getSize()) + EXV_INFO << "--> Reading Chunk : [" << header_.getId() << "] size= " << header_.getSize() << "(" << io_->tell() + << "/" << io_->size() << ")" << std::endl; #endif - } else { - io_->read(buf.data(), size - RIFF_TAG_SIZE); - - IptcData iptcData; - XmpData xmpData; - DummyTiffHeader tiffHeader(littleEndian); - TiffParserWorker::decode(exifData_, iptcData, xmpData, buf.data(), buf.size(), Tag::root, - TiffMapping::findDecoder, &tiffHeader); - -#ifndef SUPPRESS_WARNINGS - if (!iptcData.empty()) { - EXV_WARNING << "Ignoring IPTC information encoded in the Exif data.\n"; - } - if (!xmpData.empty()) { - EXV_WARNING << "Ignoring XMP information encoded in the Exif data.\n"; - } + + if (equal(header_.getId(), CHUNK_ID_AVIH)) + readAviHeader(); + else if (equal(header_.getId(), CHUNK_ID_STRH)) + readStreamHeader(); + else if (equal(header_.getId(), CHUNK_ID_STRF)) + readStreamFormat(header_.getSize()); + else if (equal(header_.getId(), CHUNK_ID_FMT)) { + streamType_ = Audio; + readStreamFormat(header_.getSize()); + } else if (equal(header_.getId(), CHUNK_ID_STRD)) + readStreamData(header_.getSize()); + else if (equal(header_.getId(), CHUNK_ID_STRN)) + StreamName(header_.getSize()); + else if (equal(header_.getId(), CHUNK_ID_VPRP)) + readVPRPChunk(header_.getSize()); + else if (equal(header_.getId(), CHUNK_ID_IDX1)) + readIndexChunk(header_.getSize()); + else if (equal(header_.getId(), CHUNK_ID_DATA)) + readDataChunk(header_.getSize()); + else if (equal(header_.getId(), CHUNK_ID_JUNK)) + readJunk(header_.getSize()); + else { +#ifdef EXIV2_DEBUG_MESSAGES + if (header_.getSize()) + EXV_WARNING << "--> Ignoring Chunk : " << header_.getId() << "] size= " << header_.getSize() << "(" << io_->tell() + << "/" << io_->size() << ")" << std::endl; #endif - } + io_->seekOrThrow(io_->tell() + header_.getSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData); } - // TODO decode CasioData and ZORA Tag - io_->seek(cur_pos + size, BasicIo::beg); - -} // RiffVideo::streamDataTagHandler - -void RiffVideo::dateTimeOriginal(size_t size, int i) { - uint64_t cur_pos = io_->tell(); - const size_t bufMinSize = 100; - DataBuf buf(bufMinSize); - io_->read(buf.data(), size); - if (!i) - xmpData_["Xmp.video.DateUTC"] = buf.data(); - else - xmpData_["Xmp.video.StreamName"] = buf.data(); - io_->seek(cur_pos + size, BasicIo::beg); -} // RiffVideo::dateTimeOriginal - -void RiffVideo::odmlTagsHandler() { - const size_t bufMinSize = 100; - DataBuf buf(bufMinSize); - io_->seek(-12, BasicIo::cur); - io_->read(buf.data(), RIFF_TAG_SIZE); - size_t size = Exiv2::getULong(buf.data(), littleEndian); - size_t size2 = size; - - uint64_t cur_pos = io_->tell(); - io_->read(buf.data(), RIFF_TAG_SIZE); - size -= RIFF_TAG_SIZE; - - while (size > 0) { - io_->read(buf.data(), RIFF_TAG_SIZE); - size -= RIFF_TAG_SIZE; - if (equalsRiffTag(buf, "DMLH")) { - io_->read(buf.data(), RIFF_TAG_SIZE); - size -= RIFF_TAG_SIZE; - io_->read(buf.data(), RIFF_TAG_SIZE); - size -= RIFF_TAG_SIZE; - xmpData_["Xmp.video.TotalFrameCount"] = Exiv2::getULong(buf.data(), littleEndian); - } - } - io_->seek(cur_pos + size2, BasicIo::beg); -} // RiffVideo::odmlTagsHandler - -void RiffVideo::skipListData() { - DataBuf buf(RIFF_TAG_SIZE + 1); - io_->seek(-12, BasicIo::cur); - io_->read(buf.data(), RIFF_TAG_SIZE); - size_t size = Exiv2::getULong(buf.data(), littleEndian); - - uint64_t cur_pos = io_->tell(); - io_->seek(cur_pos + size, BasicIo::beg); -} // RiffVideo::skipListData - -void RiffVideo::copyTagValue(DataBuf& buf_dest, DataBuf& buf_src, size_t index) { - buf_dest.data()[0] = buf_src.data()[0 + index]; - buf_dest.data()[1] = buf_src.data()[1 + index]; - buf_dest.data()[2] = buf_src.data()[2 + index]; - buf_dest.data()[3] = buf_src.data()[3 + index]; } -void RiffVideo::nikonTagsHandler() { - const size_t bufMinSize = 100; - DataBuf buf(bufMinSize), buf2(RIFF_TAG_SIZE + 1); - io_->seek(-12, BasicIo::cur); - io_->read(buf.data(), RIFF_TAG_SIZE); - - size_t internal_size = 0, tagID = 0, dataSize = 0, tempSize, size = Exiv2::getULong(buf.data(), littleEndian); - tempSize = size; - char str[9] = " . . . "; - uint64_t internal_pos, cur_pos; - internal_pos = cur_pos = io_->tell(); - const TagDetails* td; - double denominator = 1; - io_->read(buf.data(), RIFF_TAG_SIZE); - tempSize -= RIFF_TAG_SIZE; - - while (tempSize > 0) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), RIFF_TAG_SIZE); - io_->read(buf2.data(), RIFF_TAG_SIZE); - size_t temp = internal_size = Exiv2::getULong(buf2.data(), littleEndian); - internal_pos = io_->tell(); - tempSize -= (internal_size + 8); - - if (equalsRiffTag(buf, "NCVR")) { - while (temp > 3) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 2); - tagID = Exiv2::getULong(buf.data(), littleEndian); - io_->read(buf.data(), 2); - dataSize = Exiv2::getULong(buf.data(), littleEndian); - temp -= (RIFF_TAG_SIZE + dataSize); - - if (tagID == 0x0001) { - if (dataSize <= 0) { -#ifndef SUPPRESS_WARNINGS - EXV_ERROR << " Makernotes found in this RIFF file are not of valid size ." - << " Entries considered invalid. Not Processed.\n"; -#endif - } else { - io_->read(buf.data(), dataSize); - xmpData_["Xmp.video.MakerNoteType"] = buf.data(); - } - } else if (tagID == 0x0002) { - while (dataSize) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 1); - str[(RIFF_TAG_SIZE - dataSize) * 2] = static_cast(Exiv2::getULong(buf.data(), littleEndian) + 48); - --dataSize; - } - xmpData_["Xmp.video.MakerNoteVersion"] = str; - } - } - } else if (equalsRiffTag(buf, "NCTG")) { - while (temp > 3) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 2); - tagID = Exiv2::getULong(buf.data(), littleEndian); - io_->read(buf.data(), 2); - dataSize = Exiv2::getULong(buf.data(), littleEndian); - temp -= (RIFF_TAG_SIZE + dataSize); - td = Exiv2::find(nikonAVITags, tagID); - - if (dataSize <= 0) { -#ifndef SUPPRESS_WARNINGS - EXV_ERROR << " Makernotes found in this RIFF file are not of valid size ." - << " Entries considered invalid. Not Processed.\n"; +void RiffVideo::decodeBlocks() { + HeaderReader header(io_); + if (equal(header.getId(), CHUNK_ID_LIST)) { + readList(header); + } else { + readChunk(header); + } + + if (!io_->eof() && io_->tell() < io_->size()) { + decodeBlocks(); + } + +} // RiffVideo::decodeBlock + +void RiffVideo::readAviHeader() { +#ifdef EXIV2_DEBUG_MESSAGES + EXV_INFO << "--> dwMicroSecPerFrame = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwMaxBytesPerSec = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwPaddingGranularity = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwFlags = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwTotalFrames = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwInitialFrames = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwStreams = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwSuggestedBufferSize = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwWidth = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwHeight = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwReserved1 = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwReserved2 = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwReserved3 = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwReserved4 = " << readDWORDTag(io_) << std::endl; + if (LogMsg::info >= LogMsg::level() && LogMsg::handler()) + io_->seekOrThrow(io_->tell() - DWORD * 14, BasicIo::beg, ErrorCode::kerFailedToReadImageData); #endif - } else { - io_->read(buf.data(), dataSize); - - switch (tagID) { - case 0x0003: - case 0x0004: - case 0x0005: - case 0x0006: - case 0x0013: - case 0x0014: - case 0x0018: - case 0x001d: - case 0x001e: - case 0x001f: - case 0x0020: - xmpData_[exvGettext(td->label_)] = buf.data(); - break; - - case 0x0007: - case 0x0010: - case 0x0011: - case 0x000c: - case 0x0012: - xmpData_[exvGettext(td->label_)] = Exiv2::getULong(buf.data(), littleEndian); - break; - - case 0x0008: - case 0x0009: - case 0x000a: - case 0x000b: - case 0x000f: - case 0x001b: - case 0x0016: - copyTagValue(buf2, buf); - denominator = static_cast(Exiv2::getLong(buf2.data(), littleEndian)); - if (denominator != 0) - xmpData_[exvGettext(td->label_)] = Exiv2::getLong(buf.data(), littleEndian) / denominator; - else - xmpData_[exvGettext(td->label_)] = 0; - break; - - default: - break; - } - } - } - } - else if (equalsRiffTag(buf, "NCTH")) { // TODO Nikon Thumbnail Image - } + uint32_t TimeBetweenFrames = readDWORDTag(io_); + xmpData_["Xmp.video.MicroSecPerFrame"] = TimeBetweenFrames; + double frame_rate = 1000000. / TimeBetweenFrames; - else if (equalsRiffTag(buf, "NCVW")) { // TODO Nikon Preview Image - } + xmpData_["Xmp.video.MaxDataRate"] = readDWORDTag(io_); // MaximumDataRate - io_->seek(internal_pos + internal_size, BasicIo::beg); - } + io_->seekOrThrow(io_->tell() + DWORD * 2, BasicIo::beg, + ErrorCode::kerFailedToReadImageData); // ignore PaddingGranularity and Flags - if (size == 0) { - io_->seek(cur_pos + RIFF_TAG_SIZE, BasicIo::beg); - } else { - io_->seek(cur_pos + size, BasicIo::beg); - } -} // RiffVideo::nikonTagsHandler - -void RiffVideo::infoTagsHandler() { - const size_t bufMinSize = 10000; - DataBuf buf(bufMinSize); - io_->seek(-12, BasicIo::cur); - io_->read(buf.data(), RIFF_TAG_SIZE); - uint32_t infoSize, size = Exiv2::getULong(buf.data(), littleEndian); - uint32_t size_external = size; - const TagVocabulary* tv; - - uint64_t cur_pos = io_->tell(); - io_->read(buf.data(), RIFF_TAG_SIZE); - size -= RIFF_TAG_SIZE; - - while (size >= RIFF_TAG_SIZE) { - io_->read(buf.data(), RIFF_TAG_SIZE); - size -= RIFF_TAG_SIZE; - if (!Exiv2::getULong(buf.data(), littleEndian)) - break; - tv = Exiv2::find(infoTags, Exiv2::toString(buf.data())); - io_->read(buf.data(), RIFF_TAG_SIZE); - size -= RIFF_TAG_SIZE; - infoSize = Exiv2::getULong(buf.data(), littleEndian); - - size -= infoSize; - io_->read(buf.data(), infoSize); - if (infoSize < RIFF_TAG_SIZE) - buf.data()[infoSize] = '\0'; - - if (tv) - xmpData_[exvGettext(tv->label_)] = buf.data(); - else - continue; - } - io_->seek(cur_pos + size_external, BasicIo::beg); -} // RiffVideo::infoTagsHandler + uint32_t frame_count = readDWORDTag(io_); // TotalNumberOfFrames + xmpData_["Xmp.video.FrameCount"] = frame_count; -void RiffVideo::junkHandler(size_t size) { - DataBuf buf(size + 1), buf2(RIFF_TAG_SIZE + 1); - uint64_t cur_pos = io_->tell(); + io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, + ErrorCode::kerFailedToReadImageData); // ignore NumberOfInitialFrames - io_->read(buf.data(), RIFF_TAG_SIZE); - //! Pentax Metadata and Tags - if (equalsRiffTag(buf, "PENT")) { - io_->seek(cur_pos + 18, BasicIo::beg); - io_->read(buf.data(), 26); - xmpData_["Xmp.video.Make"] = buf.data(); + xmpData_["Xmp.audio.ChannelType"] = getStreamType(readDWORDTag(io_)); // NumberOfStreams - io_->read(buf.data(), 50); - xmpData_["Xmp.video.Model"] = buf.data(); + xmpData_["Xmp.video.StreamCount"] = readDWORDTag(io_); // SuggestedBufferSize - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), 8); - copyTagValue(buf2, buf); - xmpData_["Xmp.video.FNumber"] = - static_cast(Exiv2::getLong(buf.data(), littleEndian)) / Exiv2::getLong(buf2.data(), littleEndian); - ; + uint32_t width = readDWORDTag(io_); + xmpData_["Xmp.video.Width"] = width; - io_->seek(cur_pos + 131, BasicIo::beg); - io_->read(buf.data(), 26); - xmpData_["Xmp.video.DateTimeOriginal"] = buf.data(); + uint32_t height = readDWORDTag(io_); + xmpData_["Xmp.video.Height"] = height; - io_->read(buf.data(), 26); - xmpData_["Xmp.video.DateTimeDigitized"] = buf.data(); + io_->seekOrThrow(io_->tell() + DWORD * 4, BasicIo::beg, + ErrorCode::kerFailedToReadImageData); // TimeScale, DataRate, StartTime, DataLength - io_->seek(cur_pos + 299, BasicIo::beg); - std::memset(buf.data(), 0x0, buf.size()); + fillAspectRatio(width, height); + fillDuration(frame_rate, frame_count); +} - io_->read(buf.data(), 2); - Exiv2::XmpTextValue tv(Exiv2::toString(Exiv2::getLong(buf.data(), littleEndian))); - xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:width"), &tv); +void RiffVideo::readStreamHeader() { + std::string stream = readStringTag(io_); + streamType_ = (equal(stream, "VIDS")) ? Video : Audio; + +#ifdef EXIV2_DEBUG_MESSAGES + EXV_INFO << "--> fccType = " << stream << std::endl; + EXV_INFO << "--> fccHandler = " << readStringTag(io_) << std::endl; + EXV_INFO << "--> dwFlags = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> wPriority = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> wLanguage = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> dwInitialFrames = " << readDWORDTag(io_) << std::endl; // 20 + EXV_INFO << "--> dwScale = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwRate = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwStart = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwLength = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> dwSuggestedBufferSize = " << readDWORDTag(io_) << std::endl; // 40 + EXV_INFO << "--> dwSampleSize = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> Left = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> top = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> right = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> bottom = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> XXXXXX = " << readDWORDTag(io_) << std::endl; // 56 + if (LogMsg::info >= LogMsg::level() && LogMsg::handler()) + io_->seekOrThrow(io_->tell() - DWORD * 13, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +#endif - io_->read(buf.data(), 2); - tv.read(Exiv2::toString(Exiv2::getLong(buf.data(), littleEndian))); - xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:height"), &tv); + xmpData_["Xmp.video.Codec"] = readStringTag(io_); // DataHandler - io_->read(buf.data(), RIFF_TAG_SIZE); + io_->seekOrThrow(io_->tell() + DWORD * 2 + WORD * 2, BasicIo::beg, + ErrorCode::kerFailedToReadImageData); // dwFlags, wPriority, wLanguage, dwInitialFrames - // TODO - Storing the image Thumbnail in Base64 Format + uint32_t divisor = readDWORDTag(io_); // TimeScale - } else { - io_->seek(cur_pos, BasicIo::beg); - io_->read(buf.data(), size); - xmpData_["Xmp.video.Junk"] = buf.data(); + if (divisor) { + double rate = static_cast(readDWORDTag(io_) / divisor); + xmpData_[(streamType_ == Video) ? "Xmp.video.FrameRate" : "Xmp.audio.SampleRate"] = rate; } + io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); // dwStart - io_->seek(cur_pos + size, BasicIo::beg); -} // RiffVideo::junkHandler - -void RiffVideo::aviHeaderTagsHandler(size_t size) { - DataBuf buf(RIFF_TAG_SIZE + 1); - size_t width = 0, height = 0, frame_count = 0; - double frame_rate = 1; - - uint64_t cur_pos = io_->tell(); - - enum aviHeaderTags { frameRate, maxDataRate, frameCount = 4, streamCount = 6, imageWidth_h = 8, imageHeight_h, last }; - for (aviHeaderTags tag = frameRate; tag != aviHeaderTags::last; tag = static_cast(tag + 1)) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), RIFF_TAG_SIZE); - - switch (tag) { - case frameRate: - xmpData_["Xmp.video.MicroSecPerFrame"] = Exiv2::getULong(buf.data(), littleEndian); - frame_rate = 1000000. / Exiv2::getULong(buf.data(), littleEndian); - break; - case (maxDataRate): - xmpData_["Xmp.video.MaxDataRate"] = Exiv2::getULong(buf.data(), littleEndian) / 1024.; - break; - case frameCount: - frame_count = Exiv2::getULong(buf.data(), littleEndian); - xmpData_["Xmp.video.FrameCount"] = frame_count; - break; - case streamCount: - xmpData_["Xmp.video.StreamCount"] = Exiv2::getULong(buf.data(), littleEndian); - break; - case imageWidth_h: - width = Exiv2::getULong(buf.data(), littleEndian); - xmpData_["Xmp.video.Width"] = width; - break; - case imageHeight_h: - height = Exiv2::getULong(buf.data(), littleEndian); - xmpData_["Xmp.video.Height"] = height; - break; - default: - break; - } + if (divisor) { + double frame_count = static_cast(readDWORDTag(io_) / divisor); // DataLength + xmpData_[(streamType_ == Video) ? "Xmp.video.FrameCount" : "Xmp.audio.FrameCount"] = frame_count; } - fillAspectRatio(width, height); - fillDuration(frame_rate, frame_count); + io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); // dwSuggestedBufferSize - io_->seek(cur_pos + size, BasicIo::beg); -} // RiffVideo::aviHeaderTagsHandler + xmpData_[(streamType_ == Video) ? "Xmp.video.VideoQuality" : "Xmp.video.StreamQuality"] = readDWORDTag(io_); -void RiffVideo::streamHandler(size_t size) { - DataBuf buf(RIFF_TAG_SIZE + 1); - size_t divisor = 1; - uint64_t cur_pos = io_->tell(); + xmpData_[(streamType_ == Video) ? "Xmp.video.VideoSampleSize" : "Xmp.video.StreamSampleSize"] = readDWORDTag(io_); + io_->seekOrThrow(io_->tell() + DWORD * 2, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} - io_->read(buf.data(), RIFF_TAG_SIZE); - if (equalsRiffTag(buf, "VIDS")) - streamType_ = Video; - else if (equalsRiffTag(buf, "AUDS")) - streamType_ = Audio; +void RiffVideo::readStreamFormat(uint64_t size_) { + // The structure of the strf chunk depends on the media type. Video streams use the BITMAPINFOHEADER structure, + // whereas audio streams use the WAVEFORMATEX structure. + +#ifdef EXIV2_DEBUG_MESSAGES + if (streamType_ == Audio) { + EXV_INFO << "--> wFormatTag = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> nChannels = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> nSamplesPerSec = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> nAvgBytesPerSec = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> nBlockAlign = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> wBitsPerSample = " << readWORDTag(io_) << std::endl; + if (LogMsg::info >= LogMsg::level() && LogMsg::handler()) + io_->seekOrThrow(io_->tell() - DWORD * 4, BasicIo::beg, ErrorCode::kerFailedToReadImageData); + } else if (streamType_ == Video) { + EXV_INFO << "--> biSize = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> biWidth = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> biHeight = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> biPlanes = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> biBitCount = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> biCompression = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> biSizeImage = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> biXPelsPerMeter = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> biYPelsPerMeter = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> biClrUsed = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> biClrImportant = " << readDWORDTag(io_) << std::endl; + if (LogMsg::info >= LogMsg::level() && LogMsg::handler()) + io_->seekOrThrow(io_->tell() - DWORD * 10, BasicIo::beg, ErrorCode::kerFailedToReadImageData); + } +#endif - enum streamHeaderTags { - codec = 1, - sampleRateDivisor = 5, - sampleRate = 6, - sampleCount = 8, - quality = 10, - sampleSize = 11, - last - }; - for (streamHeaderTags tag = codec; tag != streamHeaderTags::last; tag = static_cast(tag + 1)) { - std::memset(buf.data(), 0x0, buf.size()); - io_->read(buf.data(), RIFF_TAG_SIZE); // the position is advanced by the number of bytes read, that's why we need - // to iterate sequentially , not only on switch values. - - switch (tag) { - case codec: - if (streamType_ == Video) - xmpData_["Xmp.video.Codec"] = buf.data(); - else if (streamType_ == Audio) - xmpData_["Xmp.audio.Codec"] = buf.data(); - else - xmpData_["Xmp.video.Codec"] = buf.data(); - break; - case sampleRateDivisor: - divisor = Exiv2::getULong(buf.data(), littleEndian); - break; - case sampleRate: - if (streamType_ == Video) - xmpData_["Xmp.video.FrameRate"] = returnSampleRate(buf, divisor); - else if (streamType_ == Audio) - xmpData_["Xmp.audio.SampleRate"] = returnSampleRate(buf, divisor); - else - xmpData_["Xmp.video.StreamSampleRate"] = returnSampleRate(buf, divisor); - break; - case sampleCount: - if (streamType_ == Video) - xmpData_["Xmp.video.FrameCount"] = Exiv2::getULong(buf.data(), littleEndian); - else if (streamType_ == Audio) - xmpData_["Xmp.audio.SampleCount"] = Exiv2::getULong(buf.data(), littleEndian); - else - xmpData_["Xmp.video.StreamSampleCount"] = Exiv2::getULong(buf.data(), littleEndian); - break; - case quality: - if (streamType_ == Video) - xmpData_["Xmp.video.VideoQuality"] = Exiv2::getULong(buf.data(), littleEndian); - else if (streamType_ != Audio) - xmpData_["Xmp.video.StreamQuality"] = Exiv2::getULong(buf.data(), littleEndian); - break; - case sampleSize: - if (streamType_ == Video) - xmpData_["Xmp.video.VideoSampleSize"] = Exiv2::getULong(buf.data(), littleEndian); - else if (streamType_ != Audio) - xmpData_["Xmp.video.StreamSampleSize"] = Exiv2::getULong(buf.data(), littleEndian); - break; - default: - break; + if (streamType_ == Video) { + io_->seekOrThrow(io_->tell() + DWORD * 3, BasicIo::beg, + ErrorCode::kerFailedToReadImageData); // ignore biSize, biWidth, biHeight + xmpData_["Xmp.video.Planes"] = readWORDTag(io_); + xmpData_["Xmp.video.PixelDepth"] = readWORDTag(io_); + xmpData_["Xmp.video.Compressor"] = readStringTag(io_); + xmpData_["Xmp.video.ImageLength"] = readDWORDTag(io_); + xmpData_["Xmp.video.PixelPerMeterX"] = readQWORDTag(io_); + xmpData_["Xmp.video.PixelPerMeterY"] = readQWORDTag(io_); + uint32_t NumOfColours = readDWORDTag(io_); + if (NumOfColours == 0) + xmpData_["Xmp.video.NumOfColours"] = "Unspecified"; + else + xmpData_["Xmp.video.NumOfColours"] = NumOfColours; + uint32_t NumIfImpColours = readDWORDTag(io_); + if (NumIfImpColours == 0) + xmpData_["Xmp.video.NumIfImpColours"] = "All"; + else + xmpData_["Xmp.video.NumIfImpColours"] = NumIfImpColours; + } else if (streamType_ == Audio) { + uint16_t format_tag = readWORDTag(io_); + auto it = Internal::audioEncodingValues.find(format_tag); + if (it != Internal::audioEncodingValues.end()) { + xmpData_["Xmp.audio.Compressor"] = it->second; + } else { + xmpData_["Xmp.audio.Compressor"] = format_tag; } + + xmpData_["Xmp.audio.ChannelType"] = getStreamType(readDWORDTag(io_)); + xmpData_["Xmp.audio.SampleRate"] = readDWORDTag(io_); // nSamplesPerSec + io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); // nAvgBytesPerSec + xmpData_["Xmp.audio.SampleType"] = readDWORDTag(io_); // nBlockAlign + xmpData_["Xmp.audio.BitsPerSample"] = readDWORDTag(io_); // wBitsPerSample + if (xmpData_["Xmp.video.FileType"].toString() == "AVI ") + io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); // cbSize + } else { + io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData); } - io_->seek(cur_pos + size, BasicIo::beg); -} // RiffVideo::streamHandler +} -void RiffVideo::streamFormatHandler(size_t size) { - DataBuf buf(RIFF_TAG_SIZE + 1); - uint64_t cur_pos = io_->tell(); +void RiffVideo::readStreamData(uint64_t size_) { + io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} - if (streamType_ == Video) { - io_->read(buf.data(), RIFF_TAG_SIZE); - - enum bmptags { - imageWidth, - imageHeight, - planes, - bitDepth, - compression, - imageLength, - pixelsPerMeterX, - pixelsPerMeterY, - numColors, - numImportantColors, - last - }; - for (bmptags tag = imageWidth; tag != bmptags::last; tag = static_cast(tag + 1)) { - std::memset(buf.data(), 0x0, buf.size()); - - switch (tag) { - case imageWidth: // Will be used in case of debugging - io_->read(buf.data(), RIFF_TAG_SIZE); - break; - case imageHeight: // Will be used in case of debugging - io_->read(buf.data(), RIFF_TAG_SIZE); - break; - case planes: - io_->read(buf.data(), 2); - xmpData_["Xmp.video.Planes"] = Exiv2::getUShort(buf.data(), littleEndian); - break; - case bitDepth: - io_->read(buf.data(), 2); - xmpData_["Xmp.video.PixelDepth"] = Exiv2::getUShort(buf.data(), littleEndian); - break; - case compression: - io_->read(buf.data(), RIFF_TAG_SIZE); - xmpData_["Xmp.video.Compressor"] = buf.data(); - break; - case imageLength: - io_->read(buf.data(), RIFF_TAG_SIZE); - xmpData_["Xmp.video.ImageLength"] = Exiv2::getULong(buf.data(), littleEndian); - break; - case pixelsPerMeterX: - io_->read(buf.data(), RIFF_TAG_SIZE); - xmpData_["Xmp.video.PixelPerMeterX"] = Exiv2::getULong(buf.data(), littleEndian); - break; - case pixelsPerMeterY: - io_->read(buf.data(), RIFF_TAG_SIZE); - xmpData_["Xmp.video.PixelPerMeterY"] = Exiv2::getULong(buf.data(), littleEndian); - break; - case numColors: - io_->read(buf.data(), RIFF_TAG_SIZE); - if (Exiv2::getULong(buf.data(), littleEndian) == 0) { - xmpData_["Xmp.video.NumOfColours"] = "Unspecified"; - } else { - xmpData_["Xmp.video.NumOfColours"] = Exiv2::getULong(buf.data(), littleEndian); - } - break; - case numImportantColors: - io_->read(buf.data(), RIFF_TAG_SIZE); - if (Exiv2::getULong(buf.data(), littleEndian)) { - xmpData_["Xmp.video.NumIfImpColours"] = Exiv2::getULong(buf.data(), littleEndian); - } else { - xmpData_["Xmp.video.NumOfImpColours"] = "All"; - } - break; - default: - break; - } - } - } else if (streamType_ == Audio) { - int c = 0; - const TagDetails* td; - enum audioFormatTags { encoding, numberOfChannels, audioSampleRate, avgBytesPerSec = 4, bitsPerSample = 7, last }; - for (audioFormatTags tag = encoding; tag != audioFormatTags::last; tag = static_cast(tag + 1)) { - io_->read(buf.data(), 2); - - switch (tag) { - case encoding: - td = Exiv2::find(audioEncodingValues, Exiv2::getUShort(buf.data(), littleEndian)); - if (td) { - xmpData_["Xmp.audio.Compressor"] = exvGettext(td->label_); - } else { - xmpData_["Xmp.audio.Compressor"] = Exiv2::getUShort(buf.data(), littleEndian); - } - break; - case numberOfChannels: - c = Exiv2::getUShort(buf.data(), littleEndian); - if (c == 1) - xmpData_["Xmp.audio.ChannelType"] = "Mono"; - else if (c == 2) - xmpData_["Xmp.audio.ChannelType"] = "Stereo"; - else if (c == 5) - xmpData_["Xmp.audio.ChannelType"] = "5.1 Surround Sound"; - else if (c == 7) - xmpData_["Xmp.audio.ChannelType"] = "7.1 Surround Sound"; - else - xmpData_["Xmp.audio.ChannelType"] = "Mono"; - break; - case audioSampleRate: - xmpData_["Xmp.audio.SampleRate"] = Exiv2::getUShort(buf.data(), littleEndian); - break; - case avgBytesPerSec: - xmpData_["Xmp.audio.SampleType"] = Exiv2::getUShort(buf.data(), littleEndian); - break; - case bitsPerSample: - xmpData_["Xmp.audio.BitsPerSample"] = Exiv2::getUShort(buf.data(), littleEndian); - io_->read(buf.data(), 2); - break; - default: - break; - } +void RiffVideo::StreamName(uint64_t size_) { + // This element contains a name for the stream. That stream name should only use plain ASCII, especially not UTF-8. + io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} + +void RiffVideo::readInfoListChunk(uint64_t size_) { + uint64_t current_size = DWORD; + while (current_size < size_) { + std::string type = readStringTag(io_); + size_t size = readDWORDTag(io_); + std::string content = readStringTag(io_, size); + auto it = Internal::infoTags.find(type); + if (it != Internal::infoTags.end()) { + xmpData_[it->second] = content; } + current_size += DWORD * 2 + size; } - io_->seek(cur_pos + size, BasicIo::beg); -} // RiffVideo::streamFormatHandler +} -double RiffVideo::returnSampleRate(Exiv2::DataBuf& buf, size_t divisor) { - return (static_cast(Exiv2::getULong(buf.data(), littleEndian)) / divisor); -} // RiffVideo::returnSampleRate +void RiffVideo::readMoviList(uint64_t size_) { + io_->seekOrThrow(io_->tell() + size_ - DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} -const char* RiffVideo::printAudioEncoding(uint64_t i) { - auto td = Exiv2::find(audioEncodingValues, i); - if (td) - return exvGettext(td->label_); +void RiffVideo::readVPRPChunk(uint64_t size_) { +#ifdef EXIV2_DEBUG_MESSAGES + EXV_INFO << "--> VideoFormatToken = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> VideoStandard = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> VerticalRefreshRate = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> HTotalInT = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> VTotalInLines = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> FrameAspectRatio Height = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> FrameAspectRatio Width = " << readWORDTag(io_) << std::endl; + EXV_INFO << "--> FrameWidthInPixels = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> FrameHeightInLines = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> CompressedBMHeight = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> FieldPerFrame = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> CompressedBMWidth = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> ValidBMHeight = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> ValidBMWidth = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> ValidBMXOffset = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> ValidBMYOffset = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> VideoXOffsetInT = " << readDWORDTag(io_) << std::endl; + EXV_INFO << "--> VideoYValidStartLine = " << readDWORDTag(io_) << std::endl; + if (LogMsg::info >= LogMsg::level() && LogMsg::handler()) + io_->seekOrThrow(io_->tell() - DWORD * 17, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +#endif + io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} + +void RiffVideo::readIndexChunk(uint64_t size_) { +#ifdef EXIV2_DEBUG_MESSAGES + uint64_t current_size = 0; + while (current_size < size_) { + EXV_DEBUG << "--> Identifier = " << readStringTag(io_) << "\t(" << current_size << "/" << size_ << ")" + << std::endl; + EXV_DEBUG << "--> Flags = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")" + << std::endl; + EXV_DEBUG << "--> Offset = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")" + << std::endl; + EXV_DEBUG << "--> Length = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")" + << std::endl; + current_size += DWORD * 4; + } + if (LogMsg::debug >= LogMsg::level() && LogMsg::handler()) + io_->seekOrThrow(io_->tell() - size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +#endif + io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} + +void RiffVideo::readDataChunk(uint64_t size_) { +#ifdef EXIV2_DEBUG_MESSAGES + EXV_INFO << "--> Data = " << readStringTag(io_, static_cast(size_)) << std::endl; + uint64_t readed_size = size_; + if (size_ % 2 != 0) { + EXV_INFO << "--> pad byte = " << readStringTag(io_, 1) << std::endl; + readed_size += 1; + } + if (LogMsg::info >= LogMsg::level() && LogMsg::handler()) + io_->seekOrThrow(io_->tell() - readed_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +#endif + io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData); + if (size_ % 2 != 0) + io_->seekOrThrow(io_->tell() + 1, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} + +void RiffVideo::readJunk(uint64_t size_) { + io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData); +} + +std::string RiffVideo::getStreamType(uint32_t stream) { + if (stream == 1) + return "Mono"; + else if (stream == 2) + return "Stereo"; + else if (stream == 5) + return "5.1 Surround Sound"; + else if (stream == 7) + return "7.1 Surround Sound"; + else + return "Mono"; +} + +void RiffVideo::fillDuration(double frame_rate, size_t frame_count) { + if (frame_rate == 0) + return; - return "Undefined"; -} // RiffVideo::printAudioEncoding + auto duration = static_cast(frame_count * 1000. / frame_rate); + xmpData_["Xmp.video.FileDataRate"] = io_->size() / (1048576. * duration); + xmpData_["Xmp.video.Duration"] = duration; // Duration in number of seconds +} // RiffVideo::fillDuration void RiffVideo::fillAspectRatio(size_t width, size_t height) { if (height == 0) @@ -1213,18 +793,10 @@ void RiffVideo::fillAspectRatio(size_t width, size_t height) { break; default: xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + break; } -} // RiffVideo::fillAspectRatio - -void RiffVideo::fillDuration(double frame_rate, size_t frame_count) { - if (frame_rate == 0) - return; - - auto duration = static_cast(frame_count * 1000. / frame_rate); - xmpData_["Xmp.video.FileDataRate"] = io_->size() / (1048576. * duration); - xmpData_["Xmp.video.Duration"] = duration; // Duration in number of seconds -} // RiffVideo::fillDuration +} Image::UniquePtr newRiffInstance(BasicIo::UniquePtr io, bool /*create*/) { auto image = std::make_unique(std::move(io)); @@ -1235,8 +807,8 @@ Image::UniquePtr newRiffInstance(BasicIo::UniquePtr io, bool /*create*/) { } bool isRiffType(BasicIo& iIo, bool advance) { - const int32_t len = 2; - const unsigned char RiffVideoId[4] = {'R', 'I', 'F', 'F'}; + constexpr int len = 4; + const unsigned char RiffVideoId[len] = {'R', 'I', 'F', 'F'}; byte buf[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()) { @@ -1244,7 +816,7 @@ bool isRiffType(BasicIo& iIo, bool advance) { } bool matched = (memcmp(buf, RiffVideoId, len) == 0); if (!advance || !matched) { - iIo.seek(-len, BasicIo::cur); + iIo.seek(-1 * len, BasicIo::cur); } return matched; } diff --git a/test/data/Free_Test_Data_500KB_WAV.wav b/test/data/Free_Test_Data_500KB_WAV.wav new file mode 100644 index 00000000..ed89b3ea Binary files /dev/null and b/test/data/Free_Test_Data_500KB_WAV.wav differ diff --git a/test/data/flame.avi b/test/data/flame.avi new file mode 100644 index 00000000..e8e96e30 Binary files /dev/null and b/test/data/flame.avi differ diff --git a/test/data/sample_640x360.mov b/test/data/sample_640x360.mov new file mode 100644 index 00000000..ddfdf8bd Binary files /dev/null and b/test/data/sample_640x360.mov differ diff --git a/test/data/sample_960x540.asf b/test/data/sample_960x540.asf new file mode 100644 index 00000000..aa2588cb Binary files /dev/null and b/test/data/sample_960x540.asf differ diff --git a/test/data/test_reference_files/Free_Test_Data_500KB_WAV.wav.out b/test/data/test_reference_files/Free_Test_Data_500KB_WAV.wav.out new file mode 100644 index 00000000..1563901d --- /dev/null +++ b/test/data/test_reference_files/Free_Test_Data_500KB_WAV.wav.out @@ -0,0 +1,9 @@ +Xmp.video.FileSize XmpText 6 512764 512764 +Xmp.video.MimeType XmpText 10 video/riff video/riff +Xmp.video.Container XmpText 4 RIFF RIFF +Xmp.video.FileType XmpText 4 WAVE WAVE +Xmp.audio.Compressor XmpText 13 Microsoft PCM Microsoft PCM +Xmp.audio.ChannelType XmpText 4 Mono Mono +Xmp.audio.SampleRate XmpText 10 2970615808 2970615808 +Xmp.audio.SampleType XmpText 10 1633943568 1633943568 +Xmp.audio.BitsPerSample XmpText 10 3536871796 3536871796 diff --git a/test/data/test_reference_files/flame.avi.out b/test/data/test_reference_files/flame.avi.out new file mode 100644 index 00000000..50991460 --- /dev/null +++ b/test/data/test_reference_files/flame.avi.out @@ -0,0 +1,26 @@ +Xmp.video.FileSize XmpText 6 289280 289280 +Xmp.video.MimeType XmpText 10 video/riff video/riff +Xmp.video.Container XmpText 4 RIFF RIFF +Xmp.video.FileType XmpText 4 AVI AVI +Xmp.video.MicroSecPerFrame XmpText 5 28571 28571 +Xmp.video.MaxDataRate XmpText 5 95326 95326 +Xmp.video.FrameCount XmpText 3 110 110 +Xmp.audio.ChannelType XmpText 4 Mono Mono +Xmp.video.StreamCount XmpText 4 3074 3074 +Xmp.video.Width XmpText 3 256 256 +Xmp.video.Height XmpText 3 240 240 +Xmp.video.AspectRatio XmpText 3 1:1 1:1 +Xmp.video.FileDataRate XmpText 11 8.78036e-05 8.78036e-05 +Xmp.video.Duration XmpText 4 3142 3142 +Xmp.video.Codec XmpText 4 cvid cvid +Xmp.video.FrameRate XmpText 2 35 35 +Xmp.video.VideoQuality XmpText 1 0 0 +Xmp.video.VideoSampleSize XmpText 1 0 0 +Xmp.video.Planes XmpText 1 1 1 +Xmp.video.PixelDepth XmpText 2 24 24 +Xmp.video.Compressor XmpText 4 IV41 IV41 +Xmp.video.ImageLength XmpText 6 138240 138240 +Xmp.video.PixelPerMeterX XmpText 1 0 0 +Xmp.video.PixelPerMeterY XmpText 1 0 0 +Xmp.video.NumOfColours XmpText 10 1263424842 1263424842 +Xmp.video.NumIfImpColours XmpText 4 1816 1816 diff --git a/test/data/test_reference_files/sample_640x360.mov.out b/test/data/test_reference_files/sample_640x360.mov.out new file mode 100644 index 00000000..563baf1d --- /dev/null +++ b/test/data/test_reference_files/sample_640x360.mov.out @@ -0,0 +1,50 @@ +Xmp.video.FileSize XmpText 8 0.548162 0.548162 +Xmp.video.MimeType XmpText 15 video/quicktime video/quicktime +Xmp.video.MajorBrand XmpText 25 Apple QuickTime (.MOV/QT) Apple QuickTime (.MOV/QT) +Xmp.video.MinorVersion XmpText 3 512 512 +Xmp.video.CompatibleBrands XmpSeq 1 Apple QuickTime (.MOV/QT) Apple QuickTime (.MOV/QT) +Xmp.video.MovieHeaderVersion XmpText 1 0 0 +Xmp.video.DateUTC XmpText 1 0 0 +Xmp.video.ModificationDate XmpText 1 0 0 +Xmp.video.TimeScale XmpText 4 1000 1000 +Xmp.video.Duration XmpText 5 13347 13347 +Xmp.video.PreferredRate XmpText 1 1 1 +Xmp.video.PreferredVolume XmpText 3 100 100 +Xmp.video.PreviewTime XmpText 1 0 0 +Xmp.video.PreviewDuration XmpText 1 0 0 +Xmp.video.PosterTime XmpText 1 0 0 +Xmp.video.SelectionTime XmpText 1 0 0 +Xmp.video.SelectionDuration XmpText 1 0 0 +Xmp.video.CurrentTime XmpText 1 0 0 +Xmp.video.NextTrackID XmpText 1 2 2 +Xmp.video.TrackHeaderVersion XmpText 1 0 0 +Xmp.video.TrackCreateDate XmpText 1 0 0 +Xmp.video.TrackModifyDate XmpText 1 0 0 +Xmp.video.TrackID XmpText 1 1 1 +Xmp.video.TrackDuration XmpText 2 13 13 +Xmp.video.TrackLayer XmpText 1 0 0 +Xmp.video.TrackVolume XmpText 1 0 0 +Xmp.video.Width XmpText 3 640 640 +Xmp.video.Height XmpText 3 360 360 +Xmp.video.MediaHeaderVersion XmpText 1 0 0 +Xmp.video.MediaCreateDate XmpText 1 0 0 +Xmp.video.MediaModifyDate XmpText 1 0 0 +Xmp.video.MediaTimeScale XmpText 5 30000 30000 +Xmp.video.MediaDuration XmpText 2 13 13 +Xmp.video.MediaLangCode XmpText 1 0 0 +Xmp.video.HandlerClass XmpText 12 Data Handler Data Handler +Xmp.video.HandlerType XmpText 3 URL URL +Xmp.video.GraphicsMode XmpText 7 srcCopy srcCopy +Xmp.video.OpColor XmpText 1 0 0 +Xmp.video.URL XmpText 0 +Xmp.video.Codec XmpText 39 MP4 Base w/ AVC ext [ISO 14496-12:2005] MP4 Base w/ AVC ext [ISO 14496-12:2005] +Xmp.video.VendorID XmpText 6 FFmpeg FFmpeg +Xmp.video.SourceImageWidth XmpText 3 640 640 +Xmp.video.SourceImageHeight XmpText 3 360 360 +Xmp.video.XResolution XmpText 2 72 72 +Xmp.video.YResolution XmpText 2 72 72 +Xmp.video.Compressor XmpText 22 Lavc57.107.100 libx264 Lavc57.107.100 libx264 +Xmp.video.BitDepth XmpText 2 24 24 +Xmp.video.FrameRate XmpText 8 0.999001 0.999001 +Xmp.video.SoftwareVersion XmpText 13 Lavf57.83.100 Lavf57.83.100 +Xmp.video.AspectRatio XmpText 4 16:9 16:9 diff --git a/test/data/test_reference_files/sample_960x540.asf.out b/test/data/test_reference_files/sample_960x540.asf.out new file mode 100644 index 00000000..38c508d4 --- /dev/null +++ b/test/data/test_reference_files/sample_960x540.asf.out @@ -0,0 +1,15 @@ +Xmp.video.FileSize XmpText 8 0.553182 0.553182 +Xmp.video.MimeType XmpText 9 video/asf video/asf +Xmp.video.FileID XmpText 36 00000000-0000-0000-0000-000000000000 00000000-0000-0000-0000-000000000000 +Xmp.video.FileLength XmpText 6 580053 580053 +Xmp.video.CreationDate XmpText 18 116444736000000000 116444736000000000 +Xmp.video.DataPackets XmpText 3 181 181 +Xmp.video.duration XmpText 9 164460000 164460000 +Xmp.video.SendDuration XmpText 9 133460000 133460000 +Xmp.video.Preroll XmpText 4 3100 3100 +Xmp.video.MaxBitRate XmpText 6 200000 200000 +Xmp.video.ExtendedContentDescription XmpSeq 1 major_brand: mp42, minor_version: 0, compatible_brands: mp42mp41isomavc1, WM/EncodingSettings: Lavf57.83.100, major_brand: mp42, minor_version: 0, compatible_brands: mp42mp41isomavc1, WM/EncodingSettings: Lavf57.83.100, +Xmp.video.TimeOffset XmpText 1 0 0 +Xmp.audio.CodecName XmpText 9 msmpeg4v3 msmpeg4v3 +Xmp.audio.CodecInfo XmpText 4 MP43 MP43 +Xmp.video.AspectRatio XmpText 3 1:1 1:1 diff --git a/tests/regression_tests/test_regression_allfiles.py b/tests/regression_tests/test_regression_allfiles.py index 4ce711c2..bf212d21 100644 --- a/tests/regression_tests/test_regression_allfiles.py +++ b/tests/regression_tests/test_regression_allfiles.py @@ -29,6 +29,11 @@ def get_valid_files(data_dir): ".webp", ".xmp", ".mp4", + ".asf", + ".avi", + ".mkv", + ".wav", + ".mov" ] excludes = [