Coverage Report

Created: 2026-05-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
7.38k
bool AsfVideo::GUIDTag::operator==(const AsfVideo::GUIDTag& other) const {
43
7.38k
  return data1_ == other.data1_ && data2_ == other.data2_ && data3_ == other.data3_ && data4_ == other.data4_;
44
7.38k
}
45
46
10.3k
AsfVideo::GUIDTag::GUIDTag(const uint8_t* bytes) {
47
10.3k
  data1_ = Exiv2::getULong(bytes, ByteOrder::littleEndian);
48
10.3k
  data2_ = Exiv2::getUShort(bytes + DWORD, ByteOrder::littleEndian);
49
10.3k
  data3_ = Exiv2::getUShort(bytes + DWORD + WORD, ByteOrder::littleEndian);
50
10.3k
  std::copy(bytes + QWORD, bytes + (2 * QWORD), data4_.begin());
51
10.3k
}
52
53
290
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
290
  return stringFormat("{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}", data1_, data2_, data3_,
58
290
                      data4_[0], data4_[1], data4_[2], data4_[3], data4_[4], data4_[5], data4_[6], data4_[7]);
59
290
}
60
61
20.0k
bool AsfVideo::GUIDTag::operator<(const GUIDTag& other) const {
62
20.0k
  if (data1_ != other.data1_)
63
15.2k
    return data1_ < other.data1_;
64
4.73k
  if (data2_ != other.data2_)
65
56
    return data2_ < other.data2_;
66
4.67k
  if (data3_ != other.data3_)
67
62
    return data3_ < other.data3_;
68
4.61k
  return std::lexicographical_compare(data4_.begin(), data4_.end(), other.data4_.begin(), other.data4_.end());
69
4.67k
}
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
7.38k
static bool isASFType(const byte buf[]) {
179
7.38k
  return Header == AsfVideo::GUIDTag(buf);
180
7.38k
}
181
182
727
AsfVideo::AsfVideo(BasicIo::UniquePtr io) : Image(ImageType::asf, mdNone, std::move(io)) {
183
727
}  // AsfVideo::AsfVideo
184
185
753
std::string AsfVideo::mimeType() const {
186
753
  return "video/asf";
187
753
}
188
189
0
void AsfVideo::writeMetadata() {
190
0
}
191
192
727
void AsfVideo::readMetadata() {
193
727
  if (io_->open() != 0)
194
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
195
196
  // Ensure that this is the correct image type
197
727
  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
727
  IoCloser closer(*io_);
204
727
  clearMetadata();
205
727
  io_->seek(0, BasicIo::beg);
206
727
  height_ = width_ = 1;
207
208
727
  xmpData()["Xmp.video.FileSize"] = io_->size() / 1048576.;
209
727
  xmpData()["Xmp.video.MimeType"] = mimeType();
210
211
727
  decodeBlock();
212
213
727
  xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width_, height_);
214
727
}  // 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.70k
    io->readOrThrow(IdBuf_.data(), IdBuf_.size(), Exiv2::ErrorCode::kerCorruptedMetadata);
219
220
2.70k
    size_ = readQWORDTag(io);
221
2.70k
    if (size_ >= GUID + QWORD)
222
2.54k
      remaining_size_ = size_ - GUID - QWORD;
223
2.70k
  }
