Rework Asf and Riff formats ; add test/data for video support

main
Mohamed Ali Chebbi 2 years ago
parent bbb3f5afa8
commit cb7a48f84a

@ -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
<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
// 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"
@ -33,10 +9,13 @@
// included header files
#include "image.hpp"
// *****************************************************************************
// namespace extensions
namespace Exiv2 {
// *****************************************************************************
// class definitions
/*!
@brief Class to access ASF video files.
*/
@ -56,11 +35,14 @@ class EXIV2API AsfVideo : public Image {
method to get a temporary reference.
*/
explicit AsfVideo(BasicIo::UniquePtr io);
//@}
//! @name NOT Implemented
//@{
//! Copy constructor
AsfVideo(const AsfVideo& rhs) = delete;
AsfVideo(const AsfVideo&) = delete;
//! Assignment operator
AsfVideo& operator=(const AsfVideo& rhs) = delete;
AsfVideo& operator=(const AsfVideo&) = delete;
//@}
//! @name Manipulators
@ -71,35 +53,19 @@ class EXIV2API AsfVideo : public Image {
//! @name Accessors
//@{
std::string mimeType() const override;
[[nodiscard]] std::string mimeType() const override;
//@}
private:
static constexpr size_t GUID_SIZE = 37;
static constexpr size_t CODEC_TYPE_VIDEO = 1;
static constexpr size_t CODEC_TYPE_AUDIO = 2;
static constexpr size_t BYTE = 1;
static constexpr size_t WCHAR = 2;
static constexpr size_t WORD = 2;
static constexpr size_t DWORD = 4;
static constexpr size_t QWORD = 8;
static constexpr size_t GUID = 16;
class AsfObject {
byte IdBuf_[GUID + 1];
class HeaderReader {
DataBuf IdBuf_;
uint64_t size_;
uint64_t remaining_size_;
public:
explicit AsfObject(BasicIo::UniquePtr& io) {
DataBuf SizeBuf(QWORD + 1);
io->read(IdBuf_, GUID);
io->read(SizeBuf.data(), QWORD);
size_ = Exiv2::getULongLong(SizeBuf.data(), littleEndian);
remaining_size_ = size_ - GUID - QWORD;
}
explicit HeaderReader(BasicIo::UniquePtr& io);
[[nodiscard]] uint64_t getSize() const {
return size_;
@ -109,7 +75,7 @@ class EXIV2API AsfVideo : public Image {
return remaining_size_;
}
[[nodiscard]] byte* getId() {
[[nodiscard]] DataBuf& getId() {
return IdBuf_;
}
};
@ -158,6 +124,8 @@ class EXIV2API AsfVideo : public Image {
*/
void extendedContentDescription();
void DegradableJPEGMedia();
/*!
@brief Calculates Aspect Ratio of a video, and stores it in the
respective XMP container.
@ -168,12 +136,6 @@ class EXIV2API AsfVideo : public Image {
//! Variable to store height and width of a video frame.
uint64_t height_, width_;
[[nodiscard]] uint64_t readQWORDTag();
[[nodiscard]] uint32_t readDWORDTag();
[[nodiscard]] uint16_t readWORDTag();
[[nodiscard]] std::string readStringWCHAR(uint16_t length);
[[nodiscard]] std::string readString(uint16_t length);
}; // Class AsfVideo
// *****************************************************************************
@ -192,5 +154,3 @@ EXIV2API Image::UniquePtr newAsfInstance(BasicIo::UniquePtr io, bool create);
EXIV2API bool isAsfType(BasicIo& iIo, bool advance);
} // namespace Exiv2
#endif // #ifndef ASFVIDEO_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
* asize_t 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"
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// included header files
#include "exif.hpp"
#include "exiv2lib_export.h"
#include "image.hpp"
// *****************************************************************************
// namespace extensions
namespace Exiv2 {
// *****************************************************************************
@ -53,11 +29,14 @@ class EXIV2API RiffVideo : public Image {
method to get a temporary reference.
*/
explicit RiffVideo(BasicIo::UniquePtr io);
//@}
//! @name NOT Implemented
//@{
//! Copy constructor
RiffVideo(const RiffVideo& rhs) = delete;
RiffVideo(const RiffVideo&) = delete;
//! Assignment operator
RiffVideo& operator=(const RiffVideo& rhs) = delete;
RiffVideo& operator=(const RiffVideo&) = delete;
//@}
@ -71,147 +50,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_;
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.
@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 tagDecoder(Exiv2::DataBuf& buf, size_t size);
void readAviHeader();
/*!
@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.
@brief Interpret stream header list element (strh), and save it in the respective XMP container.
*/
void junkHandler(size_t size);
void readStreamHeader();
/*!
@brief Interpret Stream tag information, and save it
in the respective XMP container.
@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 streamHandler(size_t size);
void readStreamFormat(uint64_t size_);
/*!
@brief Interpret Stream Format tag information, and save it
in the respective XMP container.
@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 streamFormatHandler(size_t size);
void readStreamData(uint64_t size_);
/*!
@brief Interpret Riff Header tag information, and save it
in the respective XMP container.
@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 aviHeaderTagsHandler(size_t size);
void StreamName(uint64_t size_);
/*!
@brief Interpret Riff List tag information, and save it
in the respective XMP container.
@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 listHandler(size_t size);
void readInfoListChunk(uint64_t size_);
/*!
@brief Interpret Riff Stream Data tag information, and save it
in the respective XMP container.
@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 streamDataTagHandler(size_t size);
/*!
@brief Interpret INFO tag information, and save it
in the respective XMP container.
*/
void infoTagsHandler();
/*!
@brief Interpret Nikon Tags related to Video information, and
save it in the respective XMP container.
*/
void nikonTagsHandler();
void readMoviList(uint64_t size_);
/*!
@brief Interpret OpenDML tag information, and save it
in the respective XMP container.
@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 odmlTagsHandler();
//! @brief Skips Particular Blocks of Metadata List.
void skipListData();
void readVPRPChunk(uint64_t size_);
/*!
@brief Interprets DateTimeOriginal tag or stream name tag
information, and save it in the respective XMP container.
@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.
@param i parameter used to overload function
*/
void dateTimeOriginal(size_t size, int i = 0);
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.
@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.
*/
[[nodiscard]] static double returnSampleRate(Exiv2::DataBuf& buf, size_t divisor = 1);
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.
@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 fillAspectRatio(size_t width = 1, size_t height = 1);
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.
@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);
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 ";
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 RIFF_CHUNK_ID_MOVI = "MOVI";
static constexpr auto RIFF_CHUNK_ID_DATA = "DATA";
static constexpr auto RIFF_CHUNK_ID_HDRL = "HDRL";
static constexpr auto RIFF_CHUNK_ID_STRL = "STRL";
static constexpr auto RIFF_CHUNK_ID_LIST = "LIST";
static constexpr auto RIFF_CHUNK_ID_JUNK = "JUNK";
static constexpr auto RIFF_CHUNK_ID_AVIH = "AVIH";
static constexpr auto RIFF_CHUNK_ID_STRH = "STRH";
static constexpr auto RIFF_CHUNK_ID_STRF = "STRF";
static constexpr auto RIFF_CHUNK_ID_FMT = "FMT ";
static constexpr auto RIFF_CHUNK_ID_STRN = "STRN";
static constexpr auto RIFF_CHUNK_ID_STRD = "STRD";
static constexpr auto RIFF_CHUNK_ID_IDIT = "IDIT";
static constexpr auto RIFF_CHUNK_ID_INFO = "INFO";
static constexpr auto RIFF_CHUNK_ID_NCDT = "NCDT";
static constexpr auto RIFF_CHUNK_ID_ODML = "ODML";
//! Variable to check the end of metadata traversing.
bool continueTraversing_;
//! Variable which stores current stream being processsed.
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";
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.
@ -222,5 +194,3 @@ EXIV2API Image::UniquePtr newRiffInstance(BasicIo::UniquePtr io, bool create);
EXIV2API bool isRiffType(BasicIo& iIo, bool advance);
} // namespace Exiv2
#endif // RIFFVIDEO_HPP

@ -1,63 +1,26 @@
// ***************************************************************** -*- 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) <mail.abu.to@gmail.com>
History: 08-Aug-12, AB: created
Credits: See header file
Spec: https://exse.eyewated.com/fls/54b3ed95bbfb1a92.pdf
*/
// *****************************************************************************
// SPDX-License-Identifier: GPL-2.0-or-later
// included header files
#include <iostream>
#include "config.h"
#include "asfvideo.hpp"
#include <iostream>
#include "basicio.hpp"
#include "convert.hpp"
#include "config.h"
#include "error.hpp"
#include "futils.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
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<std::string, std::string> GUIDReferenceTags = {
/// Top-level ASF object GUIDS
{"75B22630-668E-11CF-A6D9-00AA0062CE6C", "Header"},
{"75B22636-668E-11CF-A6D9-00AA0062CE6C", "Data"},
@ -149,52 +112,55 @@ 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"}};
/*!
@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(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[0] = returnHex(buf.data()[3] / 0x10);
GUID[1] = returnHex(buf.data()[3] % 0x10);
GUID[2] = returnHex(buf.data()[2] / 0x10);
GUID[3] = returnHex(buf.data()[2] % 0x10);
GUID[4] = returnHex(buf.data()[1] / 0x10);
GUID[5] = returnHex(buf.data()[1] % 0x10);
GUID[6] = returnHex(buf.data()[0] / 0x10);
GUID[7] = returnHex(buf.data()[0] % 0x10);
GUID[9] = returnHex(buf.data()[5] / 0x10);
GUID[10] = returnHex(buf.data()[5] % 0x10);
GUID[11] = returnHex(buf.data()[4] / 0x10);
GUID[12] = returnHex(buf.data()[4] % 0x10);
GUID[14] = returnHex(buf.data()[7] / 0x10);
GUID[15] = returnHex(buf.data()[7] % 0x10);
GUID[16] = returnHex(buf.data()[6] / 0x10);
GUID[17] = returnHex(buf.data()[6] % 0x10);
GUID[19] = returnHex(buf.data()[8] / 0x10);
GUID[20] = returnHex(buf.data()[8] % 0x10);
GUID[21] = returnHex(buf.data()[9] / 0x10);
GUID[22] = returnHex(buf.data()[9] % 0x10);
GUID[24] = returnHex(buf.data()[10] / 0x10);
GUID[25] = returnHex(buf.data()[10] % 0x10);
GUID[26] = returnHex(buf.data()[11] / 0x10);
GUID[27] = returnHex(buf.data()[11] % 0x10);
GUID[28] = returnHex(buf.data()[12] / 0x10);
GUID[29] = returnHex(buf.data()[12] % 0x10);
GUID[30] = returnHex(buf.data()[13] / 0x10);
GUID[31] = returnHex(buf.data()[13] % 0x10);
GUID[32] = returnHex(buf.data()[14] / 0x10);
GUID[33] = returnHex(buf.data()[14] % 0x10);
GUID[34] = returnHex(buf.data()[15] / 0x10);
GUID[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;
}
/*!
@ -203,7 +169,7 @@ void getGUID(byte buf[], char GUID[]) {
@param buf Exiv2 byte buffer
@return Returns true if the buffer data is equivalent to Header GUID.
*/
bool isASFType(byte buf[]) {
bool isASFType(const byte buf[]) {
return buf[0] == 0x30 && buf[1] == 0x26 && buf[2] == 0xb2 && buf[3] == 0x75 && buf[4] == 0x8e && buf[5] == 0x66 &&
buf[6] == 0xcf && buf[7] == 0x11 && buf[8] == 0xa6 && buf[9] == 0xd9 && buf[10] == 0x00 && buf[11] == 0xaa &&
buf[12] == 0x00 && buf[13] == 0x62 && buf[14] == 0xce && buf[15] == 0x6c;
@ -249,57 +215,66 @@ void AsfVideo::readMetadata() {
aspectRatio();
} // AsfVideo::readMetadata
void AsfVideo::decodeBlock() {
AsfObject obj(io_);
AsfVideo::HeaderReader::HeaderReader(BasicIo::UniquePtr& io) : IdBuf_(GUID) {
if (io->size() >= io->tell() + QWORD + QWORD) {
IdBuf_ = io->read(GUID);
size_ = readQWORDTag(io);
if (size_ >= GUID + QWORD)
remaining_size_ = size_ - GUID - QWORD;
}
}
char guid[GUID_SIZE] = "";
getGUID(obj.getId(), guid);
void AsfVideo::decodeBlock() {
HeaderReader header(io_);
std::string guid = getGUID(header.getId());
auto tv = find(GUIDReferenceTags, guid);
if (tv) {
if (compareTag(exvGettext(tv->label_), "Header")) {
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);
DataBuf reserved(BYTE + 1);
io_->read(reserved.data(), BYTE);
io_->read(reserved.data(), BYTE);
io_->seekOrThrow(io_->tell() + BYTE * 2, BasicIo::beg,
ErrorCode::kerFailedToReadImageData); // skip two reserved tags
for (uint32_t i = 0; i < nb_headers; i++) {
AsfObject obj(io_);
char guid[GUID_SIZE] = "";
getGUID(obj.getId(), guid);
auto tag = find(GUIDReferenceTags, guid);
if (tag) {
if (compareTag(exvGettext(tag->label_), "File_Properties"))
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 (compareTag(exvGettext(tag->label_), "Stream_Properties"))
else if (tag->second == "Stream_Properties")
streamProperties();
else if (compareTag(exvGettext(tag->label_), "Header_Extension"))
else if (tag->second == "Header_Extension")
headerExtension();
else if (compareTag(exvGettext(tag->label_), "Codec_List"))
else if (tag->second == "Codec_List")
codecList();
else if (compareTag(exvGettext(tag->label_), "Extended_Content_Description"))
else if (tag->second == "Extended_Content_Description")
extendedContentDescription();
else if (compareTag(exvGettext(tag->label_), "Content_Description"))
else if (tag->second == "Content_Description")
contentDescription();
else if (compareTag(exvGettext(tag->label_), "Extended_Stream_Properties"))
else if (tag->second == "Extended_Stream_Properties")
extendedStreamProperties();
else
io_->seek(io_->tell() + obj.getRemainingSize(), BasicIo::beg);
else if (tag->second == "Degradable_JPEG_Media") {
DegradableJPEGMedia();
} else
io_->seek(io_->tell() + obj.getRemainingSize(), BasicIo::beg);
io_->seekOrThrow(io_->tell() + others.getRemainingSize(), BasicIo::beg,
ErrorCode::kerFailedToReadImageData);
} else
io_->seekOrThrow(io_->tell() + others.getRemainingSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData);
}
} else
io_->seek(io_->tell() + obj.getRemainingSize(), BasicIo::beg);
io_->seekOrThrow(io_->tell() + header.getRemainingSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData);
} else
io_->seek(io_->tell() + obj.getRemainingSize(), BasicIo::beg);
io_->seekOrThrow(io_->tell() + header.getRemainingSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData);
} // AsfVideo::decodeBlock
void AsfVideo::extendedStreamProperties() {
xmpData()["Xmp.video.StartTimecode"] = readQWORDTag(); // Start Time
xmpData()["Xmp.video.EndTimecode"] = readWORDTag(); // End Time
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
@ -312,13 +287,13 @@ void AsfVideo::extendedStreamProperties() {
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(); // Average Time Per Frame
uint16_t stream_name_count = readWORDTag();
uint16_t payload_ext_sys_count = readWORDTag();
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();
uint16_t stream_length = readWORDTag(io_);
if (stream_length)
io_->seek(io_->tell() + stream_length, BasicIo::beg); // ignore Stream name
}
@ -326,38 +301,51 @@ void AsfVideo::extendedStreamProperties() {
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();
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
void AsfVideo::DegradableJPEGMedia() {
uint32_t width = readDWORDTag(io_);
width_ = width;
xmpData_["Xmp.video.Width"] = width;
uint32_t height = readDWORDTag(io_);
height_ = height;
xmpData_["Xmp.video.Height"] = height;
io_->seek(io_->tell() + WORD * 3 /*3 Reserved*/, BasicIo::beg);
uint32_t interchange_data_length = readWORDTag(io_);
io_->seek(io_->tell() + interchange_data_length /*Interchange data*/, BasicIo::beg);
}
void AsfVideo::streamProperties() {
byte streamTypedBuf[GUID];
io_->read(streamTypedBuf,GUID);
char stream_type[GUID_SIZE] = "";
getGUID(streamTypedBuf, stream_type);
DataBuf streamTypedBuf = io_->read(GUID);
auto stream_type = getGUID(streamTypedBuf);
enum streamTypeInfo { Audio = 1, Video = 2 };
int stream = 0;
auto tag_stream_type = find(GUIDReferenceTags, stream_type);
if (tag_stream_type) {
if (compareTag(exvGettext(tag_stream_type->label_), "Audio_Media"))
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 (compareTag(exvGettext(tag_stream_type->label_), "Video_Media"))
else if (tag_stream_type->second == "Video_Media")
stream = Video;
io_->seek(io_->tell() + GUID, BasicIo::beg); // ignore Error Correction Type
uint64_t time_offset = readQWORDTag();
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;
auto specific_data_length = readDWORDTag();
auto correction_data_length = readDWORDTag();
auto specific_data_length = readDWORDTag(io_);
auto correction_data_length = readDWORDTag(io_);
io_->seek(io_->tell() + WORD /*Flags*/ + DWORD /*Reserved*/ + specific_data_length + correction_data_length,
BasicIo::beg);
@ -367,62 +355,62 @@ void AsfVideo::streamProperties() {
void AsfVideo::codecList() {
io_->seek(io_->tell() + GUID /*reserved*/, BasicIo::beg);
auto entries_count = readDWORDTag();
auto entries_count = readDWORDTag(io_);
for (uint32_t i = 0; i < entries_count; i++) {
uint16_t codec_type = readWORDTag() * 2;
uint16_t codec_type = readWORDTag(io_) * 2;
std::string codec = (codec_type == 1) ? "Xmp.video" : "Xmp.audio";
uint16_t codec_name_length = readWORDTag() * 2;
uint16_t codec_name_length = readWORDTag(io_) * 2;
if (codec_name_length)
xmpData()[codec + std::string(".CodecName")] = readStringWCHAR(codec_name_length);
xmpData()[codec + std::string(".CodecName")] = readStringWcharTag(io_, codec_name_length);
uint16_t codec_desc_length = readWORDTag();
uint16_t codec_desc_length = readWORDTag(io_);
if (codec_desc_length)
xmpData()[codec + std::string(".CodecDescription")] = readStringWCHAR(codec_desc_length);
xmpData()[codec + std::string(".CodecDescription")] = readStringWcharTag(io_, codec_desc_length);
uint16_t codec_info_length = readWORDTag();
uint16_t codec_info_length = readWORDTag(io_);
if (codec_info_length)
xmpData()[codec + std::string(".CodecInfo")] = readString(codec_info_length);
xmpData()[codec + std::string(".CodecInfo")] = readStringTag(io_, codec_info_length);
}
} // AsfVideo::codecList
void AsfVideo::headerExtension() {
io_->seek(io_->tell() + GUID /*reserved1*/ + WORD /*Reserved2*/, BasicIo::beg);
auto header_ext_data_length = readDWORDTag();
auto header_ext_data_length = readDWORDTag(io_);
io_->seek(io_->tell() + header_ext_data_length, BasicIo::beg);
} // AsfVideo::headerExtension
void AsfVideo::extendedContentDescription() {
uint16_t content_descriptor_count = readWORDTag();
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();
uint16_t descriptor_name_length = readWORDTag(io_);
if (descriptor_name_length)
value += readStringWCHAR(descriptor_name_length); // Descriptor Name
value += readStringWcharTag(io_, descriptor_name_length); // Descriptor Name
uint16_t descriptor_value_data_type = readWORDTag();
uint16_t descriptor_value_length = readWORDTag();
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(": ") + readStringWCHAR(descriptor_value_length);
value += std::string(": ") + readStringWcharTag(io_, descriptor_value_length);
break;
case 1 /*BYTE array */:
value += std::string(": ") + readString(descriptor_value_length);
value += std::string(": ") + readStringTag(io_, descriptor_value_length);
break;
case 2 /*BOOL*/:
value += std::string(": ") + std::to_string(readWORDTag());
value += std::string(": ") + std::to_string(readWORDTag(io_));
break;
case 3 /*DWORD */:
value += std::string(": ") + std::to_string(readDWORDTag());
value += std::string(": ") + std::to_string(readDWORDTag(io_));
break;
case 4 /*QWORD */:
value += std::string(": ") + std::to_string(readQWORDTag());
value += std::string(": ") + std::to_string(readQWORDTag(io_));
break;
case 5 /*WORD*/:
value += std::string(": ") + std::to_string(readWORDTag());
value += std::string(": ") + std::to_string(readWORDTag(io_));
;
break;
}
@ -434,82 +422,48 @@ void AsfVideo::extendedContentDescription() {
} // AsfVideo::extendedContentDescription
void AsfVideo::contentDescription() {
uint16_t title_length = readWORDTag();
uint16_t author_length = readWORDTag();
uint16_t copyright_length = readWORDTag();
uint16_t desc_length = readWORDTag();
uint16_t rating_length = readWORDTag();
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 (title_length)
xmpData()["Xmp.video.Title"] = readStringWCHAR(title_length);
xmpData()["Xmp.video.Title"] = readStringWcharTag(io_, title_length);
if (author_length)
xmpData()["Xmp.video.Author"] = readStringWCHAR(author_length);
xmpData()["Xmp.video.Author"] = readStringWcharTag(io_, author_length);
if (copyright_length)
xmpData()["Xmp.video.Copyright"] = readStringWCHAR(copyright_length);
xmpData()["Xmp.video.Copyright"] = readStringWcharTag(io_, copyright_length);
if (desc_length)
xmpData()["Xmp.video.Description"] = readStringWCHAR(desc_length);
xmpData()["Xmp.video.Description"] = readStringWcharTag(io_, desc_length);
if (rating_length)
xmpData()["Xmp.video.Rating"] = readStringWCHAR(rating_length);
xmpData()["Xmp.video.Rating"] = readStringWcharTag(io_, rating_length);
} // AsfVideo::extendedContentDescription
uint64_t AsfVideo::readQWORDTag() {
DataBuf FieldBuf(QWORD);
io_->read(FieldBuf.data(), QWORD);
uint64_t field = Util::getUint64_t(FieldBuf);
return field;
}
uint32_t AsfVideo::readDWORDTag() {
DataBuf FieldBuf(DWORD);
io_->read(FieldBuf.data(), DWORD);
uint32_t field = Exiv2::getULong(FieldBuf.data(), littleEndian);
return field;
}
uint16_t AsfVideo::readWORDTag() {
DataBuf FieldBuf(WORD);
io_->read(FieldBuf.data(), WORD);
uint16_t field = Exiv2::getUShort(FieldBuf.data(), littleEndian);
return field;
}
std::string AsfVideo::readStringWCHAR(uint16_t length) {
DataBuf FieldBuf(length);
io_->read(FieldBuf.data(), length);
return Util::toString16(FieldBuf);
}
std::string AsfVideo::readString(uint16_t length) {
DataBuf FieldBuf(length);
io_->read(FieldBuf.data(), length);
return Exiv2::toString(FieldBuf.data());
}
void AsfVideo::fileProperties() {
byte FileIddBuf[GUID];
io_->read(FileIddBuf, GUID);
char fileId[GUID_SIZE] = "";
getGUID(FileIddBuf, fileId);
xmpData()["Xmp.video.FileID"] = fileId;
xmpData()["Xmp.video.FileLength"] = readQWORDTag();
xmpData()["Xmp.video.CreationDate"] = readQWORDTag();
xmpData()["Xmp.video.DataPackets"] = readQWORDTag();
xmpData()["Xmp.video.Duration"] = readQWORDTag();
xmpData()["Xmp.video.SendDuration"] = readQWORDTag();
xmpData()["Xmp.video.Preroll"] = readQWORDTag();
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();
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<double>(width_) / height_;
aspectRatio = floor(aspectRatio * 10) / 10;
xmpData()["Xmp.video.AspectRatio"] = aspectRatio;
@ -553,9 +507,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;

@ -4,6 +4,7 @@
#include <cmath>
#include <cstring>
#include "enforce.hpp"
std::string string_from_unterminated(const char* data, size_t data_length) {
if (data_length == 0) {
@ -13,8 +14,8 @@ 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<char>(n + 48);
return static_cast<char>(n + 55);
@ -36,12 +37,34 @@ std::string toString16(Exiv2::DataBuf& buf) {
return os.str();
}
uint64_t getUint64_t(Exiv2::DataBuf& buf) {
uint64_t temp = 0;
uint64_t readQWORDTag(BasicIo::UniquePtr& io) {
enforce(QWORD <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
DataBuf FieldBuf = io->read(QWORD);
return FieldBuf.read_uint64(0, littleEndian);
}
uint32_t readDWORDTag(BasicIo::UniquePtr& io) {
enforce(DWORD <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
DataBuf FieldBuf = io->read(DWORD);
return FieldBuf.read_uint32(0, littleEndian);
}
for (int i = 0; i < 8; ++i) {
temp = temp + static_cast<uint64_t>(buf.data()[i] * (pow(static_cast<float>(256), i)));
uint16_t readWORDTag(BasicIo::UniquePtr& io) {
enforce(WORD <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
DataBuf FieldBuf = io->read(WORD);
return FieldBuf.read_uint16(0, littleEndian);
}
return temp;
std::string readStringWcharTag(BasicIo::UniquePtr& io, uint16_t length) {
enforce(length <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
DataBuf FieldBuf = io->read(length);
return toString16(FieldBuf);
}
} // namespace Util
std::string readStringTag(BasicIo::UniquePtr& io, uint16_t length) {
enforce(length <= io->size() - io->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
DataBuf FieldBuf = io->read(length);
return Exiv2::toString(FieldBuf.data());
}
} // namespace Exiv2

@ -5,6 +5,7 @@
#include <string>
#include "types.hpp"
#include "basicio.hpp"
/*!
@brief Convert a (potentially not null terminated) array into a
std::string.
@ -22,14 +23,14 @@
*/
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);
/*!
@brief Function used to read data from data buffer, reads 16-bit character
@ -37,9 +38,27 @@ char returnHEX(int n);
@param buf Exiv2 data buffer, which stores the information
@return Returns std::string object .
*/
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;
std::string toString16(Exiv2::DataBuf& buf);
//! Function used to convert buffer data into 64-bit Integer, information stored in littleEndian format
uint64_t getUint64_t(Exiv2::DataBuf& buf);
} // namespace Util
[[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, uint16_t length) ;
[[nodiscard]] std::string readStringTag(Exiv2::BasicIo::UniquePtr& io, uint16_t length=DWORD) ;
} // namespace Exiv2
#endif // HELPER_FUNCTIONS_HPP

File diff suppressed because it is too large Load Diff

@ -1,9 +1,9 @@
Xmp.video.FileSize XmpText 7 0.48901 0.48901
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 6 Stereo Stereo
Xmp.audio.SampleRate XmpText 5 44100 44100
Xmp.audio.SampleType XmpText 5 45328 45328
Xmp.audio.BitsPerSample XmpText 2 16 16
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

@ -1,11 +1,11 @@
Xmp.video.FileSize XmpText 8 0.275879 0.275879
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 7 93.0918 93.0918
Xmp.video.MaxDataRate XmpText 5 95326 95326
Xmp.video.FrameCount XmpText 3 110 110
Xmp.video.StreamCount XmpText 1 1 1
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
@ -21,6 +21,5 @@ 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 11 Unspecified Unspecified
Xmp.video.NumOfImpColours XmpText 3 All All
Xmp.video.Junk XmpText 0
Xmp.video.NumOfColours XmpText 10 1263424842 1263424842
Xmp.video.NumIfImpColours XmpText 4 1816 1816

Loading…
Cancel
Save