Merge pull request #2458 from mohamedchebbii/TestVideoData

Rework Asf, Riff video, test data and fix OSS-Fuzz issues
main
Rosen Penev 2 years ago committed by GitHub
commit aa16a54a7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,31 +1,7 @@
// ***************************************************************** -*- C++ -*- // SPDX-License-Identifier: GPL-2.0-or-later
/* // Spec : Advanced Systems Format (ASF) Specification : Revision 01.20.05 :
* Copyright (C) 2004-2021 Exiv2 authors // https://exse.eyewated.com/fls/54b3ed95bbfb1a92.pdf
* This program is part of the Exiv2 distribution. #pragma once
*
* 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
<a href="mailto:mail.abu.to@gmail.com">mail.abu.to@gmail.com</a>
@date 08-Aug-12, AB: created
*/
#ifndef ASFVIDEO_HPP
#define ASFVIDEO_HPP
// ***************************************************************************** // *****************************************************************************
#include "exiv2lib_export.h" #include "exiv2lib_export.h"
@ -72,10 +48,29 @@ class EXIV2API AsfVideo : public Image {
[[nodiscard]] std::string mimeType() const override; [[nodiscard]] std::string mimeType() const override;
//@} //@}
private: private:
static constexpr size_t ASF_TAG_SIZE = 0x4; static constexpr size_t CODEC_TYPE_VIDEO = 1;
static constexpr size_t BUFF_MIN_SIZE = 0x8; static constexpr size_t CODEC_TYPE_AUDIO = 2;
static constexpr size_t GUI_SIZE = 16;
static constexpr size_t GUID_SIZE = 37; 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: protected:
/*! /*!
@ -83,15 +78,6 @@ class EXIV2API AsfVideo : public Image {
position. Calls tagDecoder() or skips to next tag, if required. position. Calls tagDecoder() or skips to next tag, if required.
*/ */
void decodeBlock(); 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 @brief Interpret File_Properties tag information, and save it in
the respective XMP container. the respective XMP container.
@ -112,27 +98,26 @@ class EXIV2API AsfVideo : public Image {
in the respective XMP container. in the respective XMP container.
@param size Size of the data block used to store Tag Data. @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 @brief Interpret Extended_Stream_Properties tag information, and
save it in the respective XMP container. 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 @brief Interpret Header_Extension tag information, and save it in
the respective XMP container. 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, @brief Interpret Metadata, Extended_Content_Description,
Metadata_Library tag information, and save it in the respective Metadata_Library tag information, and save it in the respective
XMP container. 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 @brief Calculates Aspect Ratio of a video, and stores it in the
respective XMP container. respective XMP container.
@ -140,12 +125,6 @@ class EXIV2API AsfVideo : public Image {
void aspectRatio(); void aspectRatio();
private: 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. //! Variable to store height and width of a video frame.
uint64_t height_, width_; 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. //! Check if the file iIo is a Windows Asf Video.
EXIV2API bool isAsfType(BasicIo& iIo, bool advance); EXIV2API bool isAsfType(BasicIo& iIo, bool advance);
} // namespace Exiv2 } // namespace Exiv2
#endif // #ifndef ASFVIDEO_HPP_

@ -1,34 +1,10 @@
// ***************************************************************** -*- C++ -*- // SPDX-License-Identifier: GPL-2.0-or-later
/* #pragma once
* 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_
// *****************************************************************************
#include "exiv2lib_export.h"
// included header files
#include "exif.hpp" #include "exif.hpp"
#include "exiv2lib_export.h"
#include "image.hpp" #include "image.hpp"
// *****************************************************************************
// namespace extensions
namespace Exiv2 { namespace Exiv2 {
// ***************************************************************************** // *****************************************************************************
@ -57,7 +33,6 @@ class EXIV2API RiffVideo : public Image {
//! @name Manipulators //! @name Manipulators
//@{ //@{
void printStructure(std::ostream& out, PrintStructureOption option, size_t depth) override;
void readMetadata() override; void readMetadata() override;
void writeMetadata() override; void writeMetadata() override;
//@} //@}
@ -65,128 +40,140 @@ class EXIV2API RiffVideo : public Image {
//! @name Accessors //! @name Accessors
//@{ //@{
[[nodiscard]] std::string mimeType() const override; [[nodiscard]] std::string mimeType() const override;
[[nodiscard]] static const char* printAudioEncoding(uint64_t i);
//@} //@}
protected: 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 @brief Interpret MainAVIHeader (avih) structure, and save it in the respective XMP container.
position. Calls tagDecoder() or skips to next tag, if required. @param size Size of the data block used to store Tag Information.
*/ */
void decodeBlock(); void readAviHeader();
/*!
@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 Stream Format tag information, and save it @brief Interpret stream header list element (strh), and save it in the respective XMP container.
in the respective XMP container. */
@param size Size of the data block used to store Tag Information. void readStreamHeader();
*/
void streamFormatHandler(size_t size);
/*! /*!
@brief Interpret Riff Header tag information, and save it @brief Interpret stream header list element (strf), and save it in the respective XMP container.
in the respective XMP container. @param size Size of the data block used to store Tag Information.
@param size Size of the data block used to store Tag Information. */
*/ void readStreamFormat(uint64_t size_);
void aviHeaderTagsHandler(size_t size);
/*! /*!
@brief Interpret Riff List tag information, and save it @brief Interpret Additional header data (strd), and save it in the respective XMP container.
in the respective XMP container. @param size Size of the data block used to store Tag Information.
@param size Size of the data block used to store Tag Information. */
*/ void readStreamData(uint64_t size_);
void listHandler(size_t size);
/*! /*!
@brief Interpret Riff Stream Data tag information, and save it @brief Interpret stream header list element (strn) , and save it in the respective XMP container.
in the respective XMP container. @param size Size of the data block used to store Tag Information.
@param size Size of the data block used to store Tag Information. */
*/ void StreamName(uint64_t size_);
void streamDataTagHandler(size_t size);
/*! /*!
@brief Interpret INFO tag information, and save it @brief Interpret INFO List Chunk, and save it in the respective XMP container.
in the respective XMP container. @param size Size of the data block used to store Tag Information.
*/ */
void infoTagsHandler(); void readInfoListChunk(uint64_t size_);
/*! /*!
@brief Interpret Nikon Tags related to Video information, and @brief Interpret Riff Stream Data tag information, and save it in the respective XMP container.
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 nikonTagsHandler(); */
void readMoviList(uint64_t size_);
/*! /*!
@brief Interpret OpenDML tag information, and save it @brief Interpret Video Properties Header chunk, and save it in the respective XMP container.
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 odmlTagsHandler(); */
//! @brief Skips Particular Blocks of Metadata List. void readVPRPChunk(uint64_t size_);
void skipListData();
/*! /*!
@brief Interprets DateTimeOriginal tag or stream name tag @brief Interpret Riff INdex Chunk, and save it in the respective XMP container.
information, and save it in the respective XMP container. @param size Size of the data block used to store Tag Information.
@param size Size of the data block used to store Tag Information. */
@param i parameter used to overload function void readIndexChunk(uint64_t size_);
*/
void dateTimeOriginal(size_t size, int i = 0);
/*! /*!
@brief Calculates Sample Rate of a particular stream. @brief Interpret Riff Stream Chunk, and save it in the respective XMP container.
@param buf Data buffer with the dividend. @param size Size of the data block used to store Tag Information.
@param divisor The Divisor required to calculate sample rate. */
@return Return the sample rate of the stream. void readDataChunk(uint64_t size_);
*/
[[nodiscard]] static double returnSampleRate(Exiv2::DataBuf& buf, size_t divisor = 1);
/*! /*!
@brief Calculates Aspect Ratio of a video, and stores it in the @brief Interpret Junk Chunk and save it in the respective XMP container.
respective XMP container. @param size Size of the data block used to store Tag Information.
@param width Width of the video. */
@param height Height of the video. void readJunk(uint64_t size_);
*/
void fillAspectRatio(size_t width = 1, size_t height = 1); std::string getStreamType(uint32_t stream);
/*! /*!
@brief Calculates Duration of a video, and stores it in the @brief Calculates Duration of a video, and stores it in the respective XMP container.
respective XMP container. @param frame_rate Frame rate of the video.
@param frame_rate Frame rate of the video. @param frame_count Total number of frames present in the video.
@param frame_count Total number of frames present in the video. */
*/
void fillDuration(double frame_rate, size_t frame_count); void fillDuration(double frame_rate, size_t frame_count);
[[nodiscard]] static bool equalsRiffTag(Exiv2::DataBuf& buf, const char* str); /*!
@brief Calculates Aspect Ratio of a video, and stores it in the respective XMP container.
static void copyTagValue(DataBuf& buf_dest, DataBuf& buf_src, size_t index = RIFF_TAG_SIZE); @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_; int streamType_;
}; // Class RiffVideo }; // 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. @brief Create a new RiffVideo instance and return an auto-pointer to it.
Caller owns the returned object and the auto-pointer ensures that Caller owns the returned object and the auto-pointer ensures that
it will be deleted. it will be deleted.
@ -197,5 +184,3 @@ EXIV2API Image::UniquePtr newRiffInstance(BasicIo::UniquePtr io, bool create);
EXIV2API bool isRiffType(BasicIo& iIo, bool advance); EXIV2API bool isRiffType(BasicIo& iIo, bool advance);
} // namespace Exiv2 } // namespace Exiv2
#endif // RIFFVIDEO_HPP_

@ -1,62 +1,27 @@
// ***************************************************************** -*- C++ -*- // SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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) <mail.abu.to@gmail.com>
History: 08-Aug-12, AB: created
Credits: See header file
*/
// *****************************************************************************
// included header files // included header files
#include <iostream>
#include "config.h"
#include "asfvideo.hpp" #include "asfvideo.hpp"
#include <iostream>
#include "basicio.hpp" #include "basicio.hpp"
#include "convert.hpp" #include "config.h"
#include "enforce.hpp"
#include "error.hpp" #include "error.hpp"
#include "futils.hpp" #include "futils.hpp"
#include "helper_functions.hpp" #include "helper_functions.hpp"
#include "tags.hpp"
#include "tags_int.hpp"
#include "types.hpp"
// + standard includes
#include <cassert>
#include <cctype>
#include <cstring>
// ***************************************************************************** // *****************************************************************************
// class member definitions // class member definitions
namespace Exiv2::Internal { namespace Exiv2::Internal {
/*! /*!
TagVocabulary Look-up list for ASF Type Video Files Look-up list for ASF Type Video Files
Associates the GUID of a TagVocabulary with its TagVocabulary Name(i.e. Human Readable Form) Associates the GUID with its Name(i.e. Human Readable Form)
Tags have been diferentiated into Various Categories. Tags have been diferentiated into Various Categories.
The categories have been listed above the TagVocabulary Groups The categories have been listed above Groups
see : see :
- https://fr.wikipedia.org/wiki/Advanced_Systems_Format - https://fr.wikipedia.org/wiki/Advanced_Systems_Format
- https://exse.eyewated.com/fls/54b3ed95bbfb1a92.pdf - https://exse.eyewated.com/fls/54b3ed95bbfb1a92.pdf
*/ */
constexpr const TagVocabulary GUIDReferenceTags[] = { const std::map<std::string, std::string> GUIDReferenceTags = {
/// Top-level ASF object GUIDS /// Top-level ASF object GUIDS
{"75B22630-668E-11CF-A6D9-00AA0062CE6C", "Header"}, {"75B22630-668E-11CF-A6D9-00AA0062CE6C", "Header"},
{"75B22636-668E-11CF-A6D9-00AA0062CE6C", "Data"}, {"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"}, {"6698B84E-0AFA-4330-AEB2-1C0A98D7A44D", "Payload_Extension_System_Encryption_Sample_ID"},
{"00E1AF06-7BEC-11D1-A582-00C04FC29CFB", "Payload_Extension_System_Degradable_JPEG"}}; {"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. @brief Function used to calculate GUID, Tags comprises of 16 bytes.
The Buffer contains the TagVocabulary in Binary Form. The information is then The Buffer contains the TagVocabulary in Binary Form. The information is then
parsed into a character array GUID. parsed into a character array GUID.
https://fr.wikipedia.org/wiki/Globally_unique_identifier
*/ */
void getGUID(const byte buf[], char GUID[]) {
int i; std::string getGUID(DataBuf& buf) {
for (i = 0; i < 4; ++i) { std::string GUID(36, '-');
GUID[(3 - i) * 2] = Util::returnHEX(buf[i] / 0x10); if (buf.size() >= 16) {
GUID[(3 - i) * 2 + 1] = Util::returnHEX(buf[i] % 0x10); GUID.at(0) = returnHex(buf.data()[3] / 0x10);
} GUID.at(1) = returnHex(buf.data()[3] % 0x10);
for (i = 4; i < 6; ++i) { GUID.at(2) = returnHex(buf.data()[2] / 0x10);
GUID[(9 - i) * 2 + 1] = Util::returnHEX(buf[i] / 0x10); GUID.at(3) = returnHex(buf.data()[2] % 0x10);
GUID[(9 - i) * 2 + 2] = Util::returnHEX(buf[i] % 0x10); GUID.at(4) = returnHex(buf.data()[1] / 0x10);
} GUID.at(5) = returnHex(buf.data()[1] % 0x10);
for (i = 6; i < 8; ++i) { GUID.at(6) = returnHex(buf.data()[0] / 0x10);
GUID[(14 - i) * 2] = Util::returnHEX(buf[i] / 0x10); GUID.at(7) = returnHex(buf.data()[0] % 0x10);
GUID[(14 - i) * 2 + 1] = Util::returnHEX(buf[i] % 0x10);
} GUID.at(9) = returnHex(buf.data()[5] / 0x10);
for (i = 8; i < 10; ++i) { GUID.at(10) = returnHex(buf.data()[5] % 0x10);
GUID[i * 2 + 3] = Util::returnHEX(buf[i] / 0x10); GUID.at(11) = returnHex(buf.data()[4] / 0x10);
GUID[i * 2 + 4] = Util::returnHEX(buf[i] % 0x10); GUID.at(12) = returnHex(buf.data()[4] % 0x10);
}
for (i = 10; i < 16; ++i) { GUID.at(14) = returnHex(buf.data()[7] / 0x10);
GUID[i * 2 + 4] = Util::returnHEX(buf[i] / 0x10); GUID.at(15) = returnHex(buf.data()[7] % 0x10);
GUID[i * 2 + 5] = Util::returnHEX(buf[i] % 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_); IoCloser closer(*io_);
clearMetadata(); clearMetadata();
continueTraversing_ = true;
io_->seek(0, BasicIo::beg); io_->seek(0, BasicIo::beg);
height_ = width_ = 1; height_ = width_ = 1;
xmpData()["Xmp.video.FileSize"] = io_->size() / 1048576.; xmpData()["Xmp.video.FileSize"] = io_->size() / 1048576.;
xmpData()["Xmp.video.FileName"] = io_->path();
xmpData()["Xmp.video.MimeType"] = mimeType(); xmpData()["Xmp.video.MimeType"] = mimeType();
while (continueTraversing_) decodeBlock();
decodeBlock();
aspectRatio(); aspectRatio();
} // AsfVideo::readMetadata } // AsfVideo::readMetadata
void AsfVideo::decodeBlock() { AsfVideo::HeaderReader::HeaderReader(BasicIo::UniquePtr& io) : IdBuf_(GUID) {
DataBuf buf(BUFF_MIN_SIZE + 1); if (io->size() >= io->tell() + GUID + QWORD) {
uint64_t size = 0; IdBuf_ = io->read(GUID);
uint64_t cur_pos = io_->tell();
byte guidBuf[GUI_SIZE];
io_->read(guidBuf, GUI_SIZE);
if (io_->eof()) { size_ = readQWORDTag(io);
continueTraversing_ = false; if (size_ >= GUID + QWORD)
return; remaining_size_ = size_ - GUID - QWORD;
} }
}
char GUID[GUID_SIZE] = ""; // the getGUID function write the GUID[36], void AsfVideo::decodeBlock() {
HeaderReader header(io_);
io_->read(buf.data(), BUFF_MIN_SIZE); std::string guid = getGUID(header.getId());
size = Util::getUint64_t(buf);
auto tv = GUIDReferenceTags.find(guid);
getGUID(guidBuf, GUID); if (tv != GUIDReferenceTags.end()) {
auto tv = Exiv2::find(GUIDReferenceTags, GUID); if (tv->second == "Header") {
if (tv) { DataBuf nbHeadersBuf(DWORD + 1);
auto tagDecoder = [&](const Internal::TagVocabulary* tv, uint64_t size) { io_->read(nbHeadersBuf.data(), DWORD);
uint64_t cur_pos = io_->tell();
DataBuf buf(1000); uint32_t nb_headers = Exiv2::getULong(nbHeadersBuf.data(), littleEndian);
unsigned long count = 0, tempLength = 0; io_->seekOrThrow(io_->tell() + BYTE * 2, BasicIo::beg,
Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::xmpSeq); ErrorCode::kerFailedToReadImageData); // skip two reserved tags
for (uint32_t i = 0; i < nb_headers; i++) {
if (compareTag(exvGettext(tv->label_), "Header")) { HeaderReader others(io_);
localPosition_ = 0; auto guid = getGUID(others.getId());
io_->read(buf.data(), 4); auto tag = GUIDReferenceTags.find(guid);
io_->read(buf.data(), 2); if (tag != GUIDReferenceTags.end()) {
if (tag->second == "File_Properties")
while (localPosition_ < cur_pos + size) fileProperties();
decodeBlock(); 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")) } else
fileProperties(); io_->seekOrThrow(io_->tell() + header.getRemainingSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData);
} // AsfVideo::decodeBlock
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 if (compareTag(exvGettext(tv->label_), "Language_List")) { void AsfVideo::extendedStreamProperties() {
std::memset(buf.data(), 0x0, buf.size()); xmpData()["Xmp.video.StartTimecode"] = readQWORDTag(io_); // Start Time
io_->read(buf.data(), 2); xmpData()["Xmp.video.EndTimecode"] = readWORDTag(io_); // End Time
count = Exiv2::getUShort(buf.data(), littleEndian);
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--) { for (uint16_t i = 0; i < payload_ext_sys_count; i++) {
std::memset(buf.data(), 0x0, buf.size()); io_->seek(io_->tell() + GUID, BasicIo::beg); // ignore Extension System ID
io_->read(buf.data(), 1); io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Extension Data Size
tempLength = static_cast<int>(buf.data()[0]); 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); void AsfVideo::DegradableJPEGMedia() {
v->read(Util::toString16(buf)); uint32_t width = readDWORDTag(io_);
} width_ = width;
xmpData().add(Exiv2::XmpKey("Xmp.video.TrackLang"), v.get()); xmpData_["Xmp.video.Width"] = width;
}
io_->seek(cur_pos + size, BasicIo::beg); uint32_t height = readDWORDTag(io_);
localPosition_ = io_->tell(); height_ = height;
}; // AsfVideo::tagDecoder xmpData_["Xmp.video.Height"] = height;
tagDecoder(tv, size - 24); io_->seek(io_->tell() + WORD * 3 /*3 Reserved*/, BasicIo::beg);
} else
io_->seek(cur_pos + size, BasicIo::beg);
localPosition_ = io_->tell(); uint32_t interchange_data_length = readWORDTag(io_);
} // AsfVideo::decodeBlock 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) { auto stream_type = getGUID(streamTypedBuf);
uint64_t cur_pos = io_->tell(), avgTimePerFrame = 0;
DataBuf buf(BUFF_MIN_SIZE);
static int previousStream;
io_->seek(cur_pos + 48, BasicIo::beg);
io_->read(buf.data(), 2); enum streamTypeInfo { Audio = 1, Video = 2 };
streamNumber_ = Exiv2::getUShort(buf.data(), littleEndian); int stream = 0;
io_->read(buf.data(), 2); auto tag_stream_type = GUIDReferenceTags.find(stream_type);
io_->read(buf.data(), BUFF_MIN_SIZE); if (tag_stream_type != GUIDReferenceTags.end()) {
avgTimePerFrame = Util::getUint64_t(buf); if (tag_stream_type->second == "Audio_Media")
stream = Audio;
else if (tag_stream_type->second == "Video_Media")
stream = Video;
if (previousStream < streamNumber_ && avgTimePerFrame != 0) io_->seek(io_->tell() + GUID, BasicIo::beg); // ignore Error Correction Type
xmpData()["Xmp.video.FrameRate"] = 10000000. / avgTimePerFrame;
previousStream = streamNumber_; uint64_t time_offset = readQWORDTag(io_);
io_->seek(cur_pos + size, BasicIo::beg); if (stream == Video)
} // AsfVideo::extendedStreamProperties xmpData()["Xmp.video.TimeOffset"] = time_offset;
else if (stream == Audio)
xmpData()["Xmp.audio.TimeOffset"] = time_offset;
void AsfVideo::contentDescription(uint64_t size) { auto specific_data_length = readDWORDTag(io_);
const size_t pos = io_->tell(); auto correction_data_length = readDWORDTag(io_);
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<const char*>(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
void AsfVideo::streamProperties() { io_->seek(io_->tell() + WORD /*Flags*/ + DWORD /*Reserved*/ + specific_data_length + correction_data_length,
DataBuf buf(20); BasicIo::beg);
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<int>(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_->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 } // AsfVideo::streamProperties
void AsfVideo::codecList() { void AsfVideo::codecList() {
DataBuf buf(200); io_->seek(io_->tell() + GUID /*reserved*/, BasicIo::beg);
io_->read(buf.data(), GUI_SIZE); auto entries_count = readDWORDTag(io_);
std::memset(buf.data(), 0x0, buf.size()); for (uint32_t i = 0; i < entries_count; i++) {
io_->read(buf.data(), 4); uint16_t codec_type = readWORDTag(io_) * 2;
int codecCount = Exiv2::getULong(buf.data(), littleEndian), descLength = 0, codecType = 0; std::string codec = (codec_type == 1) ? "Xmp.video" : "Xmp.audio";
while (codecCount--) { uint16_t codec_name_length = readWORDTag(io_) * 2;
std::memset(buf.data(), 0x0, buf.size()); if (codec_name_length)
io_->read(buf.data(), 2); xmpData()[codec + std::string(".CodecName")] = readStringWcharTag(io_, codec_name_length);
codecType = Exiv2::getUShort(buf.data(), littleEndian);
uint16_t codec_desc_length = readWORDTag(io_);
io_->read(buf.data(), 2); if (codec_desc_length)
descLength = Exiv2::getUShort(buf.data(), littleEndian) * 2; xmpData()[codec + std::string(".CodecDescription")] = readStringWcharTag(io_, codec_desc_length);
io_->read(buf.data(), descLength); uint16_t codec_info_length = readWORDTag(io_);
if (codecType == 1) Internal::enforce(codec_info_length && codec_info_length + io_->tell() < io_->size(),
xmpData()["Xmp.video.Codec"] = Util::toString16(buf); Exiv2::ErrorCode::kerCorruptedMetadata);
else if (codecType == 2) xmpData()[codec + std::string(".CodecInfo")] = readStringTag(io_, codec_info_length);
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);
} }
} // AsfVideo::codecList } // AsfVideo::codecList
void AsfVideo::headerExtension(uint64_t size) { void AsfVideo::headerExtension() {
uint64_t cur_pos = io_->tell(); io_->seek(io_->tell() + GUID /*reserved1*/ + WORD /*Reserved2*/, BasicIo::beg);
DataBuf buf(20); auto header_ext_data_length = readDWORDTag(io_);
io_->read(buf.data(), 18); io_->seek(io_->tell() + header_ext_data_length, BasicIo::beg);
buf.data()[4] = '\0';
io_->read(buf.data(), 4);
while (localPosition_ < cur_pos + size)
decodeBlock();
io_->seek(cur_pos + size, BasicIo::beg);
} // AsfVideo::headerExtension } // AsfVideo::headerExtension
void AsfVideo::metadataHandler(int meta) { void AsfVideo::extendedContentDescription() {
DataBuf buf(5000); uint16_t content_descriptor_count = readWORDTag(io_);
io_->read(buf.data(), 2); std::string value;
uint16_t recordCount = Exiv2::getUShort(buf.data(), littleEndian), nameLength = 0, dataLength = 0, dataType = 0;
Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::xmpSeq); for (uint16_t i = 0; i < content_descriptor_count; i++) {
byte guidBuf[GUI_SIZE]; uint16_t descriptor_name_length = readWORDTag(io_);
char fileID[GUID_SIZE] = ""; if (descriptor_name_length)
value += readStringWcharTag(io_, descriptor_name_length); // Descriptor Name
while (recordCount--) {
std::memset(buf.data(), 0x0, buf.size()); uint16_t descriptor_value_data_type = readWORDTag(io_);
uint16_t descriptor_value_length = readWORDTag(io_);
if (meta == 1 || meta == 3) { if (descriptor_value_length) {
io_->read(buf.data(), 4); // Descriptor Value
io_->read(buf.data(), 2); switch (descriptor_value_data_type) {
nameLength = Exiv2::getUShort(buf.data(), littleEndian); case 0 /*Unicode string */:
io_->read(buf.data(), 2); value += std::string(": ") + readStringWcharTag(io_, descriptor_value_length);
dataType = Exiv2::getUShort(buf.data(), littleEndian); break;
io_->read(buf.data(), 4); case 1 /*BYTE array */:
dataLength = Exiv2::getULong(buf.data(), littleEndian); value += std::string(": ") + readStringTag(io_, descriptor_value_length);
break;
if (nameLength > 5000) { case 2 /*BOOL*/:
#ifndef SUPPRESS_WARNINGS value += std::string(": ") + std::to_string(readWORDTag(io_));
EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 " break;
<< " entries considered invalid; not read.\n"; case 3 /*DWORD */:
#endif value += std::string(": ") + std::to_string(readDWORDTag(io_));
io_->seek(io_->tell() + nameLength, BasicIo::beg); break;
} else { case 4 /*QWORD */:
io_->read(buf.data(), nameLength); value += std::string(": ") + std::to_string(readQWORDTag(io_));
} break;
case 5 /*WORD*/:
v->read(Util::toString16(buf)); value += std::string(": ") + std::to_string(readWORDTag(io_));
if (dataType == 6) { ;
io_->read(guidBuf, GUI_SIZE); break;
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);
} }
} }
value += std::string(", ");
}
else if (meta == 2) { xmpData()["Xmp.video.ExtendedContentDescription"] = value;
io_->read(buf.data(), 2); } // AsfVideo::extendedContentDescription
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);
}
if (dataType == 0) { // Unicode String void AsfVideo::contentDescription() {
v->read(Util::toString16(buf)); uint16_t title_length = readWORDTag(io_);
} else if (dataType == 2 || dataType == 5) { // 16-bit Unsigned Integer uint16_t author_length = readWORDTag(io_);
v->read(Exiv2::toString(Exiv2::getUShort(buf.data(), littleEndian))); uint16_t copyright_length = readWORDTag(io_);
} else if (dataType == 3) { // 32-bit Unsigned Integer uint16_t desc_length = readWORDTag(io_);
v->read(Exiv2::toString(Exiv2::getULong(buf.data(), littleEndian))); uint16_t rating_length = readWORDTag(io_);
} 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()));
}
}
if (meta == 1) { if (title_length)
xmpData().add(Exiv2::XmpKey("Xmp.video.Metadata"), v.get()); xmpData()["Xmp.video.Title"] = readStringWcharTag(io_, title_length);
} 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
void AsfVideo::fileProperties() { if (author_length)
DataBuf buf(BUFF_MIN_SIZE); xmpData()["Xmp.video.Author"] = readStringWcharTag(io_, author_length);
byte guidBuf[GUI_SIZE]; if (copyright_length)
io_->read(guidBuf, GUI_SIZE); xmpData()["Xmp.video.Copyright"] = readStringWcharTag(io_, copyright_length);
char fileID[GUID_SIZE] = "";
int count = 7;
getGUID(guidBuf, fileID);
xmpData()["Xmp.video.FileID"] = fileID;
const TagDetails* td; if (desc_length)
xmpData()["Xmp.video.Description"] = readStringWcharTag(io_, desc_length);
while (count--) { if (rating_length)
td = Exiv2::find(filePropertiesTags, (count + 1)); xmpData()["Xmp.video.Rating"] = readStringWcharTag(io_, rating_length);
io_->read(buf.data(), BUFF_MIN_SIZE);
if (count == 0) { } // AsfVideo::extendedContentDescription
buf.data()[4] = '\0';
io_->read(buf.data(), 4);
io_->read(buf.data(), 4);
}
if (count == 3 || count == 2) { void AsfVideo::fileProperties() {
xmpData()[exvGettext(td->label_)] = Util::getUint64_t(buf) / 10000; DataBuf FileIddBuf = io_->read(GUID);
} else { xmpData()["Xmp.video.FileID"] = getGUID(FileIddBuf);
xmpData()[exvGettext(td->label_)] = Util::getUint64_t(buf); 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 } // AsfVideo::fileProperties
void AsfVideo::aspectRatio() { void AsfVideo::aspectRatio() {
// TODO - Make a better unified method to handle all cases of Aspect Ratio // TODO - Make a better unified method to handle all cases of Aspect Ratio
if (!height_)
return;
double aspectRatio = static_cast<double>(width_) / height_; double aspectRatio = static_cast<double>(width_) / height_;
aspectRatio = floor(aspectRatio * 10) / 10; aspectRatio = floor(aspectRatio * 10) / 10;
xmpData()["Xmp.video.AspectRatio"] = aspectRatio; xmpData()["Xmp.video.AspectRatio"] = aspectRatio;
@ -692,9 +510,8 @@ Image::UniquePtr newAsfInstance(BasicIo::UniquePtr io, bool /*create*/) {
} }
bool isAsfType(BasicIo& iIo, bool advance) { bool isAsfType(BasicIo& iIo, bool advance) {
const int32_t len = 16; byte buf[GUID];
byte buf[len]; iIo.read(buf, GUID);
iIo.read(buf, len);
if (iIo.error() || iIo.eof()) { if (iIo.error() || iIo.eof()) {
return false; return false;

@ -3,7 +3,10 @@
#include "helper_functions.hpp" #include "helper_functions.hpp"
#include <cmath> #include <cmath>
#include <codecvt>
#include <cstring> #include <cstring>
#include <locale>
#include "enforce.hpp"
std::string string_from_unterminated(const char* data, size_t data_length) { std::string string_from_unterminated(const char* data, size_t data_length) {
if (data_length == 0) { if (data_length == 0) {
@ -13,35 +16,53 @@ std::string string_from_unterminated(const char* data, size_t data_length) {
return {data, StringLength}; return {data, StringLength};
} }
namespace Util { namespace Exiv2 {
char returnHEX(int n) { char returnHex(int n) {
if (n >= 0 && n <= 9) if (n >= 0 && n <= 9)
return static_cast<char>(n + 48); return static_cast<char>(n + 48);
return static_cast<char>(n + 55); return static_cast<char>(n + 55);
} }
std::string toString16(Exiv2::DataBuf& buf) { std::string utf16ToUtf8(const std::wstring& wstr) {
std::ostringstream os; using convert_typeX = std::codecvt_utf8<wchar_t>;
char t; std::wstring_convert<convert_typeX, wchar_t> converterX;
for (size_t i = 0; i <= buf.size(); i += 2) { std::string str = converterX.to_bytes(wstr);
t = buf.data()[i] + 16 * buf.data()[i + 1]; str.erase(std::remove(str.begin(), str.end(), '\0'), str.end());
if (t == 0) { return str;
if (i)
os << '\0';
break;
}
os << t;
}
return os.str();
} }
uint64_t getUint64_t(Exiv2::DataBuf& buf) { uint64_t readQWORDTag(BasicIo::UniquePtr& io) {
uint64_t temp = 0; 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) { uint32_t readDWORDTag(BasicIo::UniquePtr& io) {
temp = temp + static_cast<uint64_t>(buf.data()[i] * (pow(static_cast<float>(256), i))); Internal::enforce(DWORD <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
} DataBuf FieldBuf = io->read(DWORD);
return temp; 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

@ -4,6 +4,7 @@
#define HELPER_FUNCTIONS_HPP #define HELPER_FUNCTIONS_HPP
#include <string> #include <string>
#include "basicio.hpp"
#include "types.hpp" #include "types.hpp"
/*! /*!
@brief Convert a (potentially not null terminated) array into a @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); 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 @brief Function used to convert a decimal number to its Hexadecimal
equivalent, then parsed into a character equivalent, then parsed into a character
@param n Integer which is to be parsed as Hexadecimal character @param n Integer which is to be parsed as Hexadecimal character
@return Return a Hexadecimal number, in 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 @brief The function utf16ToUtf8 takes a wide string wstr as input and converts it to a narrow string
array and stores it in std::string object. The conversion is performed using the std::wstring_convert class template and a std::codecvt_utf8 facet, which
@param buf Exiv2 data buffer, which stores the information implements conversion between UTF-8 and wide characters.
@return Returns std::string object . @param wstr : wide string
*/ @return Returns std::string object
std::string toString16(Exiv2::DataBuf& buf); */
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 } // namespace Exiv2
uint64_t getUint64_t(Exiv2::DataBuf& buf);
} // namespace Util
#endif // HELPER_FUNCTIONS_HPP #endif // HELPER_FUNCTIONS_HPP

@ -600,7 +600,6 @@ void MatroskaVideo::readMetadata() {
continueTraversing_ = true; continueTraversing_ = true;
height_ = width_ = 1; height_ = width_ = 1;
xmpData_["Xmp.video.FileName"] = io_->path();
xmpData_["Xmp.video.FileSize"] = io_->size() / bytesMB; xmpData_["Xmp.video.FileSize"] = io_->size() / bytesMB;
xmpData_["Xmp.video.MimeType"] = mimeType(); xmpData_["Xmp.video.MimeType"] = mimeType();

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -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

@ -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

@ -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

@ -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

@ -29,6 +29,11 @@ def get_valid_files(data_dir):
".webp", ".webp",
".xmp", ".xmp",
".mp4", ".mp4",
".asf",
".avi",
".mkv",
".wav",
".mov"
] ]
excludes = [ excludes = [

Loading…
Cancel
Save