Coverage Report

Created: 2026-10-17 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/riffvideo.cpp
Line
Count
Source
1
// ***************************************************************** -*- C++ -*-
2
/* Spec:
3
 * https://learn.microsoft.com/fr-fr/windows/win32/directshow/avi-riff-file-reference
4
 * https://cdn.hackaday.io/files/274271173436768/avi.pdf
5
 * http://abcavi.kibi.ru/docs/odml1.pdf
6
 */
7
// *****************************************************************************
8
9
// included header files
10
#include "riffvideo.hpp"
11
#include "basicio.hpp"
12
#include "config.h"
13
#include "enforce.hpp"
14
#include "error.hpp"
15
#include "futils.hpp"
16
#include "helper_functions.hpp"
17
#include "utils.hpp"
18
19
#include <array>
20
21
namespace Exiv2::Internal {
22
23
const std::map<std::string, std::string> infoTags = {
24
    {"AGES", "Xmp.video.Rated"},
25
    {"CMNT", "Xmp.video.Comment"},
26
    {"CODE", "Xmp.video.EncodedBy"},
27
    {"COMM", "Xmp.video.Comment"},
28
    {"DIRC", "Xmp.video.Director"},
29
    {"DISP", "Xmp.audio.SchemeTitle"},
30
    {"DTIM", "Xmp.video.DateTimeOriginal"},
31
    {"GENR", "Xmp.video.Genre"},
32
    {"IARL", "Xmp.video.ArchivalLocation"},
33
    {"IART", "Xmp.video.Artist"},
34
    {"IAS1", "Xmp.video.Edit1"},
35
    {"IAS2", "Xmp.video.Edit2"},
36
    {"IAS3", "Xmp.video.Edit3"},
37
    {"IAS4", "Xmp.video.Edit4"},
38
    {"IAS5", "Xmp.video.Edit5"},
39
    {"IAS6", "Xmp.video.Edit6"},
40
    {"IAS7", "Xmp.video.Edit7"},
41
    {"IAS8", "Xmp.video.Edit8"},
42
    {"IAS9", "Xmp.video.Edit9"},
43
    {"IBSU", "Xmp.video.BaseURL"},
44
    {"ICAS", "Xmp.audio.DefaultStream"},
45
    {"ICDS", "Xmp.video.CostumeDesigner"},
46
    {"ICMS", "Xmp.video.Commissioned"},
47
    {"ICMT", "Xmp.video.Comment"},
48
    {"ICNM", "Xmp.video.Cinematographer"},
49
    {"ICNT", "Xmp.video.Country"},
50
    {"ICOP", "Xmp.video.Copyright"},
51
    {"ICRD", "Xmp.video.DateTimeDigitized"},
52
    {"ICRP", "Xmp.video.Cropped"},
53
    {"IDIM", "Xmp.video.Dimensions"},
54
    {"IDPI", "Xmp.video.DotsPerInch"},
55
    {"IDST", "Xmp.video.DistributedBy"},
56
    {"IEDT", "Xmp.video.EditedBy"},
57
    {"IENC", "Xmp.video.EncodedBy"},
58
    {"IENG", "Xmp.video.Engineer"},
59
    {"IGNR", "Xmp.video.Genre"},
60
    {"IKEY", "Xmp.video.PerformerKeywords"},
61
    {"ILGT", "Xmp.video.Lightness"},
62
    {"ILGU", "Xmp.video.LogoURL"},
63
    {"ILIU", "Xmp.video.LogoIconURL"},
64
    {"ILNG", "Xmp.video.Language"},
65
    {"IMBI", "Xmp.video.InfoBannerImage"},
66
    {"IMBU", "Xmp.video.InfoBannerURL"},
67
    {"IMED", "Xmp.video.Medium"},
68
    {"IMIT", "Xmp.video.InfoText"},
69
    {"IMIU", "Xmp.video.InfoURL"},
70
    {"IMUS", "Xmp.video.MusicBy"},
71
    {"INAM", "Xmp.video.Title"},
72
    {"IPDS", "Xmp.video.ProductionDesigner"},
73
    {"IPLT", "Xmp.video.NumOfColors"},
74
    {"IPRD", "Xmp.video.Product"},
75
    {"IPRO", "Xmp.video.ProducedBy"},
76
    {"IRIP", "Xmp.video.RippedBy"},
77
    {"IRTD", "Xmp.video.Rating"},
78
    {"ISBJ", "Xmp.video.Subject"},
79
    {"ISFT", "Xmp.video.Software"},
80
    {"ISGN", "Xmp.video.SecondaryGenre"},
81
    {"ISHP", "Xmp.video.Sharpness"},
82
    {"ISRC", "Xmp.video.Source"},
83
    {"ISRF", "Xmp.video.SourceForm"},
84
    {"ISTD", "Xmp.video.ProductionStudio"},
85
    {"ISTR", "Xmp.video.Starring"},
86
    {"ITCH", "Xmp.video.Technician"},
87
    {"IWMU", "Xmp.video.WatermarkURL"},
88
    {"IWRI", "Xmp.video.WrittenBy"},
89
    {"LANG", "Xmp.video.Language"},
90
    {"LOCA", "Xmp.video.LocationInfo"},
91
    {"PRT1", "Xmp.video.Part"},
92
    {"PRT2", "Xmp.video.NumOfParts"},
93
    {"RATE", "Xmp.video.Rate"},
94
    {"STAR", "Xmp.video.Starring"},
95
    {"STAT", "Xmp.video.Statistics"},
96
    {"TAPE", "Xmp.video.TapeName"},
97
    {"TCDO", "Xmp.video.EndTimecode"},
98
    {"TCOD", "Xmp.video.StartTimecode"},
99
    {"TITL", "Xmp.video.Title"},
100
    {"TLEN", "Xmp.video.Length"},
101
    {"TORG", "Xmp.video.Organization"},
102
    {"TRCK", "Xmp.video.TrackNumber"},
103
    {"TURL", "Xmp.video.URL"},
104
    {"TVER", "Xmp.video.SoftwareVersion"},
105
    {"VMAJ", "Xmp.video.VegasVersionMajor"},
106
    {"VMIN", "Xmp.video.VegasVersionMinor"},
107
    {"YEAR", "Xmp.video.Year"},
108
};
109
110
const std::map<uint16_t, std::string> audioEncodingValues = {
111
    {0x1, "Microsoft PCM"},
112
    {0x2, "Microsoft ADPCM"},
113
    {0x3, "Microsoft IEEE float"},
114
    {0x4, "Compaq VSELP"},
115
    {0x5, "IBM CVSD"},
116
    {0x6, "Microsoft a-Law"},
117
    {0x7, "Microsoft u-Law"},
118
    {0x8, "Microsoft DTS"},
119
    {0x9, "DRM"},
120
    {0xa, "WMA 9 Speech"},
121
    {0xb, "Microsoft Windows Media RT Voice"},
122
    {0x10, "OKI-ADPCM"},
123
    {0x11, "Intel IMA/DVI-ADPCM"},
124
    {0x12, "Videologic Mediaspace ADPCM"},
125
    {0x13, "Sierra ADPCM"},
126
    {0x14, "Antex G.723 ADPCM"},
127
    {0x15, "DSP Solutions DIGISTD"},
128
    {0x16, "DSP Solutions DIGIFIX"},
129
    {0x17, "Dialoic OKI ADPCM"},
130
    {0x18, "Media Vision ADPCM"},
131
    {0x19, "HP CU"},
132
    {0x1a, "HP Dynamic Voice"},
133
    {0x20, "Yamaha ADPCM"},
134
    {0x21, "SONARC Speech Compression"},
135
    {0x22, "DSP Group True Speech"},
136
    {0x23, "Echo Speech Corp."},
137
    {0x24, "Virtual Music Audiofile AF36"},
138
    {0x25, "Audio Processing Tech."},
139
    {0x26, "Virtual Music Audiofile AF10"},
140
    {0x27, "Aculab Prosody 1612"},
141
    {0x28, "Merging Tech. LRC"},
142
    {0x30, "Dolby AC2"},
143
    {0x31, "Microsoft GSM610"},
144
    {0x32, "MSN Audio"},
145
    {0x33, "Antex ADPCME"},
146
    {0x34, "Control Resources VQLPC"},
147
    {0x35, "DSP Solutions DIGIREAL"},
148
    {0x36, "DSP Solutions DIGIADPCM"},
149
    {0x37, "Control Resources CR10"},
150
    {0x38, "Natural MicroSystems VBX ADPCM"},
151
    {0x39, "Crystal Semiconductor IMA ADPCM"},
152
    {0x3a, "Echo Speech ECHOSC3"},
153
    {0x3b, "Rockwell ADPCM"},
154
    {0x3c, "Rockwell DIGITALK"},
155
    {0x3d, "Xebec Multimedia"},
156
    {0x40, "Antex G.721 ADPCM"},
157
    {0x41, "Antex G.728 CELP"},
158
    {0x42, "Microsoft MSG723"},
159
    {0x43, "IBM AVC ADPCM"},
160
    {0x45, "ITU-T G.726"},
161
    {0x50, "Microsoft MPEG"},
162
    {0x51, "RT23 or PAC"},
163
    {0x52, "InSoft RT24"},
164
    {0x53, "InSoft PAC"},
165
    {0x55, "MP3"},
166
    {0x59, "Cirrus"},
167
    {0x60, "Cirrus Logic"},
168
    {0x61, "ESS Tech. PCM"},
169
    {0x62, "Voxware Inc."},
170
    {0x63, "Canopus ATRAC"},
171
    {0x64, "APICOM G.726 ADPCM"},
172
    {0x65, "APICOM G.722 ADPCM"},
173
    {0x66, "Microsoft DSAT"},
174
    {0x67, "Microsoft DSAT DISPLAY"},
175
    {0x69, "Voxware Byte Aligned"},
176
    {0x70, "Voxware AC8"},
177
    {0x71, "Voxware AC10"},
178
    {0x72, "Voxware AC16"},
179
    {0x73, "Voxware AC20"},
180
    {0x74, "Voxware MetaVoice"},
181
    {0x75, "Voxware MetaSound"},
182
    {0x76, "Voxware RT29HW"},
183
    {0x77, "Voxware VR12"},
184
    {0x78, "Voxware VR18"},
185
    {0x79, "Voxware TQ40"},
186
    {0x7a, "Voxware SC3"},
187
    {0x7b, "Voxware SC3"},
188
    {0x80, "Soundsoft"},
189
    {0x81, "Voxware TQ60"},
190
    {0x82, "Microsoft MSRT24"},
191
    {0x83, "AT&T G.729A"},
192
    {0x84, "Motion Pixels MVI MV12"},
193
    {0x85, "DataFusion G.726"},
194
    {0x86, "DataFusion GSM610"},
195
    {0x88, "Iterated Systems Audio"},
196
    {0x89, "Onlive"},
197
    {0x8a, "Multitude, Inc. FT SX20"},
198
    {0x8b, "Infocom ITS A/S G.721 ADPCM"},
199
    {0x8c, "Convedia G729"},
200
    {0x8d, "Not specified congruency, Inc."},
201
    {0x91, "Siemens SBC24"},
202
    {0x92, "Sonic Foundry Dolby AC3 APDIF"},
203
    {0x93, "MediaSonic G.723"},
204
    {0x94, "Aculab Prosody 8kbps"},
205
    {0x97, "ZyXEL ADPCM"},
206
    {0x98, "Philips LPCBB"},
207
    {0x99, "Studer Professional Audio Packed"},
208
    {0xa0, "Malden PhonyTalk"},
209
    {0xa1, "Racal Recorder GSM"},
210
    {0xa2, "Racal Recorder G720.a"},
211
    {0xa3, "Racal G723.1"},
212
    {0xa4, "Racal Tetra ACELP"},
213
    {0xb0, "NEC AAC NEC Corporation"},
214
    {0xff, "AAC"},
215
    {0x100, "Rhetorex ADPCM"},
216
    {0x101, "IBM u-Law"},
217
    {0x102, "IBM a-Law"},
218
    {0x103, "IBM ADPCM"},
219
    {0x111, "Vivo G.723"},
220
    {0x112, "Vivo Siren"},
221
    {0x120, "Philips Speech Processing CELP"},
222
    {0x121, "Philips Speech Processing GRUNDIG"},
223
    {0x123, "Digital G.723"},
224
    {0x125, "Sanyo LD ADPCM"},
225
    {0x130, "Sipro Lab ACEPLNET"},
226
    {0x131, "Sipro Lab ACELP4800"},
227
    {0x132, "Sipro Lab ACELP8V3"},
228
    {0x133, "Sipro Lab G.729"},
229
    {0x134, "Sipro Lab G.729A"},
230
    {0x135, "Sipro Lab Kelvin"},
231
    {0x136, "VoiceAge AMR"},
232
    {0x140, "Dictaphone G.726 ADPCM"},
233
    {0x150, "Qualcomm PureVoice"},
234
    {0x151, "Qualcomm HalfRate"},
235
    {0x155, "Ring Zero Systems TUBGSM"},
236
    {0x160, "Microsoft Audio1"},
237
    {0x161, "Windows Media Audio V2 V7 V8 V9 / DivX audio (WMA) / Alex AC3 Audio"},
238
    {0x162, "Windows Media Audio Professional V9"},
239
    {0x163, "Windows Media Audio Lossless V9"},
240
    {0x164, "WMA Pro over S/PDIF"},
241
    {0x170, "UNISYS NAP ADPCM"},
242
    {0x171, "UNISYS NAP ULAW"},
243
    {0x172, "UNISYS NAP ALAW"},
244
    {0x173, "UNISYS NAP 16K"},
245
    {0x174, "MM SYCOM ACM SYC008 SyCom Technologies"},
246
    {0x175, "MM SYCOM ACM SYC701 G726L SyCom Technologies"},
247
    {0x176, "MM SYCOM ACM SYC701 CELP54 SyCom Technologies"},
248
    {0x177, "MM SYCOM ACM SYC701 CELP68 SyCom Technologies"},
249
    {0x178, "Knowledge Adventure ADPCM"},
250
    {0x180, "Fraunhofer IIS MPEG2AAC"},
251
    {0x190, "Digital Theater Systems DTS DS"},
252
    {0x200, "Creative Labs ADPCM"},
253
    {0x202, "Creative Labs FASTSPEECH8"},
254
    {0x203, "Creative Labs FASTSPEECH10"},
255
    {0x210, "UHER ADPCM"},
256
    {0x215, "Ulead DV ACM"},
257
    {0x216, "Ulead DV ACM"},
258
    {0x220, "Quarterdeck Corp."},
259
    {0x230, "I-Link VC"},
260
    {0x240, "Aureal Semiconductor Raw Sport"},
261
    {0x241, "ESST AC3"},
262
    {0x250, "Interactive Products HSX"},
263
    {0x251, "Interactive Products RPELP"},
264
    {0x260, "Consistent CS2"},
265
    {0x270, "Sony SCX"},
266
    {0x271, "Sony SCY"},
267
    {0x272, "Sony ATRAC3"},
268
    {0x273, "Sony SPC"},
269
    {0x280, "TELUM Telum Inc."},
270
    {0x281, "TELUMIA Telum Inc."},
271
    {0x285, "Norcom Voice Systems ADPCM"},
272
    {0x300, "Fujitsu FM TOWNS SND"},
273
    {0x301, "Fujitsu (not specified)"},
274
    {0x302, "Fujitsu (not specified)"},
275
    {0x303, "Fujitsu (not specified)"},
276
    {0x304, "Fujitsu (not specified)"},
277
    {0x305, "Fujitsu (not specified)"},
278
    {0x306, "Fujitsu (not specified)"},
279
    {0x307, "Fujitsu (not specified)"},
280
    {0x308, "Fujitsu (not specified)"},
281
    {0x350, "Micronas Semiconductors, Inc. Development"},
282
    {0x351, "Micronas Semiconductors, Inc. CELP833"},
283
    {0x400, "Brooktree Digital"},
284
    {0x401, "Intel Music Coder (IMC)"},
285
    {0x402, "Ligos Indeo Audio"},
286
    {0x450, "QDesign Music"},
287
    {0x500, "On2 VP7 On2 Technologies"},
288
    {0x501, "On2 VP6 On2 Technologies"},
289
    {0x680, "AT&T VME VMPCM"},
290
    {0x681, "AT&T TCP"},
291
    {0x700, "YMPEG Alpha (dummy for MPEG-2 compressor)"},
292
    {0x8ae, "ClearJump LiteWave (lossless)"},
293
    {0x1000, "Olivetti GSM"},
294
    {0x1001, "Olivetti ADPCM"},
295
    {0x1002, "Olivetti CELP"},
296
    {0x1003, "Olivetti SBC"},
297
    {0x1004, "Olivetti OPR"},
298
    {0x1100, "Lernout & Hauspie"},
299
    {0x1101, "Lernout & Hauspie CELP codec"},
300
    {0x1102, "Lernout & Hauspie SBC codec"},
301
    {0x1103, "Lernout & Hauspie SBC codec"},
302
    {0x1104, "Lernout & Hauspie SBC codec"},
303
    {0x1400, "Norris Comm. Inc."},
304
    {0x1401, "ISIAudio"},
305
    {0x1500, "AT&T Soundspace Music Compression"},
306
    {0x181c, "VoxWare RT24 speech codec"},
307
    {0x181e, "Lucent elemedia AX24000P Music codec"},
308
    {0x1971, "Sonic Foundry LOSSLESS"},
309
    {0x1979, "Innings Telecom Inc. ADPCM"},
310
    {0x1c07, "Lucent SX8300P speech codec"},
311
    {0x1c0c, "Lucent SX5363S G.723 compliant codec"},
312
    {0x1f03, "CUseeMe DigiTalk (ex-Rocwell)"},
313
    {0x1fc4, "NCT Soft ALF2CD ACM"},
314
    {0x2000, "FAST Multimedia DVM"},
315
    {0x2001, "Dolby DTS (Digital Theater System)"},
316
    {0x2002, "RealAudio 1 / 2 14.4"},
317
    {0x2003, "RealAudio 1 / 2 28.8"},
318
    {0x2004, "RealAudio G2 / 8 Cook (low bitrate)"},
319
    {0x2005, "RealAudio 3 / 4 / 5 Music (DNET)"},
320
    {0x2006, "RealAudio 10 AAC (RAAC)"},
321
    {0x2007, "RealAudio 10 AAC+ (RACP)"},
322
    {0x2500, "Reserved range to 0x2600 Microsoft"},
323
    {0x3313, "makeAVIS (ffvfw fake AVI sound from AviSynth scripts)"},
324
    {0x4143, "Divio MPEG-4 AAC audio"},
325
    {0x4201, "Nokia adaptive multirate"},
326
    {0x4243, "Divio G726 Divio, Inc."},
327
    {0x434c, "LEAD Speech"},
328
    {0x564c, "LEAD Vorbis"},
329
    {0x5756, "WavPack Audio"},
330
    {0x674f, "Ogg Vorbis (mode 1)"},
331
    {0x6750, "Ogg Vorbis (mode 2)"},
332
    {0x6751, "Ogg Vorbis (mode 3)"},
333
    {0x676f, "Ogg Vorbis (mode 1+)"},
334
    {0x6770, "Ogg Vorbis (mode 2+)"},
335
    {0x6771, "Ogg Vorbis (mode 3+)"},
336
    {0x7000, "3COM NBX 3Com Corporation"},
337
    {0x706d, "FAAD AAC"},
338
    {0x7a21, "GSM-AMR (CBR, no SID)"},
339
    {0x7a22, "GSM-AMR (VBR, including SID)"},
340
    {0xa100, "Comverse Infosys Ltd. G723 1"},
341
    {0xa101, "Comverse Infosys Ltd. AVQSBC"},
342
    {0xa102, "Comverse Infosys Ltd. OLDSBC"},
343
    {0xa103, "Symbol Technologies G729A"},
344
    {0xa104, "VoiceAge AMR WB VoiceAge Corporation"},
345
    {0xa105, "Ingenient Technologies Inc. G726"},
346
    {0xa106, "ISO/MPEG-4 advanced audio Coding"},
347
    {0xa107, "Encore Software Ltd G726"},
348
    {0xa109, "Speex ACM Codec xiph.org"},
349
    {0xdfac, "DebugMode SonicFoundry Vegas FrameServer ACM Codec"},
350
    {0xe708, "Unknown -"},
351
    {0xf1ac, "Free Lossless Audio Codec FLAC"},
352
    {0xfffe, "Extensible"},
353
    {0xffff, "Development"},
354
};
355
356
}  // namespace Exiv2::Internal
357
// *****************************************************************************
358
// class member definitions
359
360
namespace Exiv2 {
361
362
enum streamTypeInfo { Audio = 1, MIDI, Text, Video };
363
364
RiffVideo::RiffVideo(BasicIo::UniquePtr io, const ImageCtorParams& params) :
365
432
    Image(ImageType::riff, mdNone, std::move(io), params) {
366
432
}  // RiffVideo::RiffVideo
367
368
432
std::string RiffVideo::mimeType() const {
369
432
  return "video/riff";
370
432
}
371
372
0
void RiffVideo::writeMetadata() {
373
0
}  // RiffVideo::writeMetadata
374
375
432
void RiffVideo::readMetadata() {
376
432
  if (io_->open() != 0)
377
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
378
379
  // Ensure that this is the correct image type
380
432
  if (!isRiffType(*io_, false)) {
381
0
    if (io_->error() || io_->eof())
382
0
      throw Error(ErrorCode::kerFailedToReadImageData);
383
0
    throw Error(ErrorCode::kerNotAnImage, "RIFF");
384
0
  }
385
386
432
  IoCloser closer(*io_);
387
432
  clearMetadata();
388
389
432
  xmpData_["Xmp.video.FileSize"] = io_->size();
390
432
  xmpData_["Xmp.video.MimeType"] = mimeType();
391
392
432
  HeaderReader header(io_);
393
432
  xmpData_["Xmp.video.Container"] = header.getId();
394
395
432
  xmpData_["Xmp.video.FileType"] = readStringTag(io_);
396
397
432
  decodeBlocks();
398
432
}  // RiffVideo::readMetadata
399
400
30.0k
RiffVideo::HeaderReader::HeaderReader(const BasicIo::UniquePtr& io) {
401
30.0k
  Internal::enforce(io->size() > io->tell() + DWORD + DWORD, Exiv2::ErrorCode::kerCorruptedMetadata);
402
30.0k
  id_ = readStringTag(io);
403
30.0k
  size_ = readDWORDTag(io);
404
30.0k
}
405
406
312k
bool RiffVideo::equal(const std::string& str1, const std::string& str2) {
407
312k
  if (str1.size() != str2.size())
408
304k
    return false;
409
410
8.01k
  return Internal::upper(str1) == str2;
411
312k
}
412
413
458
void RiffVideo::readList(const HeaderReader& header_) {
414
458
  std::string chunk_type = readStringTag(io_);
415
416
#ifdef EXIV2_DEBUG_MESSAGES
417
  EXV_INFO << "-> Reading list : id= " << header_.getId() << "  type= " << chunk_type << " size= " << header_.getSize()
418
           << "(" << io_->tell() << "/" << io_->size() << ")" << '\n';
419
#endif
420
421
458
  if (equal(chunk_type, CHUNK_ID_INFO))
422
241
    readInfoListChunk(header_.getSize());
423
217
  else if (equal(chunk_type, CHUNK_ID_MOVI)) {
424
7
    readMoviList(header_.getSize());
425
7
  }
426
458
}
427
428
29.0k
void RiffVideo::readChunk(const HeaderReader& header_) {
429
#ifdef EXIV2_DEBUG_MESSAGES
430
  if (header_.getSize())
431
    EXV_INFO << "--> Reading Chunk : [" << header_.getId() << "] size= " << header_.getSize() << "(" << io_->tell()
432
             << "/" << io_->size() << ")" << '\n';
433
#endif
434
435
29.0k
  if (equal(header_.getId(), CHUNK_ID_AVIH))
436
244
    readAviHeader();
437
28.8k
  else if (equal(header_.getId(), CHUNK_ID_STRH))
438
162
    readStreamHeader();
439
28.6k
  else if (equal(header_.getId(), CHUNK_ID_STRF))
440
78
    readStreamFormat(header_.getSize());
441
28.6k
  else if (equal(header_.getId(), CHUNK_ID_FMT)) {
442
721
    streamType_ = Audio;
443
721
    readStreamFormat(header_.getSize());
444
27.8k
  } else if (equal(header_.getId(), CHUNK_ID_STRD))
445
20
    readStreamData(header_.getSize());
446
27.8k
  else if (equal(header_.getId(), CHUNK_ID_STRN))
447
2
    StreamName(header_.getSize());
448
27.8k
  else if (equal(header_.getId(), CHUNK_ID_VPRP))
449
10
    readVPRPChunk(header_.getSize());
450
27.8k
  else if (equal(header_.getId(), CHUNK_ID_IDX1))
451
10
    readIndexChunk(header_.getSize());
452
27.8k
  else if (equal(header_.getId(), CHUNK_ID_DATA))
453
48
    readDataChunk(header_.getSize());
454
27.7k
  else if (equal(header_.getId(), CHUNK_ID_JUNK))
455
18
    readJunk(header_.getSize());
456
27.7k
  else {
457
#ifdef EXIV2_DEBUG_MESSAGES
458
    if (header_.getSize())
459
      EXV_WARNING << "--> Ignoring Chunk : " << header_.getId() << "] size= " << header_.getSize() << "(" << io_->tell()
460
                  << "/" << io_->size() << ")" << '\n';
461
#endif
462
27.7k
    io_->seekOrThrow(io_->tell() + header_.getSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData);
463
27.7k
  }
464
29.0k
}
465
466
432
void RiffVideo::decodeBlocks() {
467
29.6k
  do {
468
29.6k
    if (HeaderReader header(io_); equal(header.getId(), CHUNK_ID_LIST)) {
469
458
      readList(header);
470
29.1k
    } else {
471
29.1k
      readChunk(header);
472
29.1k
    }
473
29.6k
  } while (!io_->eof() && io_->tell() < io_->size());
474
432
}  // RiffVideo::decodeBlock
475
476
244
void RiffVideo::readAviHeader() {
477
#ifdef EXIV2_DEBUG_MESSAGES
478
  EXV_INFO << "--> dwMicroSecPerFrame    = " << readDWORDTag(io_) << '\n';
479
  EXV_INFO << "--> dwMaxBytesPerSec      = " << readDWORDTag(io_) << '\n';
480
  EXV_INFO << "--> dwPaddingGranularity  = " << readDWORDTag(io_) << '\n';
481
  EXV_INFO << "--> dwFlags               = " << readDWORDTag(io_) << '\n';
482
  EXV_INFO << "--> dwTotalFrames         = " << readDWORDTag(io_) << '\n';
483
  EXV_INFO << "--> dwInitialFrames       = " << readDWORDTag(io_) << '\n';
484
  EXV_INFO << "--> dwStreams             = " << readDWORDTag(io_) << '\n';
485
  EXV_INFO << "--> dwSuggestedBufferSize = " << readDWORDTag(io_) << '\n';
486
  EXV_INFO << "--> dwWidth               = " << readDWORDTag(io_) << '\n';
487
  EXV_INFO << "--> dwHeight              = " << readDWORDTag(io_) << '\n';
488
  EXV_INFO << "--> dwReserved1           = " << readDWORDTag(io_) << '\n';
489
  EXV_INFO << "--> dwReserved2           = " << readDWORDTag(io_) << '\n';
490
  EXV_INFO << "--> dwReserved3           = " << readDWORDTag(io_) << '\n';
491
  EXV_INFO << "--> dwReserved4           = " << readDWORDTag(io_) << '\n';
492
  if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
493
    io_->seekOrThrow(io_->tell() - DWORD * 14, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
494
#endif
495
496
244
  uint32_t TimeBetweenFrames = readDWORDTag(io_);
497
244
  xmpData_["Xmp.video.MicroSecPerFrame"] = TimeBetweenFrames;
498
244
  double frame_rate = 1000000. / TimeBetweenFrames;
499
500
244
  xmpData_["Xmp.video.MaxDataRate"] = readDWORDTag(io_);  // MaximumDataRate
501
502
244
  io_->seekOrThrow(io_->tell() + (DWORD * 2), BasicIo::beg,
503
244
                   ErrorCode::kerFailedToReadImageData);  // ignore PaddingGranularity and Flags
504
505
244
  uint32_t frame_count = readDWORDTag(io_);  // TotalNumberOfFrames
506
244
  xmpData_["Xmp.video.FrameCount"] = frame_count;
507
508
244
  io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg,
509
244
                   ErrorCode::kerFailedToReadImageData);  // ignore NumberOfInitialFrames
510
511
244
  xmpData_["Xmp.audio.ChannelType"] = getStreamType(readDWORDTag(io_));  // NumberOfStreams
512
513
244
  xmpData_["Xmp.video.StreamCount"] = readDWORDTag(io_);  // SuggestedBufferSize
514
515
244
  uint32_t width = readDWORDTag(io_);
516
244
  xmpData_["Xmp.video.Width"] = width;
517
518
244
  uint32_t height = readDWORDTag(io_);
519
244
  xmpData_["Xmp.video.Height"] = height;
520
521
244
  io_->seekOrThrow(io_->tell() + (DWORD * 4), BasicIo::beg,
522
244
                   ErrorCode::kerFailedToReadImageData);  // TimeScale, DataRate, StartTime, DataLength
523
524
244
  xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width, height);
525
526
244
  fillDuration(frame_rate, frame_count);
527
244
}
528
529
162
void RiffVideo::readStreamHeader() {
530
162
  std::string stream = readStringTag(io_);
531
162
  streamType_ = (equal(stream, "VIDS")) ? Video : Audio;
532
533
#ifdef EXIV2_DEBUG_MESSAGES
534
  EXV_INFO << "--> fccType                = " << stream << '\n';
535
  EXV_INFO << "--> fccHandler             = " << readStringTag(io_) << '\n';
536
  EXV_INFO << "--> dwFlags                = " << readDWORDTag(io_) << '\n';
537
  EXV_INFO << "--> wPriority              = " << readWORDTag(io_) << '\n';
538
  EXV_INFO << "--> wLanguage              = " << readWORDTag(io_) << '\n';
539
  EXV_INFO << "--> dwInitialFrames        = " << readDWORDTag(io_) << '\n';  // 20
540
  EXV_INFO << "--> dwScale                = " << readDWORDTag(io_) << '\n';
541
  EXV_INFO << "--> dwRate                 = " << readDWORDTag(io_) << '\n';
542
  EXV_INFO << "--> dwStart                = " << readDWORDTag(io_) << '\n';
543
  EXV_INFO << "--> dwLength               = " << readDWORDTag(io_) << '\n';
544
  EXV_INFO << "--> dwSuggestedBufferSize  = " << readDWORDTag(io_) << '\n';  // 40
545
  EXV_INFO << "--> dwSampleSize           = " << readDWORDTag(io_) << '\n';
546
  EXV_INFO << "--> Left                   = " << readWORDTag(io_) << '\n';
547
  EXV_INFO << "--> top                    = " << readWORDTag(io_) << '\n';
548
  EXV_INFO << "--> right                  = " << readWORDTag(io_) << '\n';
549
  EXV_INFO << "--> bottom                 = " << readWORDTag(io_) << '\n';
550
  EXV_INFO << "--> XXXXXX                 = " << readDWORDTag(io_) << '\n';  // 56
551
  if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
552
    io_->seekOrThrow(io_->tell() - DWORD * 13, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
553
#endif
554
555
162
  xmpData_["Xmp.video.Codec"] = readStringTag(io_);  // DataHandler
556
557
162
  io_->seekOrThrow(io_->tell() + (DWORD * 2) + (WORD * 2), BasicIo::beg,
558
162
                   ErrorCode::kerFailedToReadImageData);  // dwFlags, wPriority, wLanguage, dwInitialFrames
559
560
162
  uint32_t divisor = readDWORDTag(io_);  // TimeScale
561
562
162
  if (divisor) {
563
135
    auto rate = static_cast<double>(readDWORDTag(io_)) / divisor;
564
135
    xmpData_[(streamType_ == Video) ? "Xmp.video.FrameRate" : "Xmp.audio.SampleRate"] = rate;
565
135
  }
566
162
  io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData);  // dwStart
567
568
162
  if (divisor) {
569
133
    auto frame_count = static_cast<double>(readDWORDTag(io_)) / divisor;  // DataLength
570
133
    xmpData_[(streamType_ == Video) ? "Xmp.video.FrameCount" : "Xmp.audio.FrameCount"] = frame_count;
571
133
  }
572
573
162
  io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData);  // dwSuggestedBufferSize
