Coverage Report

Created: 2026-08-11 07:29

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.44k
bool AsfVideo::GUIDTag::operator==(const AsfVideo::GUIDTag& other) const {
43
7.44k
  return data1_ == other.data1_ && data2_ == other.data2_ && data3_ == other.data3_ && data4_ == other.data4_;
44
7.44k
}
45
46
7.44k
AsfVideo::GUIDTag::GUIDTag(const uint8_t* bytes) {
47
7.44k
  data1_ = Exiv2::getULong(bytes, ByteOrder::littleEndian);
48
7.44k
  data2_ = Exiv2::getUShort(bytes + DWORD, ByteOrder::littleEndian);
49
7.44k
  data3_ = Exiv2::getUShort(bytes + DWORD + WORD, ByteOrder::littleEndian);
50
7.44k
  std::copy(bytes + QWORD, bytes + (2 * QWORD), data4_.begin());
51
7.44k
}
52
53
0
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
0
  return stringFormat("{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}", data1_, data2_, data3_,
58
0
                      data4_[0], data4_[1], data4_[2], data4_[3], data4_[4], data4_[5], data4_[6], data4_[7]);
59
0
}
60
61
982
bool AsfVideo::GUIDTag::operator<(const GUIDTag& other) const {
62
982
  if (data1_ != other.data1_)
63
982
    return data1_ < other.data1_;
64
0
  if (data2_ != other.data2_)
65
0
    return data2_ < other.data2_;
66
0
  if (data3_ != other.data3_)
67
0
    return data3_ < other.data3_;
68
0
  return std::lexicographical_compare(data4_.begin(), data4_.end(), other.data4_.begin(), other.data4_.end());
69
0
}
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.44k
static bool isASFType(const byte buf[]) {
179
7.44k
  return Header == AsfVideo::GUIDTag(buf);
180
7.44k
}
181
182
AsfVideo::AsfVideo(BasicIo::UniquePtr io, const ImageCtorParams& params) :
183
0
    Image(ImageType::asf, mdNone, std::move(io), params), max_recursion_depth_(params.max_recursion_depth()) {
184
0
}  // AsfVideo::AsfVideo
185
186
0
std::string AsfVideo::mimeType() const {
187
0
  return "video/asf";
188
0
}
189
190
0
void AsfVideo::writeMetadata() {
191
0
}
192
193
0
void AsfVideo::readMetadata() {
194
0
  if (io_->open() != 0)
195
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
196
197
  // Ensure that this is the correct image type
198
0
  if (!isAsfType(*io_, false)) {
199
0
    if (io_->error() || io_->eof())
200
0
      throw Error(ErrorCode::kerFailedToReadImageData);
201
0
    throw Error(ErrorCode::kerNotAnImage, "ASF");
202
0
  }
203
204
0
  IoCloser closer(*io_);
205
0
  clearMetadata();
206
0
  io_->seek(0, BasicIo::beg);
207
0
  height_ = width_ = 1;
208
209
0
  xmpData()["Xmp.video.FileSize"] = io_->size() / 1048576.;
210
0
  xmpData()["Xmp.video.MimeType"] = mimeType();
211
212
0
  decodeBlock(0);
213
214
0
  xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width_, height_);
