Coverage Report

Created: 2026-02-26 07:48

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