574
575
162
  xmpData_[(streamType_ == Video) ? "Xmp.video.VideoQuality" : "Xmp.video.StreamQuality"] = readDWORDTag(io_);
576
577
162
  xmpData_[(streamType_ == Video) ? "Xmp.video.VideoSampleSize" : "Xmp.video.StreamSampleSize"] = readDWORDTag(io_);
578
162
  io_->seekOrThrow(io_->tell() + (DWORD * 2), BasicIo::beg, ErrorCode::kerFailedToReadImageData);
579
162
}
580
581
799
void RiffVideo::readStreamFormat(uint64_t size_) {
582
  // The structure of the strf chunk depends on the media type. Video streams use the BITMAPINFOHEADER structure,
583
  // whereas audio streams use the WAVEFORMATEX structure.
584
585
#ifdef EXIV2_DEBUG_MESSAGES
586
  if (streamType_ == Audio) {
587
    EXV_INFO << "--> wFormatTag      = " << readWORDTag(io_) << '\n';
588
    EXV_INFO << "--> nChannels       = " << readWORDTag(io_) << '\n';
589
    EXV_INFO << "--> nSamplesPerSec  = " << readDWORDTag(io_) << '\n';
590
    EXV_INFO << "--> nAvgBytesPerSec = " << readDWORDTag(io_) << '\n';
591
    EXV_INFO << "--> nBlockAlign     = " << readWORDTag(io_) << '\n';
592
    EXV_INFO << "--> wBitsPerSample  = " << readWORDTag(io_) << '\n';
593
    if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
594
      io_->seekOrThrow(io_->tell() - DWORD * 4, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
595
  } else if (streamType_ == Video) {
596
    EXV_INFO << "--> biSize            = " << readDWORDTag(io_) << '\n';
597
    EXV_INFO << "--> biWidth           = " << readDWORDTag(io_) << '\n';
598
    EXV_INFO << "--> biHeight          = " << readDWORDTag(io_) << '\n';
599
    EXV_INFO << "--> biPlanes          = " << readWORDTag(io_) << '\n';
600
    EXV_INFO << "--> biBitCount        = " << readWORDTag(io_) << '\n';
601
    EXV_INFO << "--> biCompression     = " << readDWORDTag(io_) << '\n';
602
    EXV_INFO << "--> biSizeImage       = " << readDWORDTag(io_) << '\n';
603
    EXV_INFO << "--> biXPelsPerMeter   = " << readDWORDTag(io_) << '\n';
604
    EXV_INFO << "--> biYPelsPerMeter   = " << readDWORDTag(io_) << '\n';
605
    EXV_INFO << "--> biClrUsed         = " << readDWORDTag(io_) << '\n';
606
    EXV_INFO << "--> biClrImportant    = " << readDWORDTag(io_) << '\n';
607
    if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
608
      io_->seekOrThrow(io_->tell() - DWORD * 10, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
609
  }
610
#endif
611
612
799
  if (streamType_ == Video) {
613
66
    io_->seekOrThrow(io_->tell() + (DWORD * 3), BasicIo::beg,
614
66
                     ErrorCode::kerFailedToReadImageData);  // ignore biSize, biWidth, biHeight
615
66
    xmpData_["Xmp.video.Planes"] = readWORDTag(io_);
616
66
    xmpData_["Xmp.video.PixelDepth"] = readWORDTag(io_);
617
66
    xmpData_["Xmp.video.Compressor"] = readStringTag(io_);
618
66
    xmpData_["Xmp.video.ImageLength"] = readDWORDTag(io_);
619
66
    xmpData_["Xmp.video.PixelPerMeterX"] = readQWORDTag(io_);
620
66
    xmpData_["Xmp.video.PixelPerMeterY"] = readQWORDTag(io_);
621
66
    if (uint32_t NumOfColours = readDWORDTag(io_))
622
31
      xmpData_["Xmp.video.NumOfColours"] = NumOfColours;
623
35
    else
624
35
      xmpData_["Xmp.video.NumOfColours"] = "Unspecified";
625
66
    if (uint32_t NumIfImpColours = readDWORDTag(io_))
626
29
      xmpData_["Xmp.video.NumIfImpColours"] = NumIfImpColours;
627
37
    else
628
37
      xmpData_["Xmp.video.NumIfImpColours"] = "All";
629
733
  } else if (streamType_ == Audio) {
630
723
    uint16_t format_tag = readWORDTag(io_);
631
723
    if (auto it = Internal::audioEncodingValues.find(format_tag); it != Internal::audioEncodingValues.end())
632
123
      xmpData_["Xmp.audio.Compressor"] = it->second;
633
600
    else
634
600
      xmpData_["Xmp.audio.Compressor"] = format_tag;
635
636
723
    xmpData_["Xmp.audio.ChannelType"] = getStreamType(readDWORDTag(io_));
637
723
    xmpData_["Xmp.audio.SampleRate"] = readDWORDTag(io_);                                      // nSamplesPerSec
638
723
    io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData);  // nAvgBytesPerSec
639
723
    xmpData_["Xmp.audio.SampleType"] = readDWORDTag(io_);                                      // nBlockAlign
640
723
    xmpData_["Xmp.audio.BitsPerSample"] = readDWORDTag(io_);                                   // wBitsPerSample
641
723
    if (xmpData_["Xmp.video.FileType"].toString() == "AVI ")
642
60
      io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData);  // cbSize
643
723
  } else {
644
10
    io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
645
10
  }
646
799
}
647
648
20
void RiffVideo::readStreamData(uint64_t size_) const {
649
20
  io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
650
20
}
651
652
2
void RiffVideo::StreamName(uint64_t size_) const {
653
  // This element contains a name for the stream. That stream name should only use plain ASCII, especially not UTF-8.
654
2
  io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
655
2
}
656
657
241
void RiffVideo::readInfoListChunk(uint64_t size_) {
658
241
  uint64_t current_size = DWORD;
659
660
  // Add the elements to a temporary table first, so that we can check
661
  // for duplicates before adding them to xmpData_.
662
241
  std::map<std::string, std::string> table;
663
1.34k
  while (current_size < size_) {
664
1.10k
    std::string type = readStringTag(io_);
665
1.10k
    size_t size = readDWORDTag(io_);
666
1.10k
    std::string content = readStringTag(io_, size);
667
1.10k
    if (auto it = Internal::infoTags.find(type); it != Internal::infoTags.end()) {
668
      // Check that it isn't a duplicate.
669
11
      Internal::enforce(table.find(it->second) == table.end(), ErrorCode::kerCorruptedMetadata);
670
11
      table[it->second] = content;
671
11
    }
672
1.10k
    current_size += DWORD * 2;
673
1.10k
    current_size += size;
674
1.10k
  }
675
  // Copy the elements from the temporary table to xmpData_.
676
241
  for (const auto& it : table) {
677
6
    xmpData_[it.first] = it.second;
678
6
  }
679
241
}
680
681
7
void RiffVideo::readMoviList(uint64_t size_) const {
682
7
  io_->seekOrThrow(io_->tell() + size_ - DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
683
7
}
684
685
10
void RiffVideo::readVPRPChunk(uint64_t size_) const {
686
#ifdef EXIV2_DEBUG_MESSAGES
687
  EXV_INFO << "--> VideoFormatToken         = " << readDWORDTag(io_) << '\n';
688
  EXV_INFO << "--> VideoStandard            = " << readDWORDTag(io_) << '\n';
689
  EXV_INFO << "--> VerticalRefreshRate      = " << readDWORDTag(io_) << '\n';
690
  EXV_INFO << "--> HTotalInT                = " << readDWORDTag(io_) << '\n';
691
  EXV_INFO << "--> VTotalInLines            = " << readDWORDTag(io_) << '\n';
692
  EXV_INFO << "--> FrameAspectRatio Height  = " << readWORDTag(io_) << '\n';
693
  EXV_INFO << "--> FrameAspectRatio Width   = " << readWORDTag(io_) << '\n';
694
  EXV_INFO << "--> FrameWidthInPixels       = " << readDWORDTag(io_) << '\n';
695
  EXV_INFO << "--> FrameHeightInLines       = " << readDWORDTag(io_) << '\n';
696
  EXV_INFO << "--> CompressedBMHeight       = " << readDWORDTag(io_) << '\n';
697
  EXV_INFO << "--> FieldPerFrame            = " << readDWORDTag(io_) << '\n';
698
  EXV_INFO << "--> CompressedBMWidth        = " << readDWORDTag(io_) << '\n';
699
  EXV_INFO << "--> ValidBMHeight            = " << readDWORDTag(io_) << '\n';
700
  EXV_INFO << "--> ValidBMWidth             = " << readDWORDTag(io_) << '\n';
701
  EXV_INFO << "--> ValidBMXOffset           = " << readDWORDTag(io_) << '\n';
702
  EXV_INFO << "--> ValidBMYOffset           = " << readDWORDTag(io_) << '\n';
703
  EXV_INFO << "--> VideoXOffsetInT          = " << readDWORDTag(io_) << '\n';
704
  EXV_INFO << "--> VideoYValidStartLine     = " << readDWORDTag(io_) << '\n';
705
  if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
706
    io_->seekOrThrow(io_->tell() - DWORD * 17, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
707
#endif
708
10
  io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
709
10
}
710
711
10
void RiffVideo::readIndexChunk(uint64_t size_) const {
712
#ifdef EXIV2_DEBUG_MESSAGES
713
  uint64_t current_size = 0;
714
  while (current_size < size_) {
715
    EXV_DEBUG << "--> Identifier               = " << readStringTag(io_) << "\t(" << current_size << "/" << size_ << ")"
716
              << '\n';
717
    EXV_DEBUG << "--> Flags                    = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")"
718
              << '\n';
719
    EXV_DEBUG << "--> Offset                   = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")"
720
              << '\n';
721
    EXV_DEBUG << "--> Length                   = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")"
722
              << '\n';
723
    current_size += DWORD * 4;
724
  }
725
  if (LogMsg::debug >= LogMsg::level() && LogMsg::handler())
726
    io_->seekOrThrow(io_->tell() - size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
727
#endif
728
10
  io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
729
10
}
730
731
48
void RiffVideo::readDataChunk(uint64_t size_) const {
732
#ifdef EXIV2_DEBUG_MESSAGES
733
  EXV_INFO << "--> Data               = " << readStringTag(io_, static_cast<size_t>(size_)) << '\n';
734
  uint64_t readed_size = size_;
735
  if (size_ % 2 != 0) {
736
    EXV_INFO << "--> pad byte          = " << readStringTag(io_, 1) << '\n';
737
    readed_size += 1;
738
  }
739
  if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
740
    io_->seekOrThrow(io_->tell() - readed_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
741
#endif
742
48
  io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
743
48
  if (size_ % 2 != 0)
744
41
    io_->seekOrThrow(io_->tell() + 1, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
745
48
}
746
747
18
void RiffVideo::readJunk(uint64_t size_) const {
748
18
  io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
749
18
}
750
751
959
std::string RiffVideo::getStreamType(uint32_t stream) {
752
959
  if (stream == 1)
753
55
    return "Mono";
754
904
  if (stream == 2)
755
7
    return "Stereo";
756
897
  if (stream == 5)
757
175
    return "5.1 Surround Sound";
758
722
  if (stream == 7)
759
7
    return "7.1 Surround Sound";
760
715
  return "Mono";
761
722
}
762
763
225
void RiffVideo::fillDuration(double frame_rate, size_t frame_count) {
764
225
  if (frame_rate == 0)
765
0
    return;
766
767
225
  auto duration = static_cast<uint64_t>(frame_count * 1000. / frame_rate);
768
225
  xmpData_["Xmp.video.FileDataRate"] = io_->size() / (1048576. * duration);
769
225
  xmpData_["Xmp.video.Duration"] = duration;  // Duration in number of seconds
770
225
}  // RiffVideo::fillDuration
771
772
432
Image::UniquePtr newRiffInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
773
432
  auto image = std::make_unique<RiffVideo>(std::move(io), params);
774
432
  if (!image->good()) {
775
0
    return nullptr;
776
0
  }
777
432
  return image;
778
432
}
779
780
11.6k
bool isRiffType(BasicIo& iIo, bool advance) {
781
11.6k
  constexpr int len = 4;
782
11.6k
  const std::array<byte, len> RiffVideoId{'R', 'I', 'F', 'F'};
783
11.6k
  std::array<byte, len> buf;
784
11.6k
  iIo.read(buf.data(), len);
785
11.6k
  if (iIo.error() || iIo.eof()) {
786
325
    return false;
787
325
  }
788
11.3k
  bool matched = buf == RiffVideoId;
789
11.3k
  if (!advance || !matched) {
790
11.3k
    iIo.seek(-1 * len, BasicIo::cur);
791
11.3k
  }
792
11.3k
  return matched;
793
11.6k
}
794
}  // namespace Exiv2