/src/exiv2/src/asfvideo.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | // included header files |
3 | | #include "asfvideo.hpp" |
4 | | |
5 | | #include "basicio.hpp" |
6 | | #include "config.h" |
7 | | #include "enforce.hpp" |
8 | | #include "error.hpp" |
9 | | #include "futils.hpp" |
10 | | #include "helper_functions.hpp" |
11 | | #include "image_int.hpp" |
12 | | |
13 | | // ***************************************************************************** |
14 | | // class member definitions |
15 | | namespace Exiv2 { |
16 | | |
17 | | /*! |
18 | | Look-up list for ASF Type Video Files |
19 | | Associates the GUID with its Name(i.e. Human Readable Form) |
20 | | Tags have been differentiated into Various Categories. |
21 | | The categories have been listed above Groups |
22 | | see : |
23 | | - https://fr.wikipedia.org/wiki/Advanced_Systems_Format |
24 | | - https://exse.eyewated.com/fls/54b3ed95bbfb1a92.pdf |
25 | | */ |
26 | | /* |
27 | | * @class GUID_struct |
28 | | * |
29 | | * @brief A class to represent a globally unique identifier (GUID) structure |
30 | | * |
31 | | * This class represents a globally unique identifier (GUID) structure which is used to identify objects in a |
32 | | * distributed environment. A GUID is a unique identifier that is generated on a computer and can be used to |
33 | | * identify an object across different systems. The GUID structure is comprised of four 32-bit values and an |
34 | | * array of 8 bytes. |
35 | | * |
36 | | * @note The byte order of the GUID structure is in little endian. |
37 | | * |
38 | | * @see https://en.wikipedia.org/wiki/Globally_unique_identifier |
39 | | * |
40 | | */ |
41 | | |
42 | 4.91k | bool AsfVideo::GUIDTag::operator==(const AsfVideo::GUIDTag& other) const { |
43 | 4.91k | return data1_ == other.data1_ && data2_ == other.data2_ && data3_ == other.data3_ && data4_ == other.data4_; |
44 | 4.91k | } |
45 | | |
46 | 7.84k | AsfVideo::GUIDTag::GUIDTag(const uint8_t* bytes) { |
47 | 7.84k | data1_ = Exiv2::getULong(bytes, ByteOrder::littleEndian); |
48 | 7.84k | data2_ = Exiv2::getUShort(bytes + DWORD, ByteOrder::littleEndian); |
49 | 7.84k | data3_ = Exiv2::getUShort(bytes + DWORD + WORD, ByteOrder::littleEndian); |
50 | 7.84k | std::copy(bytes + QWORD, bytes + (2 * QWORD), data4_.begin()); |
51 | 7.84k | } |
52 | | |
53 | 231 | std::string AsfVideo::GUIDTag::to_string() const { |
54 | | // Concatenate all strings into a single string |
55 | | // Convert the string to uppercase |
56 | | // Example of output 399595EC-8667-4E2D-8FDB-98814CE76C1E |
57 | 231 | return stringFormat("{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}", data1_, data2_, data3_, |
58 | 231 | data4_[0], data4_[1], data4_[2], data4_[3], data4_[4], data4_[5], data4_[6], data4_[7]); |
59 | 231 | } |
60 | | |
61 | 20.0k | bool AsfVideo::GUIDTag::operator<(const GUIDTag& other) const { |
62 | 20.0k | if (data1_ != other.data1_) |
63 | 15.3k | return data1_ < other.data1_; |
64 | 4.74k | if (data2_ != other.data2_) |
65 | 90 | return data2_ < other.data2_; |
66 | 4.65k | if (data3_ != other.data3_) |
67 | 73 | return data3_ < other.data3_; |
68 | 4.58k | return std::lexicographical_compare(data4_.begin(), data4_.end(), other.data4_.begin(), other.data4_.end()); |
69 | 4.65k | } |
70 | | |
71 | | constexpr AsfVideo::GUIDTag Header(0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}); |
72 | | |
73 | | const std::map<AsfVideo::GUIDTag, std::string> GUIDReferenceTags = { |
74 | | //!< Top-level ASF object GUIDS |
75 | | {Header, "Header"}, |
76 | | {{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}, "Data"}, |
77 | | {{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}, "Simple_Index"}, |
78 | | {{0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}, "Index"}, |
79 | | {{0xFEB103F8, 0x12AD, 0x4C64, {0x84, 0x0F, 0x2A, 0x1D, 0x2F, 0x7A, 0xD4, 0x8C}}, "Media_Index"}, |
80 | | {{0x3CB73FD0, 0x0C4A, 0x4803, {0x95, 0x3D, 0xED, 0xF7, 0xB6, 0x22, 0x8F, 0x0C}}, "Timecode_Index"}, |
81 | | |
82 | | //!< Header Object GUIDs |
83 | | {{0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}, "File_Properties"}, |
84 | | {{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}, "Stream_Properties"}, |
85 | | {{0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}, "Header_Extension"}, |
86 | | {{0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}, "Codec_List"}, |
87 | | {{0x1EFB1A30, 0x0B62, 0x11D0, {0xA3, 0x9B, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}, "Script_Command"}, |
88 | | {{0xF487CD01, 0xA951, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x00, 0xC2, 0x05, 0x36}}, "Marker"}, |
89 | | {{0xD6E229DC, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}, "Bitrate_Mutual_Exclusion"}, |
90 | | {{0x75B22635, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}, "Error_Correction"}, |
91 | | {{0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}, "Content_Description"}, |
92 | | {{0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}, "Extended_Content_Description"}, |
93 | | {{0x2211B3FA, 0xBD23, 0x11D2, {0xB4, 0xB7, 0x00, 0xA0, 0xC9, 0x55, 0xFC, 0x6E}}, "Content_Branding"}, |
94 | | {{0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}}, "Stream_Bitrate_Properties"}, |
95 | | {{0x2211B3FB, 0xBD23, 0x11D2, {0xB4, 0xB7, 0x00, 0xA0, 0xC9, 0x55, 0xFC, 0x6E}}, "Content_Encryption"}, |
96 | | {{0x298AE614, 0x2622, 0x4C17, {0xB9, 0x35, 0xDA, 0xE0, 0x7E, 0xE9, 0x28, 0x9C}}, "Extended_Content_Encryption"}, |
97 | | {{0x2211B3FC, 0xBD23, 0x11D2, {0xB4, 0xB7, 0x00, 0xA0, 0xC9, 0x55, 0xFC, 0x6E}}, "Digital_Signature"}, |
98 | | {{0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}}, "Padding"}, |
99 | | |
100 | | //!< Header Extension Object GUIDs |
101 | | {{0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}}, "Extended_Stream_Properties"}, |
102 | | {{0xA08649CF, 0x4775, 0x4670, {0x8A, 0x16, 0x6E, 0x35, 0x35, 0x75, 0x66, 0xCD}}, "Advanced_Mutual_Exclusion"}, |
103 | | {{0xD1465A40, 0x5A79, 0x4338, {0xB7, 0x1B, 0xE3, 0x6B, 0x8F, 0xD6, 0xC2, 0x49}}, "Group_Mutual_Exclusion"}, |
104 | | {{0xD4FED15B, 0x88D3, 0x454F, {0x81, 0xF0, 0xED, 0x5C, 0x45, 0x99, 0x9E, 0x24}}, "Stream_Prioritization"}, |
105 | | {{0xA69609E6, 0x517B, 0x11D2, {0xB6, 0xAF, 0x00, 0xC0, 0x4F, 0xD9, 0x08, 0xE9}}, "Bandwidth_Sharing"}, |
106 | | {{0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}}, "Language_List"}, |
107 | | {{0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}}, "Metadata"}, |
108 | | {{0x44231C94, 0x9498, 0x49D1, {0xA1, 0x41, 0x1D, 0x13, 0x4E, 0x45, 0x70, 0x54}}, "Metadata_Library"}, |
109 | | {{0xD6E229DF, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}, "Index_Parameters"}, |
110 | | {{0x6B203BAD, 0x3F11, 0x48E4, {0xAC, 0xA8, 0xD7, 0x61, 0x3D, 0xE2, 0xCF, 0xA7}}, "Media_Index_Parameters"}, |
111 | | {{0xF55E496D, 0x9797, 0x4B5D, {0x8C, 0x8B, 0x60, 0x4D, 0xFE, 0x9B, 0xFB, 0x24}}, "Timecode_Index_Parameters"}, |
112 | | {{0x26F18B5D, 0x4584, 0x47EC, {0x9F, 0x5F, 0x0E, 0x65, 0x1F, 0x04, 0x52, 0xC9}}, "Compatibility"}, |
113 | | {{0x43058533, 0x6981, 0x49E6, {0x9B, 0x74, 0xAD, 0x12, 0xCB, 0x86, 0xD5, 0x8C}}, "Advanced_Content_Encryption"}, |
114 | | |
115 | | //!< Stream Properties Object Stream Type GUIDs |
116 | | {{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}, "Audio_Media"}, |
117 | | {{0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}, "Video_Media"}, |
118 | | {{0x59DACFC0, 0x59E6, 0x11D0, {0xA3, 0xAC, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}, "Command_Media"}, |
119 | | {{0xB61BE100, 0x5B4E, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}, "JFIF_Media"}, |
120 | | {{0x35907DE0, 0xE415, 0x11CF, {0xA9, 0x17, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}, "Degradable_JPEG_Media"}, |
121 | | {{0x91BD222C, 0xF21C, 0x497A, {0x8B, 0x6D, 0x5A, 0xA8, 0x6B, 0xFC, 0x01, 0x85}}, "File_Transfer_Media"}, |
122 | | {{0x3AFB65E2, 0x47EF, 0x40F2, {0xAC, 0x2C, 0x70, 0xA9, 0x0D, 0x71, 0xD3, 0x43}}, "Binary_Media"}, |
123 | | |
124 | | //!< Web stream Type-Specific Data GUIDs |
125 | | {{0x776257D4, 0xC627, 0x41CB, {0x8F, 0x81, 0x7A, 0xC7, 0xFF, 0x1C, 0x40, 0xCC}}, "Web_Stream_Media_Subtype"}, |
126 | | {{0xDA1E6B13, 0x8359, 0x4050, {0xB3, 0x98, 0x38, 0x8E, 0x96, 0x5B, 0xF0, 0x0C}}, "Web_Stream_Format"}, |
127 | | |
128 | | //!< Stream Properties Object Error Correction Type GUIDs |
129 | | {{0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}, "No_Error_Correction"}, |
130 | | {{0xBFC3CD50, 0x618F, 0x11CF, {0x8B, 0xB2, 0x00, 0xAA, 0x00, 0xB4, 0xE2, 0x20}}, "Audio_Spread"}, |
131 | | //!< Header Extension Object GUIDs |
132 | | {{0xABD3D211, 0xA9BA, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}, "Reserved_1"}, |
133 | | |
134 | | //!< Advanced Content Encryption Object System ID GUIDs |
135 | | {{0x7A079BB6, 0xDAA4, 0x4E12, {0xA5, 0xCA, 0x91, 0xD3, 0x8D, 0xC1, 0x1A, 0x8D}}, |
136 | | "Content_Encryption_System_Windows_Media_DRM_Network_Devices"}, |
137 | | |
138 | | //!< Codec List Object GUIDs |
139 | | {{0x86D15241, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}, "Reserved_2"}, |
140 | | |
141 | | //!< Script Command Object GUIDs |
142 | | {{0x4B1ACBE3, 0x100B, 0x11D0, {0xA3, 0x9B, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}, "Reserved_3"}, |
143 | | |
144 | | //!< Marker Object GUIDs |
145 | | {{0x4CFEDB20, 0x75F6, 0x11CF, {0x9C, 0x0F, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}, "Reserved_4"}, |
146 | | |
147 | | //!< Mutual Exclusion Object Exclusion Type GUIDs |
148 | | {{0xD6E22A00, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}, "Mutex_Language"}, |
149 | | {{0xD6E22A01, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}, "Mutex_Bitrate"}, |
150 | | {{0xD6E22A02, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}, "Mutex_Unknown"}, |
151 | | //!< Bandwidth Sharing Object GUID |
152 | | {{0xAF6060AA, 0x5197, 0x11D2, {0xB6, 0xAF, 0x00, 0xC0, 0x4F, 0xD9, 0x08, 0xE9}}, "Bandwidth_Sharing_Exclusive"}, |
153 | | {{0xAF6060AB, 0x5197, 0x11D2, {0xB6, 0xAF, 0x00, 0xC0, 0x4F, 0xD9, 0x08, 0xE9}}, "Bandwidth_Sharing_Partial"}, |
154 | | |
155 | | //!< Standard Payload Extension System GUIDs |
156 | | {{0x399595EC, 0x8667, 0x4E2D, {0x8F, 0xDB, 0x98, 0x81, 0x4C, 0xE7, 0x6C, 0x1E}}, |
157 | | "Payload_Extension_System_Timecode"}, |
158 | | {{0xE165EC0E, 0x19ED, 0x45D7, {0xB4, 0xA7, 0x25, 0xCB, 0xD1, 0xE2, 0x8E, 0x9B}}, |
159 | | "Payload_Extension_System_File_Name"}, |
160 | | {{0xD590DC20, 0x07BC, 0x436C, {0x9C, 0xF7, 0xF3, 0xBB, 0xFB, 0xF1, 0xA4, 0xDC}}, |
161 | | "Payload_Extension_System_Content_Type"}, |
162 | | {{0x1B1EE554, 0xF9EA, 0x4BC8, {0x82, 0x1A, 0x37, 0x6B, 0x74, 0xE4, 0xC4, 0xB8}}, |
163 | | "Payload_Extension_System_Pixel_Aspect_Ratio"}, |
164 | | {{0xC6BD9450, 0x867F, 0x4907, {0x83, 0xA3, 0xC7, 0x79, 0x21, 0xB7, 0x33, 0xAD}}, |
165 | | "Payload_Extension_System_Sample_Duration"}, |
166 | | {{0x6698B84E, 0x0AFA, 0x4330, {0xAE, 0xB2, 0x1C, 0x0A, 0x98, 0xD7, 0xA4, 0x4D}}, |
167 | | "Payload_Extension_System_Encryption_Sample_ID"}, |
168 | | {{0x00E1AF06, 0x7BEC, 0x11D1, {0xA5, 0x82, 0x00, 0xC0, 0x4F, 0xC2, 0x9C, 0xFB}}, |
169 | | "Payload_Extension_System_Degradable_JPEG"}, |
170 | | }; |
171 | | |
172 | | /*! |
173 | | @brief Function used to check if data stored in buf is equivalent to |
174 | | ASF Header TagVocabulary's GUID. |
175 | | @param buf Exiv2 byte buffer |
176 | | @return Returns true if the buffer data is equivalent to Header GUID. |
177 | | */ |
178 | 4.91k | static bool isASFType(const byte buf[]) { |
179 | 4.91k | return Header == AsfVideo::GUIDTag(buf); |
180 | 4.91k | } |
181 | | |
182 | 765 | AsfVideo::AsfVideo(BasicIo::UniquePtr io) : Image(ImageType::asf, mdNone, std::move(io)) { |
183 | 765 | } // AsfVideo::AsfVideo |
184 | | |
185 | 809 | std::string AsfVideo::mimeType() const { |
186 | 809 | return "video/asf"; |
187 | 809 | } |
188 | | |
189 | 0 | void AsfVideo::writeMetadata() { |
190 | 0 | } |
191 | | |
192 | 765 | void AsfVideo::readMetadata() { |
193 | 765 | if (io_->open() != 0) |
194 | 0 | throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError()); |
195 | | |
196 | | // Ensure that this is the correct image type |
197 | 765 | if (!isAsfType(*io_, false)) { |
198 | 0 | if (io_->error() || io_->eof()) |
199 | 0 | throw Error(ErrorCode::kerFailedToReadImageData); |
200 | 0 | throw Error(ErrorCode::kerNotAnImage, "ASF"); |
201 | 0 | } |
202 | | |
203 | 765 | IoCloser closer(*io_); |
204 | 765 | clearMetadata(); |
205 | 765 | io_->seek(0, BasicIo::beg); |
206 | 765 | height_ = width_ = 1; |
207 | | |
208 | 765 | xmpData()["Xmp.video.FileSize"] = io_->size() / 1048576.; |
209 | 765 | xmpData()["Xmp.video.MimeType"] = mimeType(); |
210 | | |
211 | 765 | decodeBlock(); |
212 | | |
213 | 765 | xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width_, height_); |
214 | 765 | } // AsfVideo::readMetadata |
215 | | |
216 | 2.75k | AsfVideo::HeaderReader::HeaderReader(const BasicIo::UniquePtr& io) : IdBuf_(GUID) { |
217 | 2.75k | if (io->size() >= io->tell() + GUID + QWORD) { |
218 | 2.71k | io->readOrThrow(IdBuf_.data(), IdBuf_.size(), Exiv2::ErrorCode::kerCorruptedMetadata); |
219 | | |
220 | 2.71k | size_ = readQWORDTag(io); |
221 | 2.71k | if (size_ >= GUID + QWORD) |
222 | 2.58k | remaining_size_ = size_ - GUID - QWORD; |
223 | 2.71k | } |
224 | 2.75k | } |
225 | | |
226 | 2.75k | void AsfVideo::decodeBlock() { |
227 | 2.75k | Internal::enforce(GUID + QWORD <= io_->size() - io_->tell(), Exiv2::ErrorCode::kerCorruptedMetadata); |
228 | 2.75k | HeaderReader objectHeader(io_); |
229 | | #ifdef EXIV2_DEBUG_MESSAGES |
230 | | EXV_INFO << "decodeBlock = " << GUIDTag(objectHeader.getId().data()).to_string() |
231 | | << "\tsize= " << objectHeader.getSize() << "\t " << io_->tell() << "/" << io_->size() << '\n'; |
232 | | #endif |
233 | 2.75k | Internal::enforce(objectHeader.getSize() <= io_->size() - io_->tell(), Exiv2::ErrorCode::kerCorruptedMetadata); |
234 | 2.75k | auto tag = GUIDReferenceTags.find(GUIDTag(objectHeader.getId().data())); |
235 | | |
236 | 2.75k | if (tag != GUIDReferenceTags.end()) { |
237 | 1.98k | if (tag->second == "Header") |
238 | 702 | decodeHeader(); |
239 | 1.28k | else if (tag->second == "File_Properties") |
240 | 231 | fileProperties(); |
241 | 1.05k | else if (tag->second == "Stream_Properties") |
242 | 259 | streamProperties(); |
243 | 795 | else if (tag->second == "Header_Extension") |
244 | 221 | headerExtension(); |
245 | 574 | else if (tag->second == "Codec_List") |
246 | 128 | codecList(); |
247 | 446 | else if (tag->second == "Extended_Content_Description") |
248 | 375 | extendedContentDescription(); |
249 | 71 | else if (tag->second == "Content_Description") |
250 | 52 | contentDescription(); |
251 | 19 | else if (tag->second == "Extended_Stream_Properties") |
252 | 0 | extendedStreamProperties(); |
253 | 19 | else if (tag->second == "Degradable_JPEG_Media") |
254 | 0 | DegradableJPEGMedia(); |
255 | 19 | else { // tag found but not processed |
256 | | // Make sure that the remaining size is non-zero, so that we won't |
257 | | // keep revisiting the same location in the file. |
258 | 19 | const uint64_t remaining_size = objectHeader.getRemainingSize(); |
259 | 19 | Internal::enforce(remaining_size > 0, Exiv2::ErrorCode::kerCorruptedMetadata); |
260 | 19 | io_->seekOrThrow(io_->tell() + remaining_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData); |
261 | 19 | } |
262 | 1.98k | } else { // tag not found |
263 | | // Make sure that the remaining size is non-zero, so that we won't keep |
264 | | // revisiting the same location in the file. |
265 | 770 | const uint64_t remaining_size = objectHeader.getRemainingSize(); |
266 | 770 | Internal::enforce(remaining_size > 0, Exiv2::ErrorCode::kerCorruptedMetadata); |
267 | 770 | io_->seekOrThrow(io_->tell() + remaining_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData); |
268 | 770 | } |
269 | | |
270 | 2.75k | } // AsfVideo::decodeBlock |
271 | | |
272 | 702 | void AsfVideo::decodeHeader() { |
273 | 702 | DataBuf nbHeadersBuf(DWORD + 1); |
274 | 702 | io_->readOrThrow(nbHeadersBuf.data(), DWORD, Exiv2::ErrorCode::kerCorruptedMetadata); |
275 | | |
276 | 702 | uint32_t nb_headers = Exiv2::getULong(nbHeadersBuf.data(), littleEndian); |
277 | 702 | Internal::enforce(nb_headers < std::numeric_limits<uint32_t>::max(), Exiv2::ErrorCode::kerCorruptedMetadata); |
278 | 702 | io_->seekOrThrow(io_->tell() + (BYTE * 2), BasicIo::beg, |
279 | 702 | ErrorCode::kerFailedToReadImageData); // skip two reserved tags |
280 | 2.69k | for (uint32_t i = 0; i < nb_headers; i++) { |
281 | 1.99k | decodeBlock(); |
282 | 1.99k | } |
283 | 702 | } |
284 | | |
285 | 0 | void AsfVideo::extendedStreamProperties() { |
286 | 0 | xmpData()["Xmp.video.StartTimecode"] = readQWORDTag(io_); // Start Time |
287 | 0 | xmpData()["Xmp.video.EndTimecode"] = readWORDTag(io_); // End Time |
288 | |
|
289 | 0 | io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Data Bitrate |
290 | 0 | io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Buffer Size |
291 | 0 | io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Initial Buffer Fullness |
292 | 0 | io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Alternate Data Bitrate |
293 | 0 | io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Alternate Buffer Size |
294 | 0 | io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Alternate Initial Buffer Fullness |
295 | 0 | io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Maximum Object Size |
296 | 0 | io_->seek(io_->tell() + DWORD, BasicIo::beg); // ignore Flags Buffer Size |
297 | 0 | io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Flags Stream Number |
298 | 0 | io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Stream Language ID Index |
299 | |
|
300 | 0 | xmpData()["Xmp.video.FrameRate"] = readWORDTag(io_); // Average Time Per Frame |
301 | 0 | uint16_t stream_name_count = readWORDTag(io_); |
302 | 0 | uint16_t payload_ext_sys_count = readWORDTag(io_); |
303 | |
|
304 | 0 | for (uint16_t i = 0; i < stream_name_count; i++) { |
305 | 0 | io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Language ID Index |
306 | 0 | uint16_t stream_length = readWORDTag(io_); |
307 | 0 | if (stream_length) |
308 | 0 | io_->seek(io_->tell() + stream_length, BasicIo::beg); // ignore Stream name |
309 | 0 | } |
310 | |
|
311 | 0 | for (uint16_t i = 0; i < payload_ext_sys_count; i++) { |
312 | 0 | io_->seek(io_->tell() + GUID, BasicIo::beg); // ignore Extension System ID |
313 | 0 | io_->seek(io_->tell() + WORD, BasicIo::beg); // ignore Extension Data Size |
314 | 0 | uint16_t ext_sys_info_length = readWORDTag(io_); |
315 | 0 | if (ext_sys_info_length) |
316 | 0 | io_->seek(io_->tell() + ext_sys_info_length, BasicIo::beg); // ignore Extension System Info |
317 | 0 | } |
318 | 0 | } // AsfVideo::extendedStreamProperties |
319 | | |
320 | 0 | void AsfVideo::DegradableJPEGMedia() { |
321 | 0 | uint32_t width = readDWORDTag(io_); |
322 | 0 | width_ = width; |
323 | 0 | xmpData_["Xmp.video.Width"] = width; |
324 | |
|
325 | 0 | uint32_t height = readDWORDTag(io_); |
326 | 0 | height_ = height; |
327 | 0 | xmpData_["Xmp.video.Height"] = height; |
328 | |
|
329 | 0 | io_->seek(io_->tell() + (WORD * 3) /*3 Reserved*/, BasicIo::beg); |
330 | |
|
331 | 0 | uint32_t interchange_data_length = readWORDTag(io_); |
332 | 0 | io_->seek(io_->tell() + interchange_data_length /*Interchange data*/, BasicIo::beg); |
333 | 0 | } |
334 | | |
335 | 259 | void AsfVideo::streamProperties() { |
336 | 259 | DataBuf streamTypedBuf(GUID); |
337 | 259 | io_->readOrThrow(streamTypedBuf.data(), streamTypedBuf.size(), Exiv2::ErrorCode::kerCorruptedMetadata); |
338 | | |
339 | 259 | enum class streamTypeInfo { Audio = 1, Video = 2 }; |
340 | 259 | auto stream = streamTypeInfo{0}; |
341 | | |
342 | 259 | auto tag_stream_type = GUIDReferenceTags.find(GUIDTag(streamTypedBuf.data())); |
343 | 259 | if (tag_stream_type != GUIDReferenceTags.end()) { |
344 | 230 | if (tag_stream_type->second == "Audio_Media") |
345 | 144 | stream = streamTypeInfo::Audio; |
346 | 86 | else if (tag_stream_type->second == "Video_Media") |
347 | 50 | stream = streamTypeInfo::Video; |
348 | | |
349 | 230 | io_->seek(io_->tell() + GUID, BasicIo::beg); // ignore Error Correction Type |
350 | | |
351 | 230 | uint64_t time_offset = readQWORDTag(io_); |
352 | 230 | if (stream == streamTypeInfo::Video) |
353 | 50 | xmpData()["Xmp.video.TimeOffset"] = time_offset; |
354 | 180 | else if (stream == streamTypeInfo::Audio) |
355 | 144 | xmpData()["Xmp.audio.TimeOffset"] = time_offset; |
356 | | |
357 | 230 | auto specific_data_length = readDWORDTag(io_); |
358 | 230 | auto correction_data_length = readDWORDTag(io_); |
359 | | |
360 | 230 | io_->seek(io_->tell() + WORD /*Flags*/ + DWORD /*Reserved*/ + specific_data_length + correction_data_length, |
361 | 230 | BasicIo::beg); |
362 | 230 | } |
363 | | |
364 | 259 | } // AsfVideo::streamProperties |
365 | | |
366 | 128 | void AsfVideo::codecList() { |
367 | 128 | io_->seek(io_->tell() + GUID /*reserved*/, BasicIo::beg); |
368 | 128 | auto entries_count = readDWORDTag(io_); |
369 | 566 | for (uint32_t i = 0; i < entries_count; i++) { |
370 | 438 | uint16_t codec_type = readWORDTag(io_) * 2; |
371 | 438 | std::string codec = (codec_type == 1) ? "Xmp.video" : "Xmp.audio"; |
372 | | |
373 | 438 | if (uint16_t codec_name_length = readWORDTag(io_) * 2) |
374 | 368 | xmpData()[codec + std::string(".CodecName")] = readStringWcharTag(io_, codec_name_length); |
375 | | |
376 | 438 | if (uint16_t codec_desc_length = readWORDTag(io_)) |
377 | 345 | xmpData()[codec + std::string(".CodecDescription")] = readStringWcharTag(io_, codec_desc_length); |
378 | | |
379 | 438 | uint16_t codec_info_length = readWORDTag(io_); |
380 | 438 | Internal::enforce(codec_info_length && codec_info_length < io_->size() - io_->tell(), |
381 | 438 | Exiv2::ErrorCode::kerCorruptedMetadata); |
382 | 438 | xmpData()[codec + std::string(".CodecInfo")] = readStringTag(io_, codec_info_length); |
383 | 438 | } |
384 | 128 | } // AsfVideo::codecList |
385 | | |
386 | 221 | void AsfVideo::headerExtension() const { |
387 | 221 | io_->seek(io_->tell() + GUID /*reserved1*/ + WORD /*Reserved2*/, BasicIo::beg); |
388 | 221 | auto header_ext_data_length = readDWORDTag(io_); |
389 | 221 | io_->seek(io_->tell() + header_ext_data_length, BasicIo::beg); |
390 | 221 | } // AsfVideo::headerExtension |
391 | | |
392 | 375 | void AsfVideo::extendedContentDescription() { |
393 | 375 | uint16_t content_descriptor_count = readWORDTag(io_); |
394 | 375 | std::string value; |
395 | | |
396 | 2.82k | for (uint16_t i = 0; i < content_descriptor_count; i++) { |
397 | 2.48k | if (uint16_t descriptor_name_length = readWORDTag(io_)) |
398 | 2.03k | value += readStringWcharTag(io_, descriptor_name_length); // Descriptor Name |
399 | | |
400 | 2.48k | uint16_t descriptor_value_data_type = readWORDTag(io_); |
401 | 2.48k | if (uint16_t descriptor_value_length = readWORDTag(io_)) { |
402 | | // Descriptor Value |
403 | 1.92k | switch (descriptor_value_data_type) { |
404 | 1.42k | case 0 /*Unicode string */: |
405 | 1.42k | value += std::string(": ") + readStringWcharTag(io_, descriptor_value_length); |
406 | 1.42k | break; |
407 | 22 | case 1 /*BYTE array */: |
408 | 22 | value += std::string(": ") + readStringTag(io_, descriptor_value_length); |
409 | 22 | break; |
410 | 19 | case 2 /*BOOL*/: |
411 | 19 | value += std::string(": ") + std::to_string(readWORDTag(io_)); |
412 | 19 | break; |
413 | 6 | case 3 /*DWORD */: |
414 | 6 | value += std::string(": ") + std::to_string(readDWORDTag(io_)); |
415 | 6 | break; |
416 | 10 | case 4 /*QWORD */: |
417 | 10 | value += std::string(": ") + std::to_string(readQWORDTag(io_)); |
418 | 10 | break; |
419 | 6 | case 5 /*WORD*/: |
420 | 6 | value += std::string(": ") + std::to_string(readWORDTag(io_)); |
421 | 6 | break; |
422 | 1.92k | } |
423 | 1.92k | } |
424 | 2.45k | value += std::string(", "); |
425 | 2.45k | } |
426 | | |
427 | 343 | xmpData()["Xmp.video.ExtendedContentDescription"] = value; |
428 | 343 | } // AsfVideo::extendedContentDescription |
429 | | |
430 | 52 | void AsfVideo::contentDescription() { |
431 | 52 | uint16_t title_length = readWORDTag(io_); |
432 | 52 | uint16_t author_length = readWORDTag(io_); |
433 | 52 | uint16_t copyright_length = readWORDTag(io_); |
434 | 52 | uint16_t desc_length = readWORDTag(io_); |
435 | 52 | uint16_t rating_length = readWORDTag(io_); |
436 | | |
437 | 52 | if (title_length) |
438 | 33 | xmpData()["Xmp.video.Title"] = readStringWcharTag(io_, title_length); |
439 | | |
440 | 52 | if (author_length) |
441 | 29 | xmpData()["Xmp.video.Author"] = readStringWcharTag(io_, author_length); |
442 | | |
443 | 52 | if (copyright_length) |
444 | 15 | xmpData()["Xmp.video.Copyright"] = readStringWcharTag(io_, copyright_length); |
445 | | |
446 | 52 | if (desc_length) |
447 | 11 | xmpData()["Xmp.video.Description"] = readStringWcharTag(io_, desc_length); |
448 | | |
449 | 52 | if (rating_length) |
450 | 10 | xmpData()["Xmp.video.Rating"] = readStringWcharTag(io_, rating_length); |
451 | | |
452 | 52 | } // AsfVideo::extendedContentDescription |
453 | | |
454 | 231 | void AsfVideo::fileProperties() { |
455 | 231 | DataBuf FileIddBuf(GUID); |
456 | 231 | io_->readOrThrow(FileIddBuf.data(), FileIddBuf.size(), Exiv2::ErrorCode::kerCorruptedMetadata); |
457 | 231 | xmpData()["Xmp.video.FileID"] = GUIDTag(FileIddBuf.data()).to_string(); |
458 | 231 | xmpData()["Xmp.video.FileLength"] = readQWORDTag(io_); |
459 | 231 | xmpData()["Xmp.video.CreationDate"] = readQWORDTag(io_); |
460 | 231 | xmpData()["Xmp.video.DataPackets"] = readQWORDTag(io_); |
461 | 231 | xmpData()["Xmp.video.duration"] = readQWORDTag(io_); |
462 | 231 | xmpData()["Xmp.video.SendDuration"] = readQWORDTag(io_); |
463 | 231 | xmpData()["Xmp.video.Preroll"] = readQWORDTag(io_); |
464 | | |
465 | 231 | io_->seek(io_->tell() + DWORD + DWORD + DWORD, |
466 | 231 | BasicIo::beg); // ignore Flags, Minimum Data Packet Size and Maximum Data Packet Size |
467 | 231 | xmpData()["Xmp.video.MaxBitRate"] = readDWORDTag(io_); |
468 | 231 | } // AsfVideo::fileProperties |
469 | | |
470 | 765 | Image::UniquePtr newAsfInstance(BasicIo::UniquePtr io, bool /*create*/) { |
471 | 765 | auto image = std::make_unique<AsfVideo>(std::move(io)); |
472 | 765 | if (!image->good()) { |
473 | 0 | return nullptr; |
474 | 0 | } |
475 | 765 | return image; |
476 | 765 | } |
477 | | |
478 | 5.15k | bool isAsfType(BasicIo& iIo, bool advance) { |
479 | 5.15k | byte buf[GUID]; |
480 | 5.15k | iIo.read(buf, GUID); |
481 | | |
482 | 5.15k | if (iIo.error() || iIo.eof()) { |
483 | 235 | return false; |
484 | 235 | } |
485 | | |
486 | 4.91k | bool matched = isASFType(buf); |
487 | 4.91k | if (!advance || !matched) { |
488 | 4.91k | iIo.seek(0, BasicIo::beg); |
489 | 4.91k | } |
490 | | |
491 | 4.91k | return matched; |
492 | 5.15k | } |
493 | | |
494 | | } // namespace Exiv2 |