215
0
}  // AsfVideo::readMetadata
216
217
0
AsfVideo::HeaderReader::HeaderReader(const BasicIo::UniquePtr& io) : IdBuf_(GUID) {
218
0
  if (io->size() >= io->tell() + GUID + QWORD) {
219
0
    io->readOrThrow(IdBuf_.data(), IdBuf_.size(), Exiv2::ErrorCode::kerCorruptedMetadata);
220
221
0
    size_ = readQWORDTag(io);
222
0
    if (size_ >= GUID + QWORD)
223
0
      remaining_size_ = size_ - GUID - QWORD;
224
0
  }
225
0
}
226
227
0
void AsfVideo::decodeBlock(size_t depth) {
228
0
  Internal::enforce(GUID + QWORD <= io_->size() - io_->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
229
0
  HeaderReader objectHeader(io_);
230
#ifdef EXIV2_DEBUG_MESSAGES
231
  EXV_INFO << "decodeBlock = " << GUIDTag(objectHeader.getId().data()).to_string()
232
           << "\tsize= " << objectHeader.getSize() << "\t " << io_->tell() << "/" << io_->size() << '\n';
233
#endif
234
0
  Internal::enforce(objectHeader.getSize() <= io_->size() - io_->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
235
0
  auto tag = GUIDReferenceTags.find(GUIDTag(objectHeader.getId().data()));
236
237
0
  if (tag != GUIDReferenceTags.end()) {
238
0
    if (tag->second == "Header")
239
0
      decodeHeader(depth + 1);
240
0
    else if (tag->second == "File_Properties")
241
0
      fileProperties();
242
0
    else if (tag->second == "Stream_Properties")
243
0
      streamProperties();
244
0
    else if (tag->second == "Header_Extension")
245
0
      headerExtension();
246
0
    else if (tag->second == "Codec_List")
247
0
      codecList();
248
0
    else if (tag->second == "Extended_Content_Description")
249
0
      extendedContentDescription();
250
0
    else if (tag->second == "Content_Description")
251
0
      contentDescription();
252
0
    else if (tag->second == "Extended_Stream_Properties")
253
0
      extendedStreamProperties();
254
0
    else if (tag->second == "Degradable_JPEG_Media")
255
0
      DegradableJPEGMedia();
256
0
    else {  // tag found but not processed
257
      // Make sure that the remaining size is non-zero, so that we won't
258
      // keep revisiting the same location in the file.
259
0
      const uint64_t remaining_size = objectHeader.getRemainingSize();
260
0
      Internal::enforce(remaining_size > 0, Exiv2::ErrorCode::kerCorruptedMetadata);
261
0
      io_->seekOrThrow(io_->tell() + remaining_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
262
0
    }
263
0
  } else {  // tag not found
264
    // Make sure that the remaining size is non-zero, so that we won't keep
265
    // revisiting the same location in the file.
266
0
    const uint64_t remaining_size = objectHeader.getRemainingSize();
267
0
    Internal::enforce(remaining_size > 0, Exiv2::ErrorCode::kerCorruptedMetadata);
268
0
    io_->seekOrThrow(io_->tell() + remaining_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
269
0
  }
270
271
0
}  // AsfVideo::decodeBlock
272
273
0
void AsfVideo::decodeHeader(size_t depth) {
274
0
  Internal::enforce(depth <= max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata);
275
0
  DataBuf nbHeadersBuf(DWORD + 1);
276
0
  io_->readOrThrow(nbHeadersBuf.data(), DWORD, Exiv2::ErrorCode::kerCorruptedMetadata);
277
278
0
  uint32_t nb_headers = Exiv2::getULong(nbHeadersBuf.data(), littleEndian);
279
0
  Internal::enforce(nb_headers < std::numeric_limits<uint32_t>::max(), Exiv2::ErrorCode::kerCorruptedMetadata);
280
0
  io_->seekOrThrow(io_->tell() + (BYTE * 2), BasicIo::beg,
281
0
                   ErrorCode::kerFailedToReadImageData);  // skip two reserved tags
282
0
  for (uint32_t i = 0; i < nb_headers; i++) {
283
0
    decodeBlock(depth);
284
0
  }
285
0
}
286
287
0
void AsfVideo::extendedStreamProperties() {
288
0
  xmpData()["Xmp.video.StartTimecode"] = readQWORDTag(io_);  // Start Time
289
0
  xmpData()["Xmp.video.EndTimecode"] = readWORDTag(io_);     // End Time
290
291
0
  io_->seek(io_->tell() + DWORD, BasicIo::beg);  // ignore Data Bitrate
292
0
  io_->seek(io_->tell() + DWORD, BasicIo::beg);  // ignore Buffer Size
293
0
  io_->seek(io_->tell() + DWORD, BasicIo::beg);  // ignore Initial Buffer Fullness
294
0
  io_->seek(io_->tell() + DWORD, BasicIo::beg);  // ignore Alternate Data Bitrate
295
0
  io_->seek(io_->tell() + DWORD, BasicIo::beg);  // ignore Alternate Buffer Size
296
0
  io_->seek(io_->tell() + DWORD, BasicIo::beg);  // ignore Alternate Initial Buffer Fullness
297
0
  io_->seek(io_->tell() + DWORD, BasicIo::beg);  // ignore Maximum Object Size
298
0
  io_->seek(io_->tell() + DWORD, BasicIo::beg);  // ignore Flags Buffer Size
299
0
  io_->seek(io_->tell() + WORD, BasicIo::beg);   // ignore Flags Stream Number
300
0
  io_->seek(io_->tell() + WORD, BasicIo::beg);   // ignore Stream Language ID Index
301
302
0
  xmpData()["Xmp.video.FrameRate"] = readWORDTag(io_);  // Average Time Per Frame
303
0
  uint16_t stream_name_count = readWORDTag(io_);
304
0
  uint16_t payload_ext_sys_count = readWORDTag(io_);
305
306
0
  for (uint16_t i = 0; i < stream_name_count; i++) {
307
0
    io_->seek(io_->tell() + WORD, BasicIo::beg);  // ignore Language ID Index
308
0
    uint16_t stream_length = readWORDTag(io_);
309
0
    if (stream_length)
310
0
      io_->seek(io_->tell() + stream_length, BasicIo::beg);  // ignore Stream name
311
0
  }
312
313
0
  for (uint16_t i = 0; i < payload_ext_sys_count; i++) {
314
0
    io_->seek(io_->tell() + GUID, BasicIo::beg);  // ignore Extension System ID
315
0
    io_->seek(io_->tell() + WORD, BasicIo::beg);  // ignore Extension Data Size
316
0
    uint16_t ext_sys_info_length = readWORDTag(io_);
317
0
    if (ext_sys_info_length)
318
0
      io_->seek(io_->tell() + ext_sys_info_length, BasicIo::beg);  // ignore Extension System Info
319
0
  }
320
0
}  // AsfVideo::extendedStreamProperties
321
322
0
void AsfVideo::DegradableJPEGMedia() {
323
0
  uint32_t width = readDWORDTag(io_);
324
0
  width_ = width;
325
0
  xmpData_["Xmp.video.Width"] = width;
326
327
0
  uint32_t height = readDWORDTag(io_);
328
0
  height_ = height;
329
0
  xmpData_["Xmp.video.Height"] = height;
330
331
0
  io_->seek(io_->tell() + (WORD * 3) /*3 Reserved*/, BasicIo::beg);
332
333
0
  uint32_t interchange_data_length = readWORDTag(io_);
334
0
  io_->seek(io_->tell() + interchange_data_length /*Interchange data*/, BasicIo::beg);
335
0
}
336
337
0
void AsfVideo::streamProperties() {
338
0
  DataBuf streamTypedBuf(GUID);
339
0
  io_->readOrThrow(streamTypedBuf.data(), streamTypedBuf.size(), Exiv2::ErrorCode::kerCorruptedMetadata);
340
341
0
  enum class streamTypeInfo { Audio = 1, Video = 2 };
342
0
  auto stream = streamTypeInfo{0};
343
344
0
  auto tag_stream_type = GUIDReferenceTags.find(GUIDTag(streamTypedBuf.data()));
345
0
  if (tag_stream_type != GUIDReferenceTags.end()) {
346
0
    if (tag_stream_type->second == "Audio_Media")
347
0
      stream = streamTypeInfo::Audio;
348
0
    else if (tag_stream_type->second == "Video_Media")
349
0
      stream = streamTypeInfo::Video;
350
351
0
    io_->seek(io_->tell() + GUID, BasicIo::beg);  // ignore Error Correction Type
352
353
0
    uint64_t time_offset = readQWORDTag(io_);
354
0
    if (stream == streamTypeInfo::Video)
355
0
      xmpData()["Xmp.video.TimeOffset"] = time_offset;
356
0
    else if (stream == streamTypeInfo::Audio)
357
0
      xmpData()["Xmp.audio.TimeOffset"] = time_offset;
358
359
0
    auto specific_data_length = readDWORDTag(io_);
360
0
    auto correction_data_length = readDWORDTag(io_);
361
362
0
    io_->seek(io_->tell() + WORD /*Flags*/ + DWORD /*Reserved*/ + specific_data_length + correction_data_length,
363
0
              BasicIo::beg);
364
0
  }
365
366
0
}  // AsfVideo::streamProperties
367
368
0
void AsfVideo::codecList() {
369
0
  io_->seek(io_->tell() + GUID /*reserved*/, BasicIo::beg);
370
0
  auto entries_count = readDWORDTag(io_);
371
0
  for (uint32_t i = 0; i < entries_count; i++) {
372
0
    uint16_t codec_type = readWORDTag(io_) * 2;
373
0
    std::string codec = (codec_type == 1) ? "Xmp.video" : "Xmp.audio";
374
375
0
    if (uint16_t codec_name_length = readWORDTag(io_) * 2)
376
0
      xmpData()[codec + std::string(".CodecName")] = readStringWcharTag(io_, codec_name_length);
377
378
0
    if (uint16_t codec_desc_length = readWORDTag(io_))
379
0
      xmpData()[codec + std::string(".CodecDescription")] = readStringWcharTag(io_, codec_desc_length);
380
381
0
    uint16_t codec_info_length = readWORDTag(io_);
382
0
    Internal::enforce(codec_info_length && codec_info_length < io_->size() - io_->tell(),
383
0
                      Exiv2::ErrorCode::kerCorruptedMetadata);
384
0
    xmpData()[codec + std::string(".CodecInfo")] = readStringTag(io_, codec_info_length);
385
0
  }
386
0
}  // AsfVideo::codecList
387
388
0
void AsfVideo::headerExtension() const {
389
0
  io_->seek(io_->tell() + GUID /*reserved1*/ + WORD /*Reserved2*/, BasicIo::beg);
390
0
  auto header_ext_data_length = readDWORDTag(io_);
391
0
  io_->seek(io_->tell() + header_ext_data_length, BasicIo::beg);
392
0
}  // AsfVideo::headerExtension
393
394
0
void AsfVideo::extendedContentDescription() {
395
0
  uint16_t content_descriptor_count = readWORDTag(io_);
396
0
  std::string value;
397
398
0
  for (uint16_t i = 0; i < content_descriptor_count; i++) {
399
0
    if (uint16_t descriptor_name_length = readWORDTag(io_))
400
0
      value += readStringWcharTag(io_, descriptor_name_length);  // Descriptor Name
401
402
0
    uint16_t descriptor_value_data_type = readWORDTag(io_);
403
0
    if (uint16_t descriptor_value_length = readWORDTag(io_)) {
404
      // Descriptor Value
405
0
      switch (descriptor_value_data_type) {
406
0
        case 0 /*Unicode string */:
407
0
          value += std::string(": ") + readStringWcharTag(io_, descriptor_value_length);
408
0
          break;
409
0
        case 1 /*BYTE array  */:
410
0
          value += std::string(": ") + readStringTag(io_, descriptor_value_length);
411
0
          break;
412
0
        case 2 /*BOOL*/:
413
0
          value += std::string(": ") + std::to_string(readWORDTag(io_));
414
0
          break;
415
0
        case 3 /*DWORD */:
416
0
          value += std::string(": ") + std::to_string(readDWORDTag(io_));
417
0
          break;
418
0
        case 4 /*QWORD */:
419
0
          value += std::string(": ") + std::to_string(readQWORDTag(io_));
420
0
          break;
421
0
        case 5 /*WORD*/:
422
0
          value += std::string(": ") + std::to_string(readWORDTag(io_));
423
0
          break;
424
0
      }
425
0
    }
426
0
    value += std::string(", ");
427
0
  }
428
429
0
  xmpData()["Xmp.video.ExtendedContentDescription"] = value;
430
0
}  // AsfVideo::extendedContentDescription
431
432
0
void AsfVideo::contentDescription() {
433
0
  uint16_t title_length = readWORDTag(io_);
434
0
  uint16_t author_length = readWORDTag(io_);
435
0
  uint16_t copyright_length = readWORDTag(io_);
436
0
  uint16_t desc_length = readWORDTag(io_);
437
0
  uint16_t rating_length = readWORDTag(io_);
438
439
0
  if (title_length)
440
0
    xmpData()["Xmp.video.Title"] = readStringWcharTag(io_, title_length);
441
442
0
  if (author_length)
443
0
    xmpData()["Xmp.video.Author"] = readStringWcharTag(io_, author_length);
444
445
0
  if (copyright_length)
446
0
    xmpData()["Xmp.video.Copyright"] = readStringWcharTag(io_, copyright_length);
447
448
0
  if (desc_length)
449
0
    xmpData()["Xmp.video.Description"] = readStringWcharTag(io_, desc_length);
450
451
0
  if (rating_length)
452
0
    xmpData()["Xmp.video.Rating"] = readStringWcharTag(io_, rating_length);
453
454
0
}  // AsfVideo::extendedContentDescription
455
456
0
void AsfVideo::fileProperties() {
457
0
  DataBuf FileIddBuf(GUID);
458
0
  io_->readOrThrow(FileIddBuf.data(), FileIddBuf.size(), Exiv2::ErrorCode::kerCorruptedMetadata);
459
0
  xmpData()["Xmp.video.FileID"] = GUIDTag(FileIddBuf.data()).to_string();
460
0
  xmpData()["Xmp.video.FileLength"] = readQWORDTag(io_);
461
0
  xmpData()["Xmp.video.CreationDate"] = readQWORDTag(io_);
462
0
  xmpData()["Xmp.video.DataPackets"] = readQWORDTag(io_);
463
0
  xmpData()["Xmp.video.duration"] = readQWORDTag(io_);
464
0
  xmpData()["Xmp.video.SendDuration"] = readQWORDTag(io_);
465
0
  xmpData()["Xmp.video.Preroll"] = readQWORDTag(io_);
466
467
0
  io_->seek(io_->tell() + DWORD + DWORD + DWORD,
468
0
            BasicIo::beg);  // ignore Flags, Minimum Data Packet Size and Maximum Data Packet Size
469
0
  xmpData()["Xmp.video.MaxBitRate"] = readDWORDTag(io_);
470
0
}  // AsfVideo::fileProperties
471
472
0
Image::UniquePtr newAsfInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
473
0
  auto image = std::make_unique<AsfVideo>(std::move(io), params);
474
0
  if (!image->good()) {
475
0
    return nullptr;
476
0
  }
477
0
  return image;
478
0
}
479
480
7.69k
bool isAsfType(BasicIo& iIo, bool advance) {
481
7.69k
  byte buf[GUID];
482
7.69k
  iIo.read(buf, GUID);
483
484
7.69k
  if (iIo.error() || iIo.eof()) {
485
252
    return false;
486
252
  }
487
488
7.44k
  bool matched = isASFType(buf);
489
7.44k
  if (!advance || !matched) {
490
7.44k
    iIo.seek(0, BasicIo::beg);
491
7.44k
  }
492
493
7.44k
  return matched;
494
7.69k
}
495
496
}  // namespace Exiv2