add test/data for video support : make asfvideo more readable and fix some issues
This commit is contained in:
parent
5deee73141
commit
82ea6bef52
@ -74,10 +74,45 @@ class EXIV2API AsfVideo : public Image {
|
||||
std::string mimeType() const override;
|
||||
//@}
|
||||
private:
|
||||
static constexpr size_t ASF_TAG_SIZE = 0x4;
|
||||
static constexpr size_t BUFF_MIN_SIZE = 0x8;
|
||||
static constexpr size_t GUI_SIZE = 16;
|
||||
static constexpr size_t GUID_SIZE = 37;
|
||||
static constexpr size_t CODEC_TYPE_VIDEO = 1;
|
||||
static constexpr size_t CODEC_TYPE_AUDIO = 2;
|
||||
|
||||
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];
|
||||
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;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t getSize() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t getRemainingSize() const {
|
||||
return remaining_size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] byte* getId() {
|
||||
return IdBuf_;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
/*!
|
||||
@ -85,15 +120,6 @@ class EXIV2API AsfVideo : public Image {
|
||||
position. Calls tagDecoder() or skips to next tag, if required.
|
||||
*/
|
||||
void decodeBlock();
|
||||
/*!
|
||||
@brief Interpret tag information, and call the respective function
|
||||
to save it in the respective XMP container. Decodes a Tag
|
||||
Information and saves it in the respective XMP container, if
|
||||
the block size is small.
|
||||
@param tv Pointer to current tag,
|
||||
@param size Size of the data block used to store Tag Information.
|
||||
*/
|
||||
void tagDecoder(uint64_t size);
|
||||
/*!
|
||||
@brief Interpret File_Properties tag information, and save it in
|
||||
the respective XMP container.
|
||||
@ -114,27 +140,24 @@ class EXIV2API AsfVideo : public Image {
|
||||
in the respective XMP container.
|
||||
@param size Size of the data block used to store Tag Data.
|
||||
*/
|
||||
void contentDescription(uint64_t size);
|
||||
void contentDescription();
|
||||
/*!
|
||||
@brief Interpret Extended_Stream_Properties tag information, and
|
||||
save it in the respective XMP container.
|
||||
@param size Size of the data block used to store Tag Data.
|
||||
*/
|
||||
void extendedStreamProperties(uint64_t size);
|
||||
void extendedStreamProperties();
|
||||
/*!
|
||||
@brief Interpret Header_Extension tag information, and save it in
|
||||
the respective XMP container.
|
||||
@param size Size of the data block used to store Tag Data.
|
||||
*/
|
||||
void headerExtension(uint64_t size);
|
||||
void headerExtension();
|
||||
/*!
|
||||
@brief Interpret Metadata, Extended_Content_Description,
|
||||
Metadata_Library tag information, and save it in the respective
|
||||
XMP container.
|
||||
@param meta A default integer which helps to overload the function
|
||||
for various Tags that have a similar method of decoding.
|
||||
*/
|
||||
void metadataHandler(int meta = 1);
|
||||
void extendedContentDescription();
|
||||
|
||||
/*!
|
||||
@brief Calculates Aspect Ratio of a video, and stores it in the
|
||||
respective XMP container.
|
||||
@ -142,15 +165,15 @@ class EXIV2API AsfVideo : public Image {
|
||||
void aspectRatio();
|
||||
|
||||
private:
|
||||
//! Variable to check the end of metadata traversing.
|
||||
bool continueTraversing_;
|
||||
//! Variable which stores current position of the read pointer.
|
||||
uint64_t localPosition_;
|
||||
//! Variable which stores current stream being processsed.
|
||||
int streamNumber_;
|
||||
//! Variable to store height and width of a video frame.
|
||||
uint64_t height_, width_;
|
||||
|
||||
[[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
|
||||
|
||||
// *****************************************************************************
|
||||
|
||||
@ -180,6 +180,25 @@ class EXIV2API RiffVideo : public Image {
|
||||
static constexpr auto RIFF_CHUNK_HEADER_ICCP = "ICCP";
|
||||
static constexpr auto RIFF_CHUNK_HEADER_EXIF = "EXIF";
|
||||
static constexpr auto RIFF_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.
|
||||
|
||||
567
src/asfvideo.cpp
567
src/asfvideo.cpp
@ -22,6 +22,7 @@
|
||||
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
|
||||
*/
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
@ -148,17 +149,6 @@ constexpr const TagVocabulary GUIDReferenceTags[] = {
|
||||
{"6698B84E-0AFA-4330-AEB2-1C0A98D7A44D", "Payload_Extension_System_Encryption_Sample_ID"},
|
||||
{"00E1AF06-7BEC-11D1-A582-00C04FC29CFB", "Payload_Extension_System_Degradable_JPEG"}};
|
||||
|
||||
constexpr const TagDetails filePropertiesTags[] = {{7, "Xmp.video.FileLength"}, {6, "Xmp.video.CreationDate"},
|
||||
{5, "Xmp.video.DataPackets"}, {4, "Xmp.video.Duration"},
|
||||
{3, "Xmp.video.SendDuration"}, {2, "Xmp.video.Preroll"},
|
||||
{1, "Xmp.video.MaxBitRate"}};
|
||||
|
||||
constexpr const TagDetails contentDescriptionTags[] = {{0, "Xmp.video.Title"},
|
||||
{1, "Xmp.video.Author"},
|
||||
{2, "Xmp.video.Copyright"},
|
||||
{3, "Xmp.video.Description"},
|
||||
{4, "Xmp.video.Rating"}};
|
||||
|
||||
/*!
|
||||
@brief Function used to check equality of two Tags (ignores case).
|
||||
@param str1 char* Pointer to First TagVocabulary
|
||||
@ -248,404 +238,273 @@ void AsfVideo::readMetadata() {
|
||||
|
||||
IoCloser closer(*io_);
|
||||
clearMetadata();
|
||||
continueTraversing_ = true;
|
||||
io_->seek(0, BasicIo::beg);
|
||||
height_ = width_ = 1;
|
||||
|
||||
xmpData()["Xmp.video.FileSize"] = io_->size() / 1048576.;
|
||||
xmpData()["Xmp.video.MimeType"] = mimeType();
|
||||
|
||||
while (continueTraversing_)
|
||||
decodeBlock();
|
||||
decodeBlock();
|
||||
|
||||
aspectRatio();
|
||||
} // AsfVideo::readMetadata
|
||||
|
||||
void AsfVideo::decodeBlock() {
|
||||
DataBuf buf(BUFF_MIN_SIZE + 1);
|
||||
uint64_t size = 0;
|
||||
const Internal::TagVocabulary* tv;
|
||||
uint64_t cur_pos = io_->tell();
|
||||
AsfObject obj(io_);
|
||||
|
||||
byte guidBuf[GUI_SIZE];
|
||||
io_->read(guidBuf, GUI_SIZE);
|
||||
|
||||
if (io_->eof()) {
|
||||
continueTraversing_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
char GUID[GUID_SIZE] = ""; // the getGUID function write the GUID[36],
|
||||
|
||||
getGUID(guidBuf, GUID);
|
||||
tv = find(GUIDReferenceTags, GUID);
|
||||
|
||||
io_->read(buf.data(), BUFF_MIN_SIZE);
|
||||
size = Util::getUint64_t(buf);
|
||||
char guid[GUID_SIZE] = "";
|
||||
getGUID(obj.getId(), guid);
|
||||
|
||||
auto tv = find(GUIDReferenceTags, guid);
|
||||
if (tv) {
|
||||
auto tagDecoder = [&](const Internal::TagVocabulary* tv, uint64_t size) {
|
||||
uint64_t cur_pos = io_->tell();
|
||||
DataBuf buf(1000);
|
||||
unsigned long count = 0, tempLength = 0;
|
||||
Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::xmpSeq);
|
||||
if (compareTag(exvGettext(tv->label_), "Header")) {
|
||||
DataBuf nbHeadersBuf(DWORD + 1);
|
||||
io_->read(nbHeadersBuf.data(), DWORD);
|
||||
|
||||
if (compareTag(exvGettext(tv->label_), "Header")) {
|
||||
localPosition_ = 0;
|
||||
io_->read(buf.data(), 4);
|
||||
io_->read(buf.data(), 2);
|
||||
|
||||
while (localPosition_ < cur_pos + size)
|
||||
decodeBlock();
|
||||
uint32_t nb_headers = Exiv2::getULong(nbHeadersBuf.data(), littleEndian);
|
||||
DataBuf reserved(BYTE + 1);
|
||||
io_->read(reserved.data(), BYTE);
|
||||
io_->read(reserved.data(), BYTE);
|
||||
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"))
|
||||
fileProperties();
|
||||
else if (compareTag(exvGettext(tag->label_), "Stream_Properties"))
|
||||
streamProperties();
|
||||
else if (compareTag(exvGettext(tag->label_), "Header_Extension"))
|
||||
headerExtension();
|
||||
else if (compareTag(exvGettext(tag->label_), "Codec_List"))
|
||||
codecList();
|
||||
else if (compareTag(exvGettext(tag->label_), "Extended_Content_Description"))
|
||||
extendedContentDescription();
|
||||
else if (compareTag(exvGettext(tag->label_), "Content_Description"))
|
||||
contentDescription();
|
||||
else if (compareTag(exvGettext(tag->label_), "Extended_Stream_Properties"))
|
||||
extendedStreamProperties();
|
||||
else
|
||||
io_->seek(io_->tell() + obj.getRemainingSize(), BasicIo::beg);
|
||||
} else
|
||||
io_->seek(io_->tell() + obj.getRemainingSize(), BasicIo::beg);
|
||||
}
|
||||
} else
|
||||
io_->seek(io_->tell() + obj.getRemainingSize(), BasicIo::beg);
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "File_Properties"))
|
||||
fileProperties();
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Stream_Properties"))
|
||||
streamProperties();
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Metadata"))
|
||||
metadataHandler(1);
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Extended_Content_Description"))
|
||||
metadataHandler(2);
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Metadata_Library"))
|
||||
metadataHandler(3);
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Codec_List"))
|
||||
codecList();
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Content_Description"))
|
||||
contentDescription(size);
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Extended_Stream_Properties"))
|
||||
extendedStreamProperties(size);
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Header_Extension")) {
|
||||
localPosition_ = 0;
|
||||
headerExtension(size);
|
||||
}
|
||||
|
||||
else if (compareTag(exvGettext(tv->label_), "Language_List")) {
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
io_->read(buf.data(), 2);
|
||||
count = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
|
||||
while (count--) {
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
io_->read(buf.data(), 1);
|
||||
tempLength = static_cast<int>(buf.data()[0]);
|
||||
|
||||
io_->read(buf.data(), tempLength);
|
||||
v->read(Util::toString16(buf));
|
||||
}
|
||||
xmpData().add(Exiv2::XmpKey("Xmp.video.TrackLang"), v.get());
|
||||
}
|
||||
|
||||
io_->seek(cur_pos + size, BasicIo::beg);
|
||||
localPosition_ = io_->tell();
|
||||
}; // AsfVideo::tagDecoder
|
||||
|
||||
tagDecoder(tv, size - 24);
|
||||
} else
|
||||
io_->seek(cur_pos + size, BasicIo::beg);
|
||||
|
||||
localPosition_ = io_->tell();
|
||||
io_->seek(io_->tell() + obj.getRemainingSize(), BasicIo::beg);
|
||||
} // AsfVideo::decodeBlock
|
||||
|
||||
void AsfVideo::extendedStreamProperties(uint64_t size) {
|
||||
uint64_t cur_pos = io_->tell(), avgTimePerFrame = 0;
|
||||
DataBuf buf(BUFF_MIN_SIZE);
|
||||
static int previousStream;
|
||||
io_->seek(cur_pos + 48, BasicIo::beg);
|
||||
void AsfVideo::extendedStreamProperties() {
|
||||
xmpData()["Xmp.video.StartTimecode"] = readQWORDTag(); // Start Time
|
||||
xmpData()["Xmp.video.EndTimecode"] = readWORDTag(); // End Time
|
||||
|
||||
io_->read(buf.data(), 2);
|
||||
streamNumber_ = 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
|
||||
|
||||
io_->read(buf.data(), 2);
|
||||
io_->read(buf.data(), BUFF_MIN_SIZE);
|
||||
avgTimePerFrame = Util::getUint64_t(buf);
|
||||
xmpData()["Xmp.video.FrameRate"] = readWORDTag(); // Average Time Per Frame
|
||||
uint16_t stream_name_count = readWORDTag();
|
||||
uint16_t payload_ext_sys_count = readWORDTag();
|
||||
|
||||
if (previousStream < streamNumber_ && avgTimePerFrame != 0)
|
||||
xmpData()["Xmp.video.FrameRate"] = 10000000. / avgTimePerFrame;
|
||||
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();
|
||||
if (stream_length)
|
||||
io_->seek(io_->tell() + stream_length, BasicIo::beg); // ignore Stream name
|
||||
}
|
||||
|
||||
previousStream = streamNumber_;
|
||||
io_->seek(cur_pos + size, BasicIo::beg);
|
||||
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();
|
||||
if (ext_sys_info_length)
|
||||
io_->seek(io_->tell() + ext_sys_info_length, BasicIo::beg); // ignore Extension System Info
|
||||
}
|
||||
} // AsfVideo::extendedStreamProperties
|
||||
|
||||
void AsfVideo::contentDescription(uint64_t size) {
|
||||
const size_t pos = io_->tell();
|
||||
size_t length[5];
|
||||
for (size_t& i : length) {
|
||||
byte buf[2];
|
||||
io_->read(buf, 2);
|
||||
if (io_->error() || io_->eof())
|
||||
throw Error(ErrorCode::kerFailedToReadImageData);
|
||||
i = getUShort(buf, littleEndian);
|
||||
}
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
DataBuf buf(length[i]);
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
io_->read(buf.data(), length[i]);
|
||||
if (io_->error() || io_->eof())
|
||||
throw Error(ErrorCode::kerFailedToReadImageData);
|
||||
const TagDetails* td = 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() {
|
||||
DataBuf buf(20);
|
||||
byte guidBuf[GUI_SIZE];
|
||||
int stream = 0;
|
||||
byte streamTypedBuf[GUID];
|
||||
io_->read(streamTypedBuf,GUID);
|
||||
char stream_type[GUID_SIZE] = "";
|
||||
getGUID(streamTypedBuf, stream_type);
|
||||
|
||||
enum streamTypeInfo { Audio = 1, Video = 2 };
|
||||
io_->read(guidBuf, GUI_SIZE);
|
||||
char streamType[GUID_SIZE] = "";
|
||||
int stream = 0;
|
||||
|
||||
getGUID(guidBuf, streamType);
|
||||
const TagVocabulary* tv;
|
||||
tv = find(GUIDReferenceTags, streamType);
|
||||
io_->read(guidBuf, GUI_SIZE);
|
||||
auto tag_stream_type = find(GUIDReferenceTags, stream_type);
|
||||
if (tag_stream_type) {
|
||||
if (compareTag(exvGettext(tag_stream_type->label_), "Audio_Media"))
|
||||
stream = Audio;
|
||||
else if (compareTag(exvGettext(tag_stream_type->label_), "Video_Media"))
|
||||
stream = Video;
|
||||
|
||||
if (compareTag(exvGettext(tv->label_), "Audio_Media"))
|
||||
stream = Audio;
|
||||
else if (compareTag(exvGettext(tv->label_), "Video_Media"))
|
||||
stream = Video;
|
||||
io_->seek(io_->tell() + GUID, BasicIo::beg); // ignore Error Correction Type
|
||||
|
||||
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);
|
||||
uint64_t time_offset = readQWORDTag();
|
||||
if (stream == Video)
|
||||
xmpData()["Xmp.video.TimeOffset"] = time_offset;
|
||||
else if (stream == Audio)
|
||||
xmpData()["Xmp.audio.TimeOffset"] = time_offset;
|
||||
|
||||
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;
|
||||
auto specific_data_length = readDWORDTag();
|
||||
auto correction_data_length = readDWORDTag();
|
||||
|
||||
io_->read(buf.data(), 5);
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
io_->read(buf.data(), 2);
|
||||
size_t temp = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
|
||||
if (stream == 2) {
|
||||
xmpData()["Xmp.video.Width"] = temp;
|
||||
width_ = temp;
|
||||
} else if (stream == Audio) {
|
||||
// todo xmpData()["Xmp.audio.Codec"]
|
||||
io_->seek(io_->tell() + WORD /*Flags*/ + DWORD /*Reserved*/ + specific_data_length + correction_data_length,
|
||||
BasicIo::beg);
|
||||
}
|
||||
|
||||
io_->read(buf.data(), 2);
|
||||
temp = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
if (stream == Audio)
|
||||
xmpData()["Xmp.audio.ChannelType"] = temp;
|
||||
|
||||
io_->read(buf.data(), 4);
|
||||
temp = Exiv2::getULong(buf.data(), littleEndian);
|
||||
|
||||
if (stream == Video) {
|
||||
xmpData()["Xmp.video.Height"] = temp;
|
||||
height_ = temp;
|
||||
} else if (stream == Audio) {
|
||||
xmpData()["Xmp.audio.SampleRate"] = temp;
|
||||
}
|
||||
} // AsfVideo::streamProperties
|
||||
|
||||
void AsfVideo::codecList() {
|
||||
DataBuf buf(200);
|
||||
io_->read(buf.data(), GUI_SIZE);
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
io_->read(buf.data(), 4);
|
||||
int codecCount = Exiv2::getULong(buf.data(), littleEndian), descLength = 0, codecType = 0;
|
||||
io_->seek(io_->tell() + GUID /*reserved*/, BasicIo::beg);
|
||||
auto entries_count = readDWORDTag();
|
||||
for (uint32_t i = 0; i < entries_count; i++) {
|
||||
uint16_t codec_type = readWORDTag() * 2;
|
||||
std::string codec = (codec_type == 1) ? "Xmp.video" : "Xmp.audio";
|
||||
|
||||
while (codecCount--) {
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
io_->read(buf.data(), 2);
|
||||
codecType = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
uint16_t codec_name_length = readWORDTag() * 2;
|
||||
if (codec_name_length)
|
||||
xmpData()[codec + std::string(".CodecName")] = readStringWCHAR(codec_name_length);
|
||||
|
||||
io_->read(buf.data(), 2);
|
||||
descLength = Exiv2::getUShort(buf.data(), littleEndian) * 2;
|
||||
uint16_t codec_desc_length = readWORDTag();
|
||||
if (codec_desc_length)
|
||||
xmpData()[codec + std::string(".CodecDescription")] = readStringWCHAR(codec_desc_length);
|
||||
|
||||
io_->read(buf.data(), descLength);
|
||||
if (codecType == 1)
|
||||
xmpData()["Xmp.video.Codec"] = Util::toString16(buf);
|
||||
else if (codecType == 2)
|
||||
xmpData()["Xmp.audio.Compressor"] = Util::toString16(buf);
|
||||
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
io_->read(buf.data(), 2);
|
||||
descLength = Exiv2::getUShort(buf.data(), littleEndian) * 2;
|
||||
|
||||
io_->read(buf.data(), descLength);
|
||||
|
||||
if (codecType == 1)
|
||||
xmpData()["Xmp.video.CodecDescription"] = Util::toString16(buf);
|
||||
else if (codecType == 2)
|
||||
xmpData()["Xmp.audio.CodecDescription"] = Util::toString16(buf);
|
||||
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
io_->read(buf.data(), 2);
|
||||
descLength = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
|
||||
io_->read(buf.data(), descLength);
|
||||
uint16_t codec_info_length = readWORDTag();
|
||||
if (codec_info_length)
|
||||
xmpData()[codec + std::string(".CodecInfo")] = readString(codec_info_length);
|
||||
}
|
||||
} // AsfVideo::codecList
|
||||
|
||||
void AsfVideo::headerExtension(uint64_t size) {
|
||||
uint64_t cur_pos = io_->tell();
|
||||
DataBuf buf(20);
|
||||
io_->read(buf.data(), 18);
|
||||
buf.data()[4] = '\0';
|
||||
io_->read(buf.data(), 4);
|
||||
|
||||
while (localPosition_ < cur_pos + size)
|
||||
decodeBlock();
|
||||
|
||||
io_->seek(cur_pos + size, BasicIo::beg);
|
||||
void AsfVideo::headerExtension() {
|
||||
io_->seek(io_->tell() + GUID /*reserved1*/ + WORD /*Reserved2*/, BasicIo::beg);
|
||||
auto header_ext_data_length = readDWORDTag();
|
||||
io_->seek(io_->tell() + header_ext_data_length, BasicIo::beg);
|
||||
} // AsfVideo::headerExtension
|
||||
|
||||
void AsfVideo::metadataHandler(int meta) {
|
||||
DataBuf buf(5000);
|
||||
io_->read(buf.data(), 2);
|
||||
uint16_t recordCount = Exiv2::getUShort(buf.data(), littleEndian), nameLength = 0, dataLength = 0, dataType = 0;
|
||||
Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::xmpSeq);
|
||||
byte guidBuf[GUI_SIZE];
|
||||
char fileID[GUID_SIZE] = "";
|
||||
void AsfVideo::extendedContentDescription() {
|
||||
uint16_t content_descriptor_count = readWORDTag();
|
||||
std::string value;
|
||||
|
||||
while (recordCount--) {
|
||||
std::memset(buf.data(), 0x0, buf.size());
|
||||
for (uint16_t i = 0; i < content_descriptor_count; i++) {
|
||||
uint16_t descriptor_name_length = readWORDTag();
|
||||
if (descriptor_name_length)
|
||||
value += readStringWCHAR(descriptor_name_length); // Descriptor Name
|
||||
|
||||
if (meta == 1 || meta == 3) {
|
||||
io_->read(buf.data(), 4);
|
||||
io_->read(buf.data(), 2);
|
||||
nameLength = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
io_->read(buf.data(), 2);
|
||||
dataType = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
io_->read(buf.data(), 4);
|
||||
dataLength = Exiv2::getULong(buf.data(), littleEndian);
|
||||
|
||||
if (nameLength > 5000) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
|
||||
<< " entries considered invalid; not read.\n";
|
||||
#endif
|
||||
io_->seek(io_->tell() + nameLength, BasicIo::beg);
|
||||
} else {
|
||||
io_->read(buf.data(), nameLength);
|
||||
}
|
||||
|
||||
v->read(Util::toString16(buf));
|
||||
if (dataType == 6) {
|
||||
io_->read(guidBuf, GUI_SIZE);
|
||||
getGUID(guidBuf, fileID);
|
||||
} else {
|
||||
// Sanity check with an "unreasonably" large number
|
||||
if (dataLength > 5000) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
|
||||
<< " entries considered invalid; not read.\n";
|
||||
#endif
|
||||
io_->seek(io_->tell() + dataLength, BasicIo::beg);
|
||||
} else
|
||||
io_->read(buf.data(), dataLength);
|
||||
uint16_t descriptor_value_data_type = readWORDTag();
|
||||
uint16_t descriptor_value_length = readWORDTag();
|
||||
if (descriptor_value_length) {
|
||||
// Descriptor Value
|
||||
switch (descriptor_value_data_type) {
|
||||
case 0 /*Unicode string */:
|
||||
value += std::string(": ") + readStringWCHAR(descriptor_value_length);
|
||||
break;
|
||||
case 1 /*BYTE array */:
|
||||
value += std::string(": ") + readString(descriptor_value_length);
|
||||
break;
|
||||
case 2 /*BOOL*/:
|
||||
value += std::string(": ") + std::to_string(readWORDTag());
|
||||
break;
|
||||
case 3 /*DWORD */:
|
||||
value += std::string(": ") + std::to_string(readDWORDTag());
|
||||
break;
|
||||
case 4 /*QWORD */:
|
||||
value += std::string(": ") + std::to_string(readQWORDTag());
|
||||
break;
|
||||
case 5 /*WORD*/:
|
||||
value += std::string(": ") + std::to_string(readWORDTag());
|
||||
;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else if (meta == 2) {
|
||||
io_->read(buf.data(), 2);
|
||||
nameLength = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
|
||||
if (nameLength > 5000) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
|
||||
<< " entries considered invalid; not read.\n";
|
||||
#endif
|
||||
io_->seek(io_->tell() + nameLength, BasicIo::beg);
|
||||
} else {
|
||||
io_->read(buf.data(), nameLength);
|
||||
}
|
||||
|
||||
v->read(Util::toString16(buf));
|
||||
|
||||
io_->read(buf.data(), 2);
|
||||
dataType = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
|
||||
io_->read(buf.data(), 2);
|
||||
dataLength = Exiv2::getUShort(buf.data(), littleEndian);
|
||||
|
||||
// Sanity check with an "unreasonably" large number
|
||||
if (dataLength > 5000) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
|
||||
<< " entries considered invalid; not read.\n";
|
||||
#endif
|
||||
io_->seek(io_->tell() + dataLength, BasicIo::beg);
|
||||
} else
|
||||
io_->read(buf.data(), dataLength);
|
||||
}
|
||||
|
||||
if (dataType == 0) { // Unicode String
|
||||
v->read(Util::toString16(buf));
|
||||
} else if (dataType == 2 || dataType == 5) { // 16-bit Unsigned Integer
|
||||
v->read(Exiv2::toString(Exiv2::getUShort(buf.data(), littleEndian)));
|
||||
} else if (dataType == 3) { // 32-bit Unsigned Integer
|
||||
v->read(Exiv2::toString(Exiv2::getULong(buf.data(), littleEndian)));
|
||||
} else if (dataType == 4) { // 64-bit Unsigned Integer
|
||||
v->read(Exiv2::toString(Util::getUint64_t(buf)));
|
||||
} else if (dataType == 6) { // 128-bit GUID
|
||||
v->read(Exiv2::toString(fileID));
|
||||
} else { // Byte array
|
||||
v->read(Exiv2::toString(buf.data()));
|
||||
}
|
||||
value += std::string(", ");
|
||||
}
|
||||
|
||||
if (meta == 1) {
|
||||
xmpData().add(Exiv2::XmpKey("Xmp.video.Metadata"), v.get());
|
||||
} else if (meta == 2) {
|
||||
xmpData().add(Exiv2::XmpKey("Xmp.video.ExtendedContentDescription"), v.get());
|
||||
} else {
|
||||
xmpData().add(Exiv2::XmpKey("Xmp.video.MetadataLibrary"), v.get());
|
||||
}
|
||||
} // AsfVideo::metadataHandler
|
||||
xmpData()["Xmp.video.ExtendedContentDescription"] = value;
|
||||
} // 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();
|
||||
|
||||
if (title_length)
|
||||
xmpData()["Xmp.video.Title"] = readStringWCHAR(title_length);
|
||||
|
||||
if (author_length)
|
||||
xmpData()["Xmp.video.Author"] = readStringWCHAR(author_length);
|
||||
|
||||
if (copyright_length)
|
||||
xmpData()["Xmp.video.Copyright"] = readStringWCHAR(copyright_length);
|
||||
|
||||
if (desc_length)
|
||||
xmpData()["Xmp.video.Description"] = readStringWCHAR(desc_length);
|
||||
|
||||
if (rating_length)
|
||||
xmpData()["Xmp.video.Rating"] = readStringWCHAR(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() {
|
||||
DataBuf buf(BUFF_MIN_SIZE);
|
||||
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();
|
||||
|
||||
byte guidBuf[GUI_SIZE];
|
||||
io_->read(guidBuf, GUI_SIZE);
|
||||
char fileID[GUID_SIZE] = "";
|
||||
int count = 7;
|
||||
getGUID(guidBuf, fileID);
|
||||
xmpData()["Xmp.video.FileID"] = fileID;
|
||||
|
||||
const TagDetails* td;
|
||||
|
||||
while (count--) {
|
||||
td = find(filePropertiesTags, (count + 1));
|
||||
io_->read(buf.data(), BUFF_MIN_SIZE);
|
||||
|
||||
if (count == 0) {
|
||||
buf.data()[4] = '\0';
|
||||
io_->read(buf.data(), 4);
|
||||
io_->read(buf.data(), 4);
|
||||
}
|
||||
|
||||
if (count == 3 || count == 2) {
|
||||
xmpData()[exvGettext(td->label_)] = Util::getUint64_t(buf) / 10000;
|
||||
} else {
|
||||
xmpData()[exvGettext(td->label_)] = Util::getUint64_t(buf);
|
||||
}
|
||||
}
|
||||
io_->seek(io_->tell() + DWORD + DWORD + DWORD, BasicIo::beg);
|
||||
xmpData()["Xmp.video.MaxBitRate"] = readDWORDTag();
|
||||
} // AsfVideo::fileProperties
|
||||
|
||||
void AsfVideo::aspectRatio() {
|
||||
|
||||
@ -24,7 +24,7 @@ std::string toString16(Exiv2::DataBuf& buf) {
|
||||
std::ostringstream os;
|
||||
char t;
|
||||
|
||||
for (size_t i = 0; i <= buf.size(); i += 2) {
|
||||
for (size_t i = 0; i < buf.size(); i += 2) {
|
||||
t = buf.data()[i] + 16 * buf.data()[i + 1];
|
||||
if (t == 0) {
|
||||
if (i)
|
||||
|
||||
@ -441,6 +441,8 @@ std::string RiffVideo::mimeType() const {
|
||||
@return Returns true if the buffer value is equal to string.
|
||||
*/
|
||||
bool RiffVideo::equalsRiffTag(Exiv2::DataBuf& buf, const char* str) {
|
||||
if (buf.size() != RIFF_TAG_SIZE)
|
||||
return false;
|
||||
for (size_t i = 0; i < RIFF_TAG_SIZE; i++)
|
||||
if (toupper(buf.data()[i]) != str[i])
|
||||
return false;
|
||||
@ -546,74 +548,72 @@ void RiffVideo::readMetadata() {
|
||||
} // RiffVideo::readMetadata
|
||||
|
||||
void RiffVideo::decodeBlock() {
|
||||
DataBuf buf(RIFF_TAG_SIZE + 1);
|
||||
DataBuf buf2(RIFF_TAG_SIZE + 1);
|
||||
DataBuf chunk_size(RIFF_TAG_SIZE + 1);
|
||||
DataBuf chunk_id(RIFF_TAG_SIZE + 1);
|
||||
|
||||
io_->read(buf2.data(), RIFF_TAG_SIZE);
|
||||
io_->read(chunk_id.data(), RIFF_TAG_SIZE);
|
||||
|
||||
if (io_->eof() || equalsRiffTag(buf2, "MOVI") || equalsRiffTag(buf2, "DATA")) {
|
||||
if (io_->eof() || equalsRiffTag(chunk_id, RIFF_CHUNK_ID_MOVI) || equalsRiffTag(chunk_id, RIFF_CHUNK_ID_DATA)) {
|
||||
continueTraversing_ = false;
|
||||
return;
|
||||
}
|
||||
if (equalsRiffTag(buf2, "HDRL") || equalsRiffTag(buf2, "STRL")) {
|
||||
if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_HDRL) || equalsRiffTag(chunk_id, RIFF_CHUNK_ID_STRL)) {
|
||||
decodeBlock();
|
||||
} else {
|
||||
io_->read(buf.data(), RIFF_TAG_SIZE);
|
||||
size_t size = Exiv2::getULong(buf.data(), littleEndian);
|
||||
io_->read(chunk_size.data(), RIFF_TAG_SIZE);
|
||||
size_t size = Exiv2::getULong(chunk_size.data(), littleEndian);
|
||||
|
||||
tagDecoder(buf2, size);
|
||||
tagDecoder(chunk_id, size);
|
||||
}
|
||||
} // RiffVideo::decodeBlock
|
||||
|
||||
void RiffVideo::tagDecoder(Exiv2::DataBuf& buf, size_t size) {
|
||||
void RiffVideo::tagDecoder(Exiv2::DataBuf& chunk_id, size_t size) {
|
||||
uint64_t cur_pos = io_->tell();
|
||||
static bool listFlag = false, listEnd = false;
|
||||
|
||||
if (equalsRiffTag(buf, "LIST")) {
|
||||
if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_LIST)) {
|
||||
listFlag = true;
|
||||
listEnd = false;
|
||||
|
||||
while (static_cast<uint64_t>(io_->tell()) < cur_pos + size)
|
||||
while (static_cast<uint64_t>(io_->tell()) < cur_pos + size && !io_->eof()) {
|
||||
decodeBlock();
|
||||
|
||||
}
|
||||
listEnd = true;
|
||||
io_->seek(cur_pos + size, BasicIo::beg);
|
||||
} else if (equalsRiffTag(buf, "JUNK") && listEnd) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_JUNK) && listEnd) {
|
||||
junkHandler(size);
|
||||
} else if (equalsRiffTag(buf, "AVIH")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_AVIH)) {
|
||||
listFlag = false;
|
||||
aviHeaderTagsHandler(size);
|
||||
} else if (equalsRiffTag(buf, "STRH")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_STRH)) {
|
||||
listFlag = false;
|
||||
streamHandler(size);
|
||||
} else if (equalsRiffTag(buf, "STRF") || equalsRiffTag(buf, "FMT ")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_STRF) || equalsRiffTag(chunk_id, RIFF_CHUNK_ID_FMT)) {
|
||||
listFlag = false;
|
||||
if (equalsRiffTag(buf, "FMT "))
|
||||
if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_FMT))
|
||||
streamType_ = Audio;
|
||||
streamFormatHandler(size);
|
||||
} else if (equalsRiffTag(buf, "STRN")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_STRN)) {
|
||||
listFlag = false;
|
||||
dateTimeOriginal(size, 1);
|
||||
} else if (equalsRiffTag(buf, "STRD")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_STRD)) {
|
||||
listFlag = false;
|
||||
streamDataTagHandler(size);
|
||||
} else if (equalsRiffTag(buf, "IDIT")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_IDIT)) {
|
||||
listFlag = false;
|
||||
dateTimeOriginal(size);
|
||||
} else if (equalsRiffTag(buf, "INFO")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_INFO)) {
|
||||
listFlag = false;
|
||||
infoTagsHandler();
|
||||
} else if (equalsRiffTag(buf, "NCDT")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_NCDT)) {
|
||||
listFlag = false;
|
||||
nikonTagsHandler();
|
||||
} else if (equalsRiffTag(buf, "ODML")) {
|
||||
} else if (equalsRiffTag(chunk_id, RIFF_CHUNK_ID_ODML)) {
|
||||
listFlag = false;
|
||||
odmlTagsHandler();
|
||||
} else if (listFlag) {
|
||||
// std::cout<<"|unprocessed|"<<buf.data();
|
||||
skipListData();
|
||||
} else {
|
||||
// std::cout<<"|unprocessed|"<<buf.data();
|
||||
io_->seek(cur_pos + size, BasicIo::beg);
|
||||
}
|
||||
} // RiffVideo::tagDecoder
|
||||
@ -879,7 +879,7 @@ void RiffVideo::infoTagsHandler() {
|
||||
} // RiffVideo::infoTagsHandler
|
||||
|
||||
void RiffVideo::junkHandler(size_t size) {
|
||||
DataBuf buf(size + 1), buf2(RIFF_TAG_SIZE + 1);
|
||||
DataBuf buf(size), buf2(RIFF_TAG_SIZE);
|
||||
uint64_t cur_pos = io_->tell();
|
||||
|
||||
io_->read(buf.data(), RIFF_TAG_SIZE);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user