224
2.75k
}
225
226
2.76k
void AsfVideo::decodeBlock() {
227
2.76k
  Internal::enforce(GUID + QWORD <= io_->size() - io_->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
228
2.76k
  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.76k
  Internal::enforce(objectHeader.getSize() <= io_->size() - io_->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
234
2.76k
  auto tag = GUIDReferenceTags.find(GUIDTag(objectHeader.getId().data()));
235
236
2.76k
  if (tag != GUIDReferenceTags.end()) {
237
2.04k
    if (tag->second == "Header")
238
700
      decodeHeader();
239
1.34k
    else if (tag->second == "File_Properties")
240
290
      fileProperties();
241
1.05k
    else if (tag->second == "Stream_Properties")
242
214
      streamProperties();
243
838
    else if (tag->second == "Header_Extension")
244
276
      headerExtension();
245
562
    else if (tag->second == "Codec_List")
246
123
      codecList();
247
439
    else if (tag->second == "Extended_Content_Description")
248
329
      extendedContentDescription();
249
110
    else if (tag->second == "Content_Description")
250
95
      contentDescription();
251
15
    else if (tag->second == "Extended_Stream_Properties")
252
0
      extendedStreamProperties();
253
15
    else if (tag->second == "Degradable_JPEG_Media")
254
0
      DegradableJPEGMedia();
255
15
    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
15
      const uint64_t remaining_size = objectHeader.getRemainingSize();
259
15
      Internal::enforce(remaining_size > 0, Exiv2::ErrorCode::kerCorruptedMetadata);
260
15
      io_->seekOrThrow(io_->tell() + remaining_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
261
15
    }
262
2.04k
  } 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
722
    const uint64_t remaining_size = objectHeader.getRemainingSize();
266
722
    Internal::enforce(remaining_size > 0, Exiv2::ErrorCode::kerCorruptedMetadata);
267
722
    io_->seekOrThrow(io_->tell() + remaining_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
268
722
  }
269
270
2.76k
}  // AsfVideo::decodeBlock
271
272
700
void AsfVideo::decodeHeader() {
273
700
  DataBuf nbHeadersBuf(DWORD + 1);
274
700
  io_->readOrThrow(nbHeadersBuf.data(), DWORD, Exiv2::ErrorCode::kerCorruptedMetadata);
275
276
700
  uint32_t nb_headers = Exiv2::getULong(nbHeadersBuf.data(), littleEndian);
277
700
  Internal::enforce(nb_headers < std::numeric_limits<uint32_t>::max(), Exiv2::ErrorCode::kerCorruptedMetadata);
278
700
  io_->seekOrThrow(io_->tell() + (BYTE * 2), BasicIo::beg,
279
700
                   ErrorCode::kerFailedToReadImageData);  // skip two reserved tags
280
2.73k
  for (uint32_t i = 0; i < nb_headers; i++) {
281
2.03k
    decodeBlock();
282
2.03k
  }
283
700
}
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
214
void AsfVideo::streamProperties() {
336
214
  DataBuf streamTypedBuf(GUID);
337
214
  io_->readOrThrow(streamTypedBuf.data(), streamTypedBuf.size(), Exiv2::ErrorCode::kerCorruptedMetadata);
338
339
214
  enum class streamTypeInfo { Audio = 1, Video = 2 };
340
214
  auto stream = streamTypeInfo{0};
341
342
214
  auto tag_stream_type = GUIDReferenceTags.find(GUIDTag(streamTypedBuf.data()));
343
214
  if (tag_stream_type != GUIDReferenceTags.end()) {
344
188
    if (tag_stream_type->second == "Audio_Media")
345
132
      stream = streamTypeInfo::Audio;
346
56
    else if (tag_stream_type->second == "Video_Media")
347
29
      stream = streamTypeInfo::Video;
348
349
188
    io_->seek(io_->tell() + GUID, BasicIo::beg);  // ignore Error Correction Type
350
351
188
    uint64_t time_offset = readQWORDTag(io_);
352
188
    if (stream == streamTypeInfo::Video)
353
29
      xmpData()["Xmp.video.TimeOffset"] = time_offset;
354
159
    else if (stream == streamTypeInfo::Audio)
355
132
      xmpData()["Xmp.audio.TimeOffset"] = time_offset;
356
357
188
    auto specific_data_length = readDWORDTag(io_);
358
188
    auto correction_data_length = readDWORDTag(io_);
359
360
188
    io_->seek(io_->tell() + WORD /*Flags*/ + DWORD /*Reserved*/ + specific_data_length + correction_data_length,
361
188
              BasicIo::beg);
362
188
  }
363
364
214
}  // AsfVideo::streamProperties
365
366
123
void AsfVideo::codecList() {
367
123
  io_->seek(io_->tell() + GUID /*reserved*/, BasicIo::beg);
368
123
  auto entries_count = readDWORDTag(io_);
369
490
  for (uint32_t i = 0; i < entries_count; i++) {
370
367
    uint16_t codec_type = readWORDTag(io_) * 2;
371
367
    std::string codec = (codec_type == 1) ? "Xmp.video" : "Xmp.audio";
372
373
367
    if (uint16_t codec_name_length = readWORDTag(io_) * 2)
374
316
      xmpData()[codec + std::string(".CodecName")] = readStringWcharTag(io_, codec_name_length);
375
376
367
    if (uint16_t codec_desc_length = readWORDTag(io_))
377
263
      xmpData()[codec + std::string(".CodecDescription")] = readStringWcharTag(io_, codec_desc_length);
378
379
367
    uint16_t codec_info_length = readWORDTag(io_);
380
367
    Internal::enforce(codec_info_length && codec_info_length < io_->size() - io_->tell(),
381
367
                      Exiv2::ErrorCode::kerCorruptedMetadata);
382
367
    xmpData()[codec + std::string(".CodecInfo")] = readStringTag(io_, codec_info_length);
383
367
  }
384
123
}  // AsfVideo::codecList
385
386
276
void AsfVideo::headerExtension() const {
387
276
  io_->seek(io_->tell() + GUID /*reserved1*/ + WORD /*Reserved2*/, BasicIo::beg);
388
276
  auto header_ext_data_length = readDWORDTag(io_);
389
276
  io_->seek(io_->tell() + header_ext_data_length, BasicIo::beg);
390
276
}  // AsfVideo::headerExtension
391
392
329
void AsfVideo::extendedContentDescription() {
393
329
  uint16_t content_descriptor_count = readWORDTag(io_);
394
329
  std::string value;
395
396
2.58k
  for (uint16_t i = 0; i < content_descriptor_count; i++) {
397
2.28k
    if (uint16_t descriptor_name_length = readWORDTag(io_))
398
1.84k
      value += readStringWcharTag(io_, descriptor_name_length);  // Descriptor Name
399
400
2.28k
    uint16_t descriptor_value_data_type = readWORDTag(io_);
401
2.28k
    if (uint16_t descriptor_value_length = readWORDTag(io_)) {
402
      // Descriptor Value
403
1.75k
      switch (descriptor_value_data_type) {
404
1.27k
        case 0 /*Unicode string */:
405
1.27k
          value += std::string(": ") + readStringWcharTag(io_, descriptor_value_length);
406
1.27k
          break;
407
28
        case 1 /*BYTE array  */:
408
28
          value += std::string(": ") + readStringTag(io_, descriptor_value_length);
409
28
          break;
410
12
        case 2 /*BOOL*/:
411
12
          value += std::string(": ") + std::to_string(readWORDTag(io_));
412
12
          break;
413
7
        case 3 /*DWORD */:
414
7
          value += std::string(": ") + std::to_string(readDWORDTag(io_));
415
7
          break;
416
12
        case 4 /*QWORD */:
417
12
          value += std::string(": ") + std::to_string(readQWORDTag(io_));
418
12
          break;
419
5
        case 5 /*WORD*/:
420
5
          value += std::string(": ") + std::to_string(readWORDTag(io_));
421
5
          break;
422
1.75k
      }
423
1.75k
    }
424
2.25k
    value += std::string(", ");
425
2.25k
  }
426
427
307
  xmpData()["Xmp.video.ExtendedContentDescription"] = value;
428
307
}  // AsfVideo::extendedContentDescription
429
430
95
void AsfVideo::contentDescription() {
431
95
  uint16_t title_length = readWORDTag(io_);
432
95
  uint16_t author_length = readWORDTag(io_);
433
95
  uint16_t copyright_length = readWORDTag(io_);
434
95
  uint16_t desc_length = readWORDTag(io_);
435
95
  uint16_t rating_length = readWORDTag(io_);
436
437
95
  if (title_length)
438
25
    xmpData()["Xmp.video.Title"] = readStringWcharTag(io_, title_length);
439
440
95
  if (author_length)
441
42
    xmpData()["Xmp.video.Author"] = readStringWcharTag(io_, author_length);
442
443
95
  if (copyright_length)
444
37
    xmpData()["Xmp.video.Copyright"] = readStringWcharTag(io_, copyright_length);
445
446
95
  if (desc_length)
447
53
    xmpData()["Xmp.video.Description"] = readStringWcharTag(io_, desc_length);
448
449
95
  if (rating_length)
450
34
    xmpData()["Xmp.video.Rating"] = readStringWcharTag(io_, rating_length);
451
452
95
}  // AsfVideo::extendedContentDescription
453
454
290
void AsfVideo::fileProperties() {
455
290
  DataBuf FileIddBuf(GUID);
456
290
  io_->readOrThrow(FileIddBuf.data(), FileIddBuf.size(), Exiv2::ErrorCode::kerCorruptedMetadata);
457
290
  xmpData()["Xmp.video.FileID"] = GUIDTag(FileIddBuf.data()).to_string();
458
290
  xmpData()["Xmp.video.FileLength"] = readQWORDTag(io_);
459
290
  xmpData()["Xmp.video.CreationDate"] = readQWORDTag(io_);
460
290
  xmpData()["Xmp.video.DataPackets"] = readQWORDTag(io_);
461
290
  xmpData()["Xmp.video.duration"] = readQWORDTag(io_);
462
290
  xmpData()["Xmp.video.SendDuration"] = readQWORDTag(io_);
463
290
  xmpData()["Xmp.video.Preroll"] = readQWORDTag(io_);
464
465
290
  io_->seek(io_->tell() + DWORD + DWORD + DWORD,
466
290
            BasicIo::beg);  // ignore Flags, Minimum Data Packet Size and Maximum Data Packet Size
467
290
  xmpData()["Xmp.video.MaxBitRate"] = readDWORDTag(io_);
468
290
}  // AsfVideo::fileProperties
469
470
727
Image::UniquePtr newAsfInstance(BasicIo::UniquePtr io, bool /*create*/) {
471
727
  auto image = std::make_unique<AsfVideo>(std::move(io));
472
727
  if (!image->good()) {
473
0
    return nullptr;
474
0
  }
475
727
  return image;
476
727
}
477
478
7.61k
bool isAsfType(BasicIo& iIo, bool advance) {
479
7.61k
  byte buf[GUID];
480
7.61k
  iIo.read(buf, GUID);
481
482
7.61k
  if (iIo.error() || iIo.eof()) {
483
228
    return false;
484
228
  }
485
486
7.38k
  bool matched = isASFType(buf);
487
7.38k
  if (!advance || !matched) {
488
7.38k
    iIo.seek(0, BasicIo::beg);
489
7.38k
  }
490
491
7.38k
  return matched;
492
7.61k
}
493
494
}  // namespace Exiv2