File Coverage

blib/lib/Image/ExifTool/RIFF.pm
Criterion Covered Total %
statement 129 327 39.4
branch 57 192 29.6
condition 27 101 26.7
subroutine 8 14 57.1
pod 0 10 0.0
total 221 644 34.3


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: RIFF.pm
3             #
4             # Description: Read RIFF/AVI/WAV meta information
5             #
6             # Revisions: 09/14/2005 - P. Harvey Created
7             # 06/28/2017 - PH Added MBWF/RF64 support
8             #
9             # References: 1) http://www.exif.org/Exif2-2.PDF
10             # 2) http://www.vlsi.fi/datasheets/vs1011.pdf
11             # 3) http://www.music-center.com.br/spec_rif.htm
12             # 4) http://www.codeproject.com/audio/wavefiles.asp
13             # 5) http://msdn.microsoft.com/archive/en-us/directx9_c/directx/htm/avirifffilereference.asp
14             # 6) http://research.microsoft.com/invisible/tests/riff.h.htm
15             # 7) http://www.onicos.com/staff/iz/formats/wav.html
16             # 8) http://graphics.cs.uni-sb.de/NMM/dist-0.9.1/Docs/Doxygen/html/mmreg_8h-source.html
17             # 9) http://developers.videolan.org/vlc/vlc/doc/doxygen/html/codecs_8h-source.html
18             # 10) http://wiki.multimedia.cx/index.php?title=TwoCC
19             # 11) Andreas Winter (SCLive) private communication
20             # 12) http://abcavi.kibi.ru/infotags.htm
21             # 13) http://tech.ebu.ch/docs/tech/tech3285.pdf
22             # 14) https://developers.google.com/speed/webp/docs/riff_container
23             # 15) https://tech.ebu.ch/docs/tech/tech3306-2009.pdf
24             # 16) https://sites.google.com/site/musicgapi/technical-documents/wav-file-format
25             #------------------------------------------------------------------------------
26              
27             package Image::ExifTool::RIFF;
28              
29 9     9   4929 use strict;
  9         22  
  9         310  
30 9     9   48 use vars qw($VERSION $AUTOLOAD);
  9         21  
  9         457  
31 9     9   53 use Image::ExifTool qw(:DataAccess :Utils);
  9         18  
  9         64473  
32              
33             $VERSION = '1.61';
34              
35             sub ConvertTimecode($);
36             sub ProcessSGLT($$$);
37             sub ProcessSLLT($$$);
38             sub ProcessLucas($$$);
39             sub WriteRIFF($$);
40              
41             # recognized RIFF variants
42             my %riffType = (
43             'WAVE' => 'WAV', 'AVI ' => 'AVI', 'WEBP' => 'WEBP',
44             'LA02' => 'LA', 'LA03' => 'LA', 'LA04' => 'LA',
45             'OFR ' => 'OFR', 'LPAC' => 'PAC', 'wvpk' => 'WV',
46             );
47              
48             # MIME types of recognized RIFF-format files
49             my %riffMimeType = (
50             WAV => 'audio/x-wav',
51             AVI => 'video/x-msvideo',
52             WEBP => 'image/webp',
53             LA => 'audio/x-nspaudio',
54             OFR => 'audio/x-ofr',
55             PAC => 'audio/x-lpac',
56             WV => 'audio/x-wavpack',
57             );
58              
59             # character sets for recognized Windows code pages
60             my %code2charset = (
61             0 => 'Latin',
62             65001 => 'UTF8',
63             1252 => 'Latin',
64             1250 => 'Latin2',
65             1251 => 'Cyrillic',
66             1253 => 'Greek',
67             1254 => 'Turkish',
68             1255 => 'Hebrew',
69             1256 => 'Arabic',
70             1257 => 'Baltic',
71             1258 => 'Vietnam',
72             874 => 'Thai',
73             10000 => 'MacRoman',
74             10029 => 'MacLatin2',
75             10007 => 'MacCyrillic',
76             10006 => 'MacGreek',
77             10081 => 'MacTurkish',
78             10010 => 'MacRomanian',
79             10079 => 'MacIceland',
80             10082 => 'MacCroatian',
81             );
82              
83             %Image::ExifTool::RIFF::audioEncoding = ( #2
84             Notes => 'These "TwoCC" audio encoding codes are used in RIFF and ASF files.',
85             0x01 => 'Microsoft PCM',
86             0x02 => 'Microsoft ADPCM',
87             0x03 => 'Microsoft IEEE float',
88             0x04 => 'Compaq VSELP', #4
89             0x05 => 'IBM CVSD', #4
90             0x06 => 'Microsoft a-Law',
91             0x07 => 'Microsoft u-Law',
92             0x08 => 'Microsoft DTS', #4
93             0x09 => 'DRM', #4
94             0x0a => 'WMA 9 Speech', #9
95             0x0b => 'Microsoft Windows Media RT Voice', #10
96             0x10 => 'OKI-ADPCM',
97             0x11 => 'Intel IMA/DVI-ADPCM',
98             0x12 => 'Videologic Mediaspace ADPCM', #4
99             0x13 => 'Sierra ADPCM', #4
100             0x14 => 'Antex G.723 ADPCM', #4
101             0x15 => 'DSP Solutions DIGISTD',
102             0x16 => 'DSP Solutions DIGIFIX',
103             0x17 => 'Dialoic OKI ADPCM', #6
104             0x18 => 'Media Vision ADPCM', #6
105             0x19 => 'HP CU', #7
106             0x1a => 'HP Dynamic Voice', #10
107             0x20 => 'Yamaha ADPCM', #6
108             0x21 => 'SONARC Speech Compression', #6
109             0x22 => 'DSP Group True Speech', #6
110             0x23 => 'Echo Speech Corp.', #6
111             0x24 => 'Virtual Music Audiofile AF36', #6
112             0x25 => 'Audio Processing Tech.', #6
113             0x26 => 'Virtual Music Audiofile AF10', #6
114             0x27 => 'Aculab Prosody 1612', #7
115             0x28 => 'Merging Tech. LRC', #7
116             0x30 => 'Dolby AC2',
117             0x31 => 'Microsoft GSM610',
118             0x32 => 'MSN Audio', #6
119             0x33 => 'Antex ADPCME', #6
120             0x34 => 'Control Resources VQLPC', #6
121             0x35 => 'DSP Solutions DIGIREAL', #6
122             0x36 => 'DSP Solutions DIGIADPCM', #6
123             0x37 => 'Control Resources CR10', #6
124             0x38 => 'Natural MicroSystems VBX ADPCM', #6
125             0x39 => 'Crystal Semiconductor IMA ADPCM', #6
126             0x3a => 'Echo Speech ECHOSC3', #6
127             0x3b => 'Rockwell ADPCM',
128             0x3c => 'Rockwell DIGITALK',
129             0x3d => 'Xebec Multimedia', #6
130             0x40 => 'Antex G.721 ADPCM',
131             0x41 => 'Antex G.728 CELP',
132             0x42 => 'Microsoft MSG723', #7
133             0x43 => 'IBM AVC ADPCM', #10
134             0x45 => 'ITU-T G.726', #9
135             0x50 => 'Microsoft MPEG',
136             0x51 => 'RT23 or PAC', #7
137             0x52 => 'InSoft RT24', #4
138             0x53 => 'InSoft PAC', #4
139             0x55 => 'MP3',
140             0x59 => 'Cirrus', #7
141             0x60 => 'Cirrus Logic', #6
142             0x61 => 'ESS Tech. PCM', #6
143             0x62 => 'Voxware Inc.', #6
144             0x63 => 'Canopus ATRAC', #6
145             0x64 => 'APICOM G.726 ADPCM',
146             0x65 => 'APICOM G.722 ADPCM',
147             0x66 => 'Microsoft DSAT', #6
148             0x67 => 'Microsoft DSAT DISPLAY', #6
149             0x69 => 'Voxware Byte Aligned', #7
150             0x70 => 'Voxware AC8', #7
151             0x71 => 'Voxware AC10', #7
152             0x72 => 'Voxware AC16', #7
153             0x73 => 'Voxware AC20', #7
154             0x74 => 'Voxware MetaVoice', #7
155             0x75 => 'Voxware MetaSound', #7
156             0x76 => 'Voxware RT29HW', #7
157             0x77 => 'Voxware VR12', #7
158             0x78 => 'Voxware VR18', #7
159             0x79 => 'Voxware TQ40', #7
160             0x7a => 'Voxware SC3', #10
161             0x7b => 'Voxware SC3', #10
162             0x80 => 'Soundsoft', #6
163             0x81 => 'Voxware TQ60', #7
164             0x82 => 'Microsoft MSRT24', #7
165             0x83 => 'AT&T G.729A', #7
166             0x84 => 'Motion Pixels MVI MV12', #7
167             0x85 => 'DataFusion G.726', #7
168             0x86 => 'DataFusion GSM610', #7
169             0x88 => 'Iterated Systems Audio', #7
170             0x89 => 'Onlive', #7
171             0x8a => 'Multitude, Inc. FT SX20', #10
172             0x8b => 'Infocom ITS A/S G.721 ADPCM', #10
173             0x8c => 'Convedia G729', #10
174             0x8d => 'Not specified congruency, Inc.', #10
175             0x91 => 'Siemens SBC24', #7
176             0x92 => 'Sonic Foundry Dolby AC3 APDIF', #7
177             0x93 => 'MediaSonic G.723', #8
178             0x94 => 'Aculab Prosody 8kbps', #8
179             0x97 => 'ZyXEL ADPCM', #7,
180             0x98 => 'Philips LPCBB', #7
181             0x99 => 'Studer Professional Audio Packed', #7
182             0xa0 => 'Malden PhonyTalk', #8
183             0xa1 => 'Racal Recorder GSM', #10
184             0xa2 => 'Racal Recorder G720.a', #10
185             0xa3 => 'Racal G723.1', #10
186             0xa4 => 'Racal Tetra ACELP', #10
187             0xb0 => 'NEC AAC NEC Corporation', #10
188             0xff => 'AAC', #10
189             0x100 => 'Rhetorex ADPCM', #6
190             0x101 => 'IBM u-Law', #3
191             0x102 => 'IBM a-Law', #3
192             0x103 => 'IBM ADPCM', #3
193             0x111 => 'Vivo G.723', #7
194             0x112 => 'Vivo Siren', #7
195             0x120 => 'Philips Speech Processing CELP', #10
196             0x121 => 'Philips Speech Processing GRUNDIG', #10
197             0x123 => 'Digital G.723', #7
198             0x125 => 'Sanyo LD ADPCM', #8
199             0x130 => 'Sipro Lab ACEPLNET', #8
200             0x131 => 'Sipro Lab ACELP4800', #8
201             0x132 => 'Sipro Lab ACELP8V3', #8
202             0x133 => 'Sipro Lab G.729', #8
203             0x134 => 'Sipro Lab G.729A', #8
204             0x135 => 'Sipro Lab Kelvin', #8
205             0x136 => 'VoiceAge AMR', #10
206             0x140 => 'Dictaphone G.726 ADPCM', #8
207             0x150 => 'Qualcomm PureVoice', #8
208             0x151 => 'Qualcomm HalfRate', #8
209             0x155 => 'Ring Zero Systems TUBGSM', #8
210             0x160 => 'Microsoft Audio1', #8
211             0x161 => 'Windows Media Audio V2 V7 V8 V9 / DivX audio (WMA) / Alex AC3 Audio', #10
212             0x162 => 'Windows Media Audio Professional V9', #10
213             0x163 => 'Windows Media Audio Lossless V9', #10
214             0x164 => 'WMA Pro over S/PDIF', #10
215             0x170 => 'UNISYS NAP ADPCM', #10
216             0x171 => 'UNISYS NAP ULAW', #10
217             0x172 => 'UNISYS NAP ALAW', #10
218             0x173 => 'UNISYS NAP 16K', #10
219             0x174 => 'MM SYCOM ACM SYC008 SyCom Technologies', #10
220             0x175 => 'MM SYCOM ACM SYC701 G726L SyCom Technologies', #10
221             0x176 => 'MM SYCOM ACM SYC701 CELP54 SyCom Technologies', #10
222             0x177 => 'MM SYCOM ACM SYC701 CELP68 SyCom Technologies', #10
223             0x178 => 'Knowledge Adventure ADPCM', #10
224             0x180 => 'Fraunhofer IIS MPEG2AAC', #10
225             0x190 => 'Digital Theater Systems DTS DS', #10
226             0x200 => 'Creative Labs ADPCM', #6
227             0x202 => 'Creative Labs FASTSPEECH8', #6
228             0x203 => 'Creative Labs FASTSPEECH10', #6
229             0x210 => 'UHER ADPCM', #8
230             0x215 => 'Ulead DV ACM', #10
231             0x216 => 'Ulead DV ACM', #10
232             0x220 => 'Quarterdeck Corp.', #6
233             0x230 => 'I-Link VC', #8
234             0x240 => 'Aureal Semiconductor Raw Sport', #8
235             0x241 => 'ESST AC3', #10
236             0x250 => 'Interactive Products HSX', #8
237             0x251 => 'Interactive Products RPELP', #8
238             0x260 => 'Consistent CS2', #8
239             0x270 => 'Sony SCX', #8
240             0x271 => 'Sony SCY', #10
241             0x272 => 'Sony ATRAC3', #10
242             0x273 => 'Sony SPC', #10
243             0x280 => 'TELUM Telum Inc.', #10
244             0x281 => 'TELUMIA Telum Inc.', #10
245             0x285 => 'Norcom Voice Systems ADPCM', #10
246             0x300 => 'Fujitsu FM TOWNS SND', #6
247             0x301 => 'Fujitsu (not specified)', #10
248             0x302 => 'Fujitsu (not specified)', #10
249             0x303 => 'Fujitsu (not specified)', #10
250             0x304 => 'Fujitsu (not specified)', #10
251             0x305 => 'Fujitsu (not specified)', #10
252             0x306 => 'Fujitsu (not specified)', #10
253             0x307 => 'Fujitsu (not specified)', #10
254             0x308 => 'Fujitsu (not specified)', #10
255             0x350 => 'Micronas Semiconductors, Inc. Development', #10
256             0x351 => 'Micronas Semiconductors, Inc. CELP833', #10
257             0x400 => 'Brooktree Digital', #6
258             0x401 => 'Intel Music Coder (IMC)', #10
259             0x402 => 'Ligos Indeo Audio', #10
260             0x450 => 'QDesign Music', #8
261             0x500 => 'On2 VP7 On2 Technologies', #10
262             0x501 => 'On2 VP6 On2 Technologies', #10
263             0x680 => 'AT&T VME VMPCM', #7
264             0x681 => 'AT&T TCP', #8
265             0x700 => 'YMPEG Alpha (dummy for MPEG-2 compressor)', #10
266             0x8ae => 'ClearJump LiteWave (lossless)', #10
267             0x1000 => 'Olivetti GSM', #6
268             0x1001 => 'Olivetti ADPCM', #6
269             0x1002 => 'Olivetti CELP', #6
270             0x1003 => 'Olivetti SBC', #6
271             0x1004 => 'Olivetti OPR', #6
272             0x1100 => 'Lernout & Hauspie', #6
273             0x1101 => 'Lernout & Hauspie CELP codec', #10
274             0x1102 => 'Lernout & Hauspie SBC codec', #10
275             0x1103 => 'Lernout & Hauspie SBC codec', #10
276             0x1104 => 'Lernout & Hauspie SBC codec', #10
277             0x1400 => 'Norris Comm. Inc.', #6
278             0x1401 => 'ISIAudio', #7
279             0x1500 => 'AT&T Soundspace Music Compression', #7
280             0x181c => 'VoxWare RT24 speech codec', #10
281             0x181e => 'Lucent elemedia AX24000P Music codec', #10
282             0x1971 => 'Sonic Foundry LOSSLESS', #10
283             0x1979 => 'Innings Telecom Inc. ADPCM', #10
284             0x1c07 => 'Lucent SX8300P speech codec', #10
285             0x1c0c => 'Lucent SX5363S G.723 compliant codec', #10
286             0x1f03 => 'CUseeMe DigiTalk (ex-Rocwell)', #10
287             0x1fc4 => 'NCT Soft ALF2CD ACM', #10
288             0x2000 => 'FAST Multimedia DVM', #7
289             0x2001 => 'Dolby DTS (Digital Theater System)', #10
290             0x2002 => 'RealAudio 1 / 2 14.4', #10
291             0x2003 => 'RealAudio 1 / 2 28.8', #10
292             0x2004 => 'RealAudio G2 / 8 Cook (low bitrate)', #10
293             0x2005 => 'RealAudio 3 / 4 / 5 Music (DNET)', #10
294             0x2006 => 'RealAudio 10 AAC (RAAC)', #10
295             0x2007 => 'RealAudio 10 AAC+ (RACP)', #10
296             0x2500 => 'Reserved range to 0x2600 Microsoft', #10
297             0x3313 => 'makeAVIS (ffvfw fake AVI sound from AviSynth scripts)', #10
298             0x4143 => 'Divio MPEG-4 AAC audio', #10
299             0x4201 => 'Nokia adaptive multirate', #10
300             0x4243 => 'Divio G726 Divio, Inc.', #10
301             0x434c => 'LEAD Speech', #10
302             0x564c => 'LEAD Vorbis', #10
303             0x5756 => 'WavPack Audio', #10
304             0x674f => 'Ogg Vorbis (mode 1)', #10
305             0x6750 => 'Ogg Vorbis (mode 2)', #10
306             0x6751 => 'Ogg Vorbis (mode 3)', #10
307             0x676f => 'Ogg Vorbis (mode 1+)', #10
308             0x6770 => 'Ogg Vorbis (mode 2+)', #10
309             0x6771 => 'Ogg Vorbis (mode 3+)', #10
310             0x7000 => '3COM NBX 3Com Corporation', #10
311             0x706d => 'FAAD AAC', #10
312             0x7a21 => 'GSM-AMR (CBR, no SID)', #10
313             0x7a22 => 'GSM-AMR (VBR, including SID)', #10
314             0xa100 => 'Comverse Infosys Ltd. G723 1', #10
315             0xa101 => 'Comverse Infosys Ltd. AVQSBC', #10
316             0xa102 => 'Comverse Infosys Ltd. OLDSBC', #10
317             0xa103 => 'Symbol Technologies G729A', #10
318             0xa104 => 'VoiceAge AMR WB VoiceAge Corporation', #10
319             0xa105 => 'Ingenient Technologies Inc. G726', #10
320             0xa106 => 'ISO/MPEG-4 advanced audio Coding', #10
321             0xa107 => 'Encore Software Ltd G726', #10
322             0xa109 => 'Speex ACM Codec xiph.org', #10
323             0xdfac => 'DebugMode SonicFoundry Vegas FrameServer ACM Codec', #10
324             0xe708 => 'Unknown -', #10
325             0xf1ac => 'Free Lossless Audio Codec FLAC', #10
326             0xfffe => 'Extensible', #7
327             0xffff => 'Development', #4
328             );
329              
330             # RIFF info
331             %Image::ExifTool::RIFF::Main = (
332             PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
333             NOTES => q{
334             The RIFF container format is used various types of fines including AVI, WAV,
335             WEBP, LA, OFR, PAC and WV. According to the EXIF specification, Meta
336             information is embedded in two types of RIFF C chunks: C and
337             C, and information about the audio content is stored in the C
338             chunk. As well as this information, some video information and proprietary
339             manufacturer-specific information is also extracted.
340              
341             Large AVI videos may be a concatenation of two or more RIFF chunks. For
342             these files, information is extracted from subsequent RIFF chunks as
343             sub-documents, but the Duration is calculated for the full video.
344              
345             ExifTool currently has the ability to write EXIF, XMP and ICC_Profile
346             metadata to WEBP images, but can't yet write to other RIFF-based formats.
347             },
348             # (not 100% sure that the concatenation technique mentioned above is valid - PH)
349             'fmt ' => {
350             Name => 'AudioFormat',
351             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::AudioFormat' },
352             },
353             'bext' => {
354             Name => 'BroadcastExtension',
355             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::BroadcastExt' },
356             },
357             ds64 => { #15
358             Name => 'DataSize64',
359             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::DS64' },
360             },
361             list => 'ListType', #15
362             labl => { #16 (in 'adtl' chunk)
363             Name => 'CuePointLabel',
364             Priority => 0, # (so they are stored in sequence)
365             ValueConv => 'my $str=substr($val,4); $str=~s/\0+$//; unpack("V",$val) . " " . $str',
366             },
367             note => { #16 (in 'adtl' chunk)
368             Name => 'CuePointNote',
369             Priority => 0, # (so they are stored in sequence)
370             ValueConv => 'my $str=substr($val,4); $str=~s/\0+$//; unpack("V",$val) . " " . $str',
371             },
372             ltxt => { #16 (in 'adtl' chunk)
373             Name => 'LabeledText',
374             Notes => 'CuePointID Length Purpose Country Language Dialect Codepage Text',
375             Priority => 0, # (so they are stored in sequence)
376             ValueConv => q{
377             my @a = unpack('VVa4vvvv', $val);
378             $a[2] = "'$a[2]'";
379             my $txt = substr($val, 18);
380             $txt =~ s/\0+$//; # remove null terminator
381             return join(' ', @a, $txt);
382             },
383             },
384             smpl => { #16
385             Name => 'Sampler',
386             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Sampler' },
387             },
388             inst => { #16
389             Name => 'Instrument',
390             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Instrument' },
391             },
392             LIST_INFO => {
393             Name => 'Info',
394             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Info' },
395             },
396             LIST_exif => {
397             Name => 'Exif',
398             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Exif' },
399             },
400             LIST_hdrl => { # AVI header LIST chunk
401             Name => 'Hdrl',
402             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Hdrl' },
403             },
404             LIST_Tdat => { #PH (Adobe CS3 Bridge)
405             Name => 'Tdat',
406             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Tdat' },
407             },
408             LIST_ncdt => { #PH (Nikon metadata)
409             Name => 'NikonData',
410             SubDirectory => {
411             TagTable => 'Image::ExifTool::Nikon::AVI',
412             # define ProcessProc here so we don't need to load RIFF.pm from Nikon.pm
413             ProcessProc => \&Image::ExifTool::RIFF::ProcessChunks,
414             },
415             },
416             LIST_hydt => { #PH (Pentax metadata)
417             Name => 'PentaxData',
418             SubDirectory => {
419             TagTable => 'Image::ExifTool::Pentax::AVI',
420             ProcessProc => \&Image::ExifTool::RIFF::ProcessChunks,
421             },
422             },
423             LIST_pntx => { #Andras Salamon (Q-S1 AVI)
424             Name => 'PentaxData2',
425             SubDirectory => {
426             TagTable => 'Image::ExifTool::Pentax::AVI',
427             ProcessProc => \&Image::ExifTool::RIFF::ProcessChunks,
428             },
429             },
430             LIST_adtl => { #PH (ref 16, forum12387)
431             Name => 'AssociatedDataList',
432             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Main' },
433             },
434             # seen LIST_JUNK
435             JUNK => [
436             {
437             Name => 'OlympusJunk',
438             Condition => '$$valPt =~ /^OLYMDigital Camera/',
439             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::AVI' },
440             },
441             {
442             Name => 'CasioJunk',
443             Condition => '$$valPt =~ /^QVMI/',
444             # Casio stores standard EXIF-format information in AVI videos (EX-S600)
445             SubDirectory => {
446             TagTable => 'Image::ExifTool::Exif::Main',
447             DirName => 'IFD0',
448             Multi => 0, # (IFD1 is not written)
449             Start => 10,
450             ByteOrder => 'BigEndian',
451             },
452             },
453             {
454             Name => 'RicohJunk',
455             # the Ricoh Caplio GX stores sub-chunks in here
456             Condition => '$$valPt =~ /^ucmt/',
457             SubDirectory => {
458             TagTable => 'Image::ExifTool::Ricoh::AVI',
459             ProcessProc => \&Image::ExifTool::RIFF::ProcessChunks,
460             },
461             },
462             {
463             Name => 'PentaxJunk', # (Optio RS1000)
464             Condition => '$$valPt =~ /^IIII\x01\0/',
465             SubDirectory => { TagTable => 'Image::ExifTool::Pentax::Junk' },
466             },
467             {
468             Name => 'PentaxJunk2', # (Optio RZ18)
469             Condition => '$$valPt =~ /^PENTDigital Camera/',
470             SubDirectory => { TagTable => 'Image::ExifTool::Pentax::Junk2' },
471             },
472             {
473             Name => 'LucasJunk', # (Lucas LK-7900 Ace)
474             Condition => '$$valPt =~ /^0G(DA|PS)/',
475             SubDirectory => {
476             TagTable => 'Image::ExifTool::QuickTime::Stream',
477             ProcessProc => \&ProcessLucas,
478             },
479             },
480             {
481             Name => 'TextJunk',
482             # try to interpret unknown junk as an ASCII string
483             RawConv => '$val =~ /^([^\0-\x1f\x7f-\xff]+)\0*$/ ? $1 : undef',
484             }
485             ],
486             _PMX => { #PH (Adobe CS3 Bridge)
487             Name => 'XMP',
488             Notes => 'AVI and WAV files',
489             SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
490             },
491             JUNQ => { #PH (Adobe CS3 Bridge)
492             # old XMP is preserved when metadata is replaced in Bridge
493             Name => 'OldXMP',
494             Binary => 1,
495             },
496             olym => {
497             Name => 'Olym',
498             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::WAV' },
499             },
500             fact => {
501             Name => 'NumberOfSamples',
502             RawConv => 'Get32u(\$val, 0)',
503             },
504             'cue '=> {
505             Name => 'CuePoints',
506             Binary => 1,
507             Notes => q{
508             config_files/cutepointlist.config from full distribution will decode this
509             and generate a list of cue points with labels
510             },
511             },
512             plst => { Name => 'Playlist', Binary => 1 }, #16
513             afsp => { },
514             IDIT => {
515             Name => 'DateTimeOriginal',
516             Description => 'Date/Time Original',
517             Groups => { 2 => 'Time' },
518             ValueConv => 'Image::ExifTool::RIFF::ConvertRIFFDate($val)',
519             PrintConv => '$self->ConvertDateTime($val)',
520             },
521             CSET => {
522             Name => 'CharacterSet',
523             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::CSET' },
524             },
525             # tx_ tags are generated based on the Codec used for the txts stream
526             tx_USER => {
527             Name => 'UserText',
528             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::UserText' },
529             },
530             tx_Unknown => { # (untested)
531             Name => 'Text',
532             Notes => 'streamed text, extracted when the ExtractEmbedded option is used',
533             },
534             'id3 ' => {
535             Name => 'ID3',
536             SubDirectory => { TagTable => 'Image::ExifTool::ID3::Main' },
537             },
538             #
539             # WebP-specific tags
540             #
541             EXIF => [{ # (WebP)
542             Name => 'EXIF',
543             Condition => '$$valPt =~ /^(II\x2a\0|MM\0\x2a)/',
544             Notes => 'WebP files',
545             SubDirectory => {
546             TagTable => 'Image::ExifTool::Exif::Main',
547             ProcessProc => \&Image::ExifTool::ProcessTIFF,
548             },
549             },{ # (WebP) - have also seen with "Exif\0\0" header - PH
550             Name => 'EXIF',
551             Condition => '$$valPt =~ /^Exif\0\0(II\x2a\0|MM\0\x2a)/ and $self->Warn("Improper EXIF header",1)',
552             SubDirectory => {
553             TagTable => 'Image::ExifTool::Exif::Main',
554             ProcessProc => \&Image::ExifTool::ProcessTIFF,
555             Start => 6,
556             },
557             },{
558             Name => 'UnknownEXIF',
559             Binary => 1,
560             }],
561             'XMP ' => { #14 (WebP)
562             Name => 'XMP',
563             Notes => 'WebP files',
564             SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
565             },
566             ICCP => { #14 (WebP)
567             Name => 'ICC_Profile',
568             Notes => 'WebP files',
569             SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
570             },
571             'VP8 ' => { # (WebP lossy)
572             Name => 'VP8Bitstream',
573             Condition => '$$valPt =~ /^...\x9d\x01\x2a/s',
574             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::VP8' },
575             },
576             VP8L => { #14 (WebP lossless)
577             Name => 'VP8L',
578             Condition => '$$valPt =~ /^\x2f/',
579             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::VP8L' },
580             },
581             VP8X => { #14 (WebP extended)
582             Name => 'VP8X',
583             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::VP8X' },
584             },
585             ANIM => { #14 (WebP animation)
586             Name => 'ANIM',
587             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::ANIM' },
588             },
589             ANMF => { #14 (WebP animation frame)
590             Name => 'ANMF',
591             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::ANMF' },
592             },
593             ALPH => { #14 (WebP alpha)
594             Name => 'ALPH',
595             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::ALPH' },
596             },
597             SGLT => { #PH (BikeBro)
598             Name => 'BikeBroAccel',
599             SubDirectory => {
600             TagTable => 'Image::ExifTool::QuickTime::Stream',
601             ProcessProc => \&ProcessSGLT,
602             },
603             },
604             SLLT => { #PH (BikeBro)
605             Name => 'BikeBroGPS',
606             SubDirectory => {
607             TagTable => 'Image::ExifTool::QuickTime::Stream',
608             ProcessProc => \&ProcessSLLT,
609             },
610             },
611             iXML => { #PH
612             SubDirectory => { TagTable => 'Image::ExifTool::XMP::XML' },
613             },
614             aXML => { #PH
615             SubDirectory => { TagTable => 'Image::ExifTool::XMP::XML' },
616             },
617             #
618             # tags found in an AlphaImagingTech AVI video - PH
619             #
620             LIST_INF0 => { # ('0' instead of 'O' -- odd)
621             Name => 'Info',
622             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Info' },
623             },
624             gps0 => {
625             Name => 'GPSTrack',
626             SetGroups => 'RIFF', # (moves "QuickTime" tags to the "RIFF" group)
627             SubDirectory => {
628             TagTable => 'Image::ExifTool::QuickTime::Stream',
629             # (don't use code ref here or get "Prototype mismatch" warning with some Perl versions)
630             ProcessProc => 'Image::ExifTool::QuickTime::Process_gps0',
631             },
632             },
633             gsen => {
634             Name => 'GSensor',
635             SetGroups => 'RIFF', # (moves "QuickTime" tags to the "RIFF" group)
636             SubDirectory => {
637             TagTable => 'Image::ExifTool::QuickTime::Stream',
638             ProcessProc => 'Image::ExifTool::QuickTime::Process_gsen',
639             },
640             },
641             # gpsa - seen hex "01 20 00 00", same as QuickTime
642             # gsea - 16 bytes hex "04 08 02 00 20 02 00 00 1f 03 00 00 01 00 00 00"
643              
644             acid => { # writen by Acidizer
645             Name => 'Acidizer',
646             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Acidizer' },
647             },
648             );
649              
650             # the maker notes used by some digital cameras
651             %Image::ExifTool::RIFF::Junk = (
652             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
653             GROUPS => { 2 => 'Audio' },
654             );
655              
656             # Format and Audio Stream Format chunk data
657             %Image::ExifTool::RIFF::AudioFormat = (
658             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
659             GROUPS => { 2 => 'Audio' },
660             FORMAT => 'int16u',
661             0 => {
662             Name => 'Encoding',
663             PrintHex => 1,
664             PrintConv => \%Image::ExifTool::RIFF::audioEncoding,
665             SeparateTable => 'AudioEncoding',
666             },
667             1 => 'NumChannels',
668             2 => {
669             Name => 'SampleRate',
670             Format => 'int32u',
671             },
672             4 => {
673             Name => 'AvgBytesPerSec',
674             Format => 'int32u',
675             },
676             # uninteresting
677             # 6 => 'BlockAlignment',
678             7 => 'BitsPerSample',
679             );
680              
681             # Broadcast Audio Extension 'bext' information (ref 13)
682             %Image::ExifTool::RIFF::BroadcastExt = (
683             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
684             GROUPS => { 2 => 'Audio' },
685             NOTES => q{
686             Information found in the Broadcast Audio Extension chunk (see
687             L).
688             },
689             0 => {
690             Name => 'Description',
691             Format => 'string[256]',
692             },
693             256 => {
694             Name => 'Originator',
695             Format => 'string[32]',
696             },
697             288 => {
698             Name => 'OriginatorReference',
699             Format => 'string[32]',
700             },
701             320 => {
702             Name => 'DateTimeOriginal',
703             Description => 'Date/Time Original',
704             Groups => { 2 => 'Time' },
705             Format => 'string[18]',
706             ValueConv => '$_=$val; tr/-/:/; s/^(\d{4}:\d{2}:\d{2})/$1 /; $_',
707             PrintConv => '$self->ConvertDateTime($val)',
708             },
709             338 => {
710             Name => 'TimeReference',
711             Notes => 'first sample count since midnight',
712             Format => 'int32u[2]',
713             ValueConv => 'my @v=split(" ",$val); $v[0] + $v[1] * 4294967296',
714             },
715             346 => {
716             Name => 'BWFVersion',
717             Format => 'int16u',
718             },
719             348 => {
720             Name => 'BWF_UMID',
721             Format => 'undef[64]',
722             ValueConv => '$_=unpack("H*",$val); s/0{64}$//; uc $_',
723             },
724             # 412 - int8u[190] - reserved
725             602 => {
726             Name => 'CodingHistory',
727             Format => 'string[$size-602]',
728             },
729             );
730              
731             # 64-bit chunk sizes (ref 15)
732             %Image::ExifTool::RIFF::DS64 = (
733             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
734             GROUPS => { 2 => 'Audio' },
735             FORMAT => 'int64u',
736             NOTES => q{
737             64-bit data sizes for MBWF/RF64 files. See
738             L for the specification.
739             },
740             0 => {
741             Name => 'RIFFSize64',
742             PrintConv => \&Image::ExifTool::ConvertFileSize,
743             },
744             1 => {
745             Name => 'DataSize64',
746             DataMember => 'DataSize64',
747             RawConv => '$$self{DataSize64} = $val',
748             PrintConv => \&Image::ExifTool::ConvertFileSize,
749             },
750             2 => 'NumberOfSamples64',
751             # (after this comes a table of size overrides for chunk
752             # types other than 'data', but since these are currently
753             # very unlikely, support for these is not yet implemented)
754             );
755              
756             # Sampler chunk (ref 16)
757             %Image::ExifTool::RIFF::Sampler = (
758             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
759             GROUPS => { 2 => 'Audio' },
760             FORMAT => 'int32u',
761             0 => 'Manufacturer',
762             1 => 'Product',
763             2 => 'SamplePeriod',
764             3 => 'MIDIUnityNote',
765             4 => 'MIDIPitchFraction',
766             5 => {
767             Name => 'SMPTEFormat',
768             PrintConv => {
769             0 => 'none',
770             24 => '24 fps',
771             25 => '25 fps',
772             29 => '29 fps',
773             30 => '30 fps',
774             },
775             },
776             6 => {
777             Name => 'SMPTEOffset',
778             Notes => 'HH:MM:SS:FF',
779             ValueConv => q{
780             my $str = sprintf('%.8x', $val);
781             $str =~ s/(..)(..)(..)(..)/$1:$2:$3:$4/;
782             return $str;
783             },
784             },
785             7 => 'NumSampleLoops',
786             8 => 'SamplerDataLen',
787             9 => { Name => 'SamplerData', Format => 'undef[$size-40]', Binary => 1 },
788             );
789              
790             # Instrument chunk (ref 16)
791             %Image::ExifTool::RIFF::Instrument = (
792             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
793             GROUPS => { 2 => 'Audio' },
794             FORMAT => 'int8s',
795             0 => 'UnshiftedNote',
796             1 => 'FineTune',
797             2 => 'Gain',
798             3 => 'LowNote',
799             4 => 'HighNote',
800             5 => 'LowVelocity',
801             6 => 'HighVelocity',
802             );
803              
804             # Sub chunks of INFO LIST chunk
805             %Image::ExifTool::RIFF::Info = (
806             PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
807             GROUPS => { 2 => 'Audio' },
808             FORMAT => 'string',
809             NOTES => q{
810             RIFF INFO tags found in AVI video and WAV audio files. Tags which are part
811             of the EXIF 2.3 specification have an underlined Tag Name in the HTML
812             version of this documentation. Other tags are found in AVI files generated
813             by some software.
814             },
815             IARL => 'ArchivalLocation',
816             IART => { Name => 'Artist', Groups => { 2 => 'Author' } },
817             ICMS => 'Commissioned',
818             ICMT => 'Comment',
819             ICOP => { Name => 'Copyright', Groups => { 2 => 'Author' } },
820             ICRD => {
821             Name => 'DateCreated',
822             Groups => { 2 => 'Time' },
823             ValueConv => '$_=$val; s/-/:/g; $_',
824             },
825             ICRP => 'Cropped',
826             IDIM => 'Dimensions',
827             IDPI => 'DotsPerInch',
828             IENG => 'Engineer',
829             IGNR => 'Genre',
830             IKEY => 'Keywords',
831             ILGT => 'Lightness',
832             IMED => 'Medium',
833             INAM => 'Title',
834             ITRK => 'TrackNumber',
835             IPLT => 'NumColors',
836             IPRD => 'Product',
837             ISBJ => 'Subject',
838             ISFT => {
839             Name => 'Software',
840             # remove trailing nulls/spaces and split at first null
841             # (Casio writes "CASIO" in unicode after the first null)
842             ValueConv => '$_=$val; s/(\s*\0)+$//; s/(\s*\0)/, /; s/\0+//g; $_',
843             },
844             ISHP => 'Sharpness',
845             ISRC => 'Source',
846             ISRF => 'SourceForm',
847             ITCH => 'Technician',
848             #
849             # 3rd party tags
850             #
851             # internet movie database (ref 12)
852             ISGN => 'SecondaryGenre',
853             IWRI => 'WrittenBy',
854             IPRO => 'ProducedBy',
855             ICNM => 'Cinematographer',
856             IPDS => 'ProductionDesigner',
857             IEDT => 'EditedBy',
858             ICDS => 'CostumeDesigner',
859             IMUS => 'MusicBy',
860             ISTD => 'ProductionStudio',
861             IDST => 'DistributedBy',
862             ICNT => 'Country',
863             ILNG => 'Language',
864             IRTD => 'Rating',
865             ISTR => 'Starring',
866             # MovieID (ref12)
867             TITL => 'Title',
868             DIRC => 'Directory',
869             YEAR => 'Year',
870             GENR => 'Genre',
871             COMM => 'Comments',
872             LANG => 'Language',
873             AGES => 'Rated',
874             STAR => 'Starring',
875             CODE => 'EncodedBy',
876             PRT1 => 'Part',
877             PRT2 => 'NumberOfParts',
878             # Morgan Multimedia INFO tags (ref 12)
879             IAS1 => 'FirstLanguage',
880             IAS2 => 'SecondLanguage',
881             IAS3 => 'ThirdLanguage',
882             IAS4 => 'FourthLanguage',
883             IAS5 => 'FifthLanguage',
884             IAS6 => 'SixthLanguage',
885             IAS7 => 'SeventhLanguage',
886             IAS8 => 'EighthLanguage',
887             IAS9 => 'NinthLanguage',
888             ICAS => 'DefaultAudioStream',
889             IBSU => 'BaseURL',
890             ILGU => 'LogoURL',
891             ILIU => 'LogoIconURL',
892             IWMU => 'WatermarkURL',
893             IMIU => 'MoreInfoURL',
894             IMBI => 'MoreInfoBannerImage',
895             IMBU => 'MoreInfoBannerURL',
896             IMIT => 'MoreInfoText',
897             # GSpot INFO tags (ref 12)
898             IENC => 'EncodedBy',
899             IRIP => 'RippedBy',
900             # Sound Forge Pro tags
901             DISP => 'SoundSchemeTitle',
902             TLEN => { Name => 'Length', ValueConv => '$val/1000', PrintConv => '"$val s"' },
903             TRCK => 'TrackNumber',
904             TURL => 'URL',
905             TVER => 'Version',
906             LOCA => 'Location',
907             TORG => 'Organization',
908             # Sony Vegas AVI tags, also used by SCLive and Adobe Premier (ref 11)
909             TAPE => {
910             Name => 'TapeName',
911             Groups => { 2 => 'Video' },
912             },
913             TCOD => {
914             Name => 'StartTimecode',
915             # this is the tape time code for the start of the video
916             Groups => { 2 => 'Video' },
917             ValueConv => '$val * 1e-7',
918             PrintConv => \&ConvertTimecode,
919             },
920             TCDO => {
921             Name => 'EndTimecode',
922             Groups => { 2 => 'Video' },
923             ValueConv => '$val * 1e-7',
924             PrintConv => \&ConvertTimecode,
925             },
926             VMAJ => {
927             Name => 'VegasVersionMajor',
928             Groups => { 2 => 'Video' },
929             },
930             VMIN => {
931             Name => 'VegasVersionMinor',
932             Groups => { 2 => 'Video' },
933             },
934             CMNT => {
935             Name => 'Comment',
936             Groups => { 2 => 'Video' },
937             },
938             RATE => {
939             Name => 'Rate', #? (video? units?)
940             Groups => { 2 => 'Video' },
941             },
942             STAT => {
943             Name => 'Statistics',
944             Groups => { 2 => 'Video' },
945             # ("7318 0 3.430307 1", "0 0 3500.000000 1", "7 0 3.433228 1")
946             PrintConv => [
947             '"$val frames captured"',
948             '"$val dropped"',
949             '"Data rate $val"',
950             { 0 => 'Bad', 1 => 'OK' }, # capture OK?
951             ],
952             },
953             DTIM => {
954             Name => 'DateTimeOriginal',
955             Description => 'Date/Time Original',
956             Groups => { 2 => 'Time' },
957             ValueConv => q{
958             my @v = split ' ', $val;
959             return undef unless @v == 2;
960             # the Kodak EASYSHARE Sport stores this incorrectly as a string:
961             return $val if $val =~ /^\d{4}:\d{2}:\d{2} \d{2}:\d{2}:\d{2}$/;
962             # get time in seconds
963             $val = 1e-7 * ($v[0] * 4294967296 + $v[1]);
964             # shift from Jan 1, 1601 to Jan 1, 1970
965             $val -= 134774 * 24 * 3600 if $val != 0;
966             return Image::ExifTool::ConvertUnixTime($val);
967             },
968             PrintConv => '$self->ConvertDateTime($val)',
969             },
970             # not observed, but apparently part of the standard:
971             IDIT => {
972             Name => 'DateTimeOriginal',
973             Description => 'Date/Time Original',
974             Groups => { 2 => 'Time' },
975             ValueConv => 'Image::ExifTool::RIFF::ConvertRIFFDate($val)',
976             PrintConv => '$self->ConvertDateTime($val)',
977             },
978             ISMP => 'TimeCode',
979             );
980              
981             # Sub chunks of EXIF LIST chunk
982             %Image::ExifTool::RIFF::Exif = (
983             PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
984             GROUPS => { 2 => 'Audio' },
985             NOTES => 'These tags are part of the EXIF 2.3 specification for WAV audio files.',
986             ever => 'ExifVersion',
987             erel => 'RelatedImageFile',
988             etim => { Name => 'TimeCreated', Groups => { 2 => 'Time' } },
989             ecor => { Name => 'Make', Groups => { 2 => 'Camera' } },
990             emdl => { Name => 'Model', Groups => { 2 => 'Camera' }, Description => 'Camera Model Name' },
991             emnt => { Name => 'MakerNotes', Binary => 1 },
992             eucm => {
993             Name => 'UserComment',
994             PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val,"RIFF:UserComment")',
995             },
996             );
997              
998             # Sub chunks of hdrl LIST chunk
999             %Image::ExifTool::RIFF::Hdrl = (
1000             PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
1001             GROUPS => { 2 => 'Image' },
1002             avih => {
1003             Name => 'AVIHeader',
1004             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::AVIHeader' },
1005             },
1006             IDIT => {
1007             Name => 'DateTimeOriginal',
1008             Description => 'Date/Time Original',
1009             Groups => { 2 => 'Time' },
1010             ValueConv => 'Image::ExifTool::RIFF::ConvertRIFFDate($val)',
1011             PrintConv => '$self->ConvertDateTime($val)',
1012             },
1013             ISMP => 'TimeCode',
1014             LIST_strl => {
1015             Name => 'Stream',
1016             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Stream' },
1017             },
1018             LIST_odml => {
1019             Name => 'OpenDML',
1020             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::OpenDML' },
1021             },
1022             );
1023              
1024             # Sub chunks of Tdat LIST chunk (ref PH)
1025             %Image::ExifTool::RIFF::Tdat = (
1026             PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
1027             GROUPS => { 2 => 'Video' },
1028             # (have seen tc_O, tc_A, rn_O and rn_A)
1029             );
1030              
1031             # RIFF character set chunk
1032             %Image::ExifTool::RIFF::CSET = (
1033             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1034             GROUPS => { 2 => 'Other' },
1035             FORMAT => 'int16u',
1036             0 => {
1037             Name => 'CodePage',
1038             RawConv => '$$self{CodePage} = $val',
1039             },
1040             1 => 'CountryCode',
1041             2 => 'LanguageCode',
1042             3 => 'Dialect',
1043             );
1044              
1045             %Image::ExifTool::RIFF::AVIHeader = (
1046             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1047             GROUPS => { 2 => 'Video' },
1048             FORMAT => 'int32u',
1049             FIRST_ENTRY => 0,
1050             0 => {
1051             Name => 'FrameRate',
1052             # (must use RawConv because raw value used in Composite tag)
1053             RawConv => '$val ? 1e6 / $val : undef',
1054             PrintConv => 'int($val * 1000 + 0.5) / 1000',
1055             },
1056             1 => {
1057             Name => 'MaxDataRate',
1058             PrintConv => 'sprintf("%.4g kB/s",$val / 1024)',
1059             },
1060             # 2 => 'PaddingGranularity',
1061             # 3 => 'Flags',
1062             4 => 'FrameCount',
1063             # 5 => 'InitialFrames',
1064             6 => 'StreamCount',
1065             # 7 => 'SuggestedBufferSize',
1066             8 => 'ImageWidth',
1067             9 => 'ImageHeight',
1068             );
1069              
1070             %Image::ExifTool::RIFF::Stream = (
1071             PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
1072             GROUPS => { 2 => 'Image' },
1073             strh => {
1074             Name => 'StreamHeader',
1075             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::StreamHeader' },
1076             },
1077             strn => 'StreamName',
1078             strd => { #PH
1079             Name => 'StreamData',
1080             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::StreamData' },
1081             },
1082             strf => [
1083             {
1084             Name => 'AudioFormat',
1085             Condition => '$$self{RIFFStreamType} eq "auds"',
1086             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::AudioFormat' },
1087             },
1088             {
1089             Name => 'VideoFormat',
1090             Condition => '$$self{RIFFStreamType} eq "vids"',
1091             SubDirectory => { TagTable => 'Image::ExifTool::BMP::Main' },
1092             },
1093             {
1094             Name => 'TextFormat',
1095             Condition => '$$self{RIFFStreamType} eq "txts"',
1096             Hidden => 1,
1097             RawConv => '$self->Options("ExtractEmbedded") or $self->WarnOnce("Use ExtractEmbedded option to extract timed text",3); undef',
1098             },
1099             ],
1100             );
1101              
1102             # Open DML tags (ref http://www.morgan-multimedia.com/download/odmlff2.pdf)
1103             %Image::ExifTool::RIFF::OpenDML = (
1104             PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
1105             GROUPS => { 2 => 'Video' },
1106             dmlh => {
1107             Name => 'ExtendedAVIHeader',
1108             SubDirectory => { TagTable => 'Image::ExifTool::RIFF::ExtAVIHdr' },
1109             },
1110             );
1111              
1112             # Extended AVI Header tags (ref http://www.morgan-multimedia.com/download/odmlff2.pdf)
1113             %Image::ExifTool::RIFF::ExtAVIHdr = (
1114             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1115             GROUPS => { 2 => 'Video' },
1116             FORMAT => 'int32u',
1117             0 => 'TotalFrameCount',
1118             );
1119              
1120             %Image::ExifTool::RIFF::StreamHeader = (
1121             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1122             GROUPS => { 2 => 'Video' },
1123             FORMAT => 'int32u',
1124             FIRST_ENTRY => 0,
1125             PRIORITY => 0, # so we get values from the first stream
1126             0 => {
1127             Name => 'StreamType',
1128             Format => 'string[4]',
1129             RawConv => '$$self{RIFFStreamNum} = ($$self{RIFFStreamNum} || 0) + 1; $$self{RIFFStreamType} = $val',
1130             PrintConv => {
1131             auds => 'Audio',
1132             mids => 'MIDI',
1133             txts => 'Text',
1134             vids => 'Video',
1135             iavs => 'Interleaved Audio+Video',
1136             },
1137             },
1138             1 => [
1139             {
1140             Name => 'AudioCodec',
1141             Condition => '$$self{RIFFStreamType} eq "auds"',
1142             RawConv => '$$self{RIFFStreamCodec}[$$self{RIFFStreamNum}-1] = $val',
1143             Format => 'string[4]',
1144             },
1145             {
1146             Name => 'VideoCodec',
1147             Condition => '$$self{RIFFStreamType} eq "vids"',
1148             RawConv => '$$self{RIFFStreamCodec}[$$self{RIFFStreamNum}-1] = $val',
1149             Format => 'string[4]',
1150             },
1151             {
1152             Name => 'Codec',
1153             Format => 'string[4]',
1154             RawConv => '$$self{RIFFStreamCodec}[$$self{RIFFStreamNum}-1] = $val',
1155             },
1156             ],
1157             # 2 => 'StreamFlags',
1158             # 3 => 'StreamPriority',
1159             # 3.5 => 'Language',
1160             # 4 => 'InitialFrames',
1161             5 => [
1162             {
1163             Name => 'AudioSampleRate',
1164             Condition => '$$self{RIFFStreamType} eq "auds"',
1165             Format => 'rational64u',
1166             ValueConv => '$val ? 1/$val : 0',
1167             PrintConv => 'int($val * 100 + 0.5) / 100',
1168             },
1169             {
1170             Name => 'VideoFrameRate',
1171             Condition => '$$self{RIFFStreamType} eq "vids"',
1172             Format => 'rational64u',
1173             # (must use RawConv because raw value used in Composite tag)
1174             RawConv => '$val ? 1/$val : undef',
1175             PrintConv => 'int($val * 1000 + 0.5) / 1000',
1176             },
1177             {
1178             Name => 'StreamSampleRate',
1179             Format => 'rational64u',
1180             ValueConv => '$val ? 1/$val : 0',
1181             PrintConv => 'int($val * 1000 + 0.5) / 1000',
1182             },
1183             ],
1184             # 7 => 'Start',
1185             8 => [
1186             {
1187             Name => 'AudioSampleCount',
1188             Condition => '$$self{RIFFStreamType} eq "auds"',
1189             },
1190             {
1191             Name => 'VideoFrameCount',
1192             Condition => '$$self{RIFFStreamType} eq "vids"',
1193             },
1194             {
1195             Name => 'StreamSampleCount',
1196             },
1197             ],
1198             # 9 => 'SuggestedBufferSize',
1199             10 => {
1200             Name => 'Quality',
1201             PrintConv => '$val eq 0xffffffff ? "Default" : $val',
1202             },
1203             11 => {
1204             Name => 'SampleSize',
1205             PrintConv => '$val ? "$val byte" . ($val==1 ? "" : "s") : "Variable"',
1206             },
1207             # 12 => { Name => 'Frame', Format => 'int16u[4]' },
1208             );
1209              
1210             %Image::ExifTool::RIFF::StreamData = ( #PH
1211             PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessStreamData,
1212             GROUPS => { 2 => 'Video' },
1213             NOTES => q{
1214             This chunk is used to store proprietary information in AVI videos from some
1215             cameras. The first 4 characters of the data are used as the Tag ID below.
1216             },
1217             AVIF => {
1218             Name => 'AVIF',
1219             SubDirectory => {
1220             TagTable => 'Image::ExifTool::Exif::Main',
1221             DirName => 'IFD0',
1222             Start => 8,
1223             ByteOrder => 'LittleEndian',
1224             },
1225             },
1226             CASI => { # (used by Casio GV-10)
1227             Name => 'CasioData',
1228             SubDirectory => { TagTable => 'Image::ExifTool::Casio::AVI' },
1229             },
1230             Zora => 'VendorName', # (Samsung PL90 AVI files)
1231             unknown => {
1232             Name => 'UnknownData',
1233             # try to interpret unknown stream data as a string
1234             RawConv => '$_=$val; /^[^\0-\x1f\x7f-\xff]+$/ ? $_ : undef',
1235             },
1236             );
1237              
1238             # VP8 bitstream (ref http://www.rfc-editor.org/rfc/pdfrfc/rfc6386.txt.pdf)
1239             %Image::ExifTool::RIFF::VP8 = (
1240             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1241             GROUPS => { 2 => 'Image' },
1242             NOTES => q{
1243             This chunk is found in simple-format (lossy) WebP files. See
1244             L for the WebP
1245             container specification.
1246             },
1247             0 => {
1248             Name => 'VP8Version',
1249             Mask => 0x0e,
1250             PrintConv => {
1251             0 => '0 (bicubic reconstruction, normal loop)',
1252             1 => '1 (bilinear reconstruction, simple loop)',
1253             2 => '2 (bilinear reconstruction, no loop)',
1254             3 => '3 (no reconstruction, no loop)',
1255             },
1256             },
1257             6 => {
1258             Name => 'ImageWidth',
1259             Format => 'int16u',
1260             Mask => 0x3fff,
1261             Priority => 0,
1262             },
1263             6.1 => {
1264             Name => 'HorizontalScale',
1265             Format => 'int16u',
1266             Mask => 0xc000,
1267             },
1268             8 => {
1269             Name => 'ImageHeight',
1270             Format => 'int16u',
1271             Mask => 0x3fff,
1272             Priority => 0,
1273             },
1274             8.1 => {
1275             Name => 'VerticalScale',
1276             Format => 'int16u',
1277             Mask => 0xc000,
1278             },
1279             );
1280              
1281             # WebP lossless info (ref 14)
1282             %Image::ExifTool::RIFF::VP8L = (
1283             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1284             NOTES => 'This chunk is found in lossless WebP files.',
1285             GROUPS => { 2 => 'Image' },
1286             1 => {
1287             Name => 'ImageWidth',
1288             Format => 'int16u',
1289             Priority => 0,
1290             ValueConv => '($val & 0x3fff) + 1',
1291             },
1292             2 => {
1293             Name => 'ImageHeight',
1294             Format => 'int32u',
1295             Priority => 0,
1296             ValueConv => '(($val >> 6) & 0x3fff) + 1',
1297             },
1298             );
1299              
1300             # WebP extended info (ref 14)
1301             %Image::ExifTool::RIFF::VP8X = (
1302             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1303             GROUPS => { 2 => 'Image' },
1304             NOTES => 'This chunk is found in extended WebP files.',
1305             # 0 - bitmask: 2=ICC, 3=alpha, 4=EXIF, 5=XMP, 6=animation
1306             0 => {
1307             Name => 'WebP_Flags',
1308             Description => 'WebP Flags',
1309             Notes => 'flags used in Extended WebP images',
1310             Format => 'int32u',
1311             PrintConv => { BITMASK => {
1312             1 => 'Animation',
1313             2 => 'XMP',
1314             3 => 'EXIF',
1315             4 => 'Alpha',
1316             5 => 'ICC Profile',
1317             }},
1318             },
1319             4 => {
1320             Name => 'ImageWidth',
1321             Format => 'int32u',
1322             ValueConv => '($val & 0xffffff) + 1',
1323             },
1324             6 => {
1325             Name => 'ImageHeight',
1326             Format => 'int32u',
1327             ValueConv => '($val >> 8) + 1',
1328             },
1329             );
1330              
1331             # WebP animation info (ref 14)
1332             %Image::ExifTool::RIFF::ANIM = (
1333             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1334             GROUPS => { 2 => 'Image' },
1335             NOTES => 'WebP animation chunk.',
1336             0 => {
1337             Name => 'BackgroundColor',
1338             Format => 'int8u[4]',
1339             },
1340             4 => {
1341             Name => 'AnimationLoopCount',
1342             PrintConv => '$val || "inf"',
1343             },
1344             );
1345              
1346             # WebP animation frame info (ref 14)
1347             %Image::ExifTool::RIFF::ANMF = (
1348             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1349             GROUPS => { 2 => 'Image' },
1350             NOTES => 'WebP animation frame chunk.',
1351             12 => {
1352             Name => 'Duration',
1353             Format => 'int32u',
1354             Notes => 'extracted as the sum of durations of all animation frames',
1355             RawConv => q{
1356             if (defined $$self{VALUE}{Duration}) {
1357             $$self{VALUE}{Duration} += $val & 0x0fff;
1358             return undef;
1359             }
1360             return $val & 0x0fff;
1361             },
1362             ValueConv => '$val / 1000',
1363             PrintConv => 'ConvertDuration($val)',
1364             },
1365             );
1366              
1367             # streamed USER txts written by Momento M6 dashcam (ref PH)
1368             %Image::ExifTool::RIFF::UserText = (
1369             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1370             GROUPS => { 2 => 'Location' },
1371             NOTES => q{
1372             Tags decoded from the USER-format txts stream written by Momento M6 dashcam.
1373             Extracted only if the ExtractEmbedded option is used.
1374             },
1375             # (little-endian)
1376             # 0 - int32u: 32
1377             # 4 - int32u: sample number (starting from unknown offset)
1378             # 8 - int8u[4]: "w x y z" ? (w 0=front cam, 1=rear cam, z mostly 5-8)
1379             # 12 - int8u[4]: "0 x 1 0" ? (x incrementing once per second)
1380             # 16 - int8u[4]: "0 32 0 x" ?
1381             # 20 - int32u: 100-150(mostly), 250-300(once per second)
1382             # 24 - int8u[4]: "0 x y 0" ?
1383             28 => { Name => 'GPSAltitude', Format => 'int32u', ValueConv => '$val / 10' }, # (NC)
1384             # 32 - int32u: 0(mostly), 23(once per second)
1385             # 36 - int32u: 0
1386             40 => { Name => 'Accelerometer', Format => 'float[3]' },
1387             # 52 - int32u: 1
1388             56 => { Name => 'GPSSpeed', Format => 'float' }, # km/h
1389             60 => {
1390             Name => 'GPSLatitude',
1391             Format => 'float',
1392             # Note: these values are unsigned and I don't know where the hemisphere is stored,
1393             # but my only sample is from the U.S., so assume a positive latitude (for now)
1394             ValueConv => 'my $deg = int($val / 100); $deg + ($val - $deg * 100) / 60',
1395             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
1396             },
1397             64 => {
1398             Name => 'GPSLongitude',
1399             Format => 'float',
1400             # Note: these values are unsigned and I don't know where the hemisphere is stored,
1401             # but my only sample is from the U.S., so assume a negative longitude (for now)
1402             ValueConv => 'my $deg = int($val / 100); -($deg + ($val - $deg * 100) / 60)',
1403             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
1404             },
1405             68 => {
1406             Name => 'GPSDateTime',
1407             Description => 'GPS Date/Time',
1408             Groups => { 2 => 'Time' },
1409             Format => 'int32u',
1410             ValueConv => 'ConvertUnixTime($val)',
1411             # (likely local time, but clock seemed off by 3 hours in my sample)
1412             PrintConv => '$self->ConvertDateTime($val)',
1413             },
1414             );
1415              
1416             # WebP alpha info (ref 14)
1417             %Image::ExifTool::RIFF::ALPH = (
1418             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1419             GROUPS => { 2 => 'Image' },
1420             NOTES => 'WebP alpha chunk.',
1421             0 => {
1422             Name => 'AlphaPreprocessing',
1423             Mask => 0x03,
1424             PrintConv => {
1425             0 => 'none',
1426             1 => 'Level Reduction',
1427             },
1428             },
1429             0.1 => {
1430             Name => 'AlphaFiltering',
1431             Mask => 0x03,
1432             PrintConv => {
1433             0 => 'none',
1434             1 => 'Horizontal',
1435             2 => 'Vertical',
1436             3 => 'Gradient',
1437             },
1438             },
1439             0.2 => {
1440             Name => 'AlphaCompression',
1441             Mask => 0x03,
1442             PrintConv => {
1443             0 => 'none',
1444             1 => 'Lossless',
1445             },
1446             },
1447             );
1448              
1449             # Acidizer information (ref https://forums.cockos.com/showthread.php?t=227118)
1450             %Image::ExifTool::RIFF::Acidizer = (
1451             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1452             GROUPS => { 2 => 'Audio' },
1453             0 => {
1454             Name => 'AcidizerFlags',
1455             Format => 'int32u',
1456             PrintConv => { BITMASK => {
1457             0 => 'One shot',
1458             1 => 'Root note set',
1459             2 => 'Stretch',
1460             3 => 'Disk-based',
1461             4 => 'High octave',
1462             }},
1463             },
1464             4 => {
1465             Name => 'RootNote',
1466             Format => 'int16u',
1467             PrintConv => {
1468             0x30 => 'C', 0x3c => 'High C',
1469             0x31 => 'C#', 0x3d => 'High C#',
1470             0x32 => 'D', 0x3e => 'High D',
1471             0x33 => 'D#', 0x3f => 'High D#',
1472             0x34 => 'E', 0x40 => 'High E',
1473             0x35 => 'F', 0x41 => 'High F',
1474             0x36 => 'F#', 0x42 => 'High F#',
1475             0x37 => 'G', 0x43 => 'High G',
1476             0x38 => 'G#', 0x44 => 'High G#',
1477             0x39 => 'A', 0x45 => 'High A',
1478             0x3a => 'A#', 0x46 => 'High A#',
1479             0x3b => 'B', 0x47 => 'High B',
1480             },
1481             },
1482             12 => {
1483             Name => 'Beats',
1484             Format => 'int32u',
1485             },
1486             16 => {
1487             Name => 'Meter',
1488             Format => 'int16u[2]',
1489             PrintConv => '$val =~ s/(\d+) (\d+)/$2\/$1/; $val', # denominator comes first, so swap them
1490             },
1491             20 => {
1492             Name => 'Tempo',
1493             Format => 'float',
1494             },
1495             );
1496              
1497             # RIFF composite tags
1498             %Image::ExifTool::RIFF::Composite = (
1499             Duration => {
1500             Require => {
1501             0 => 'RIFF:FrameRate',
1502             1 => 'RIFF:FrameCount',
1503             },
1504             Desire => {
1505             2 => 'VideoFrameRate',
1506             3 => 'VideoFrameCount',
1507             },
1508             RawConv => 'Image::ExifTool::RIFF::CalcDuration($self, @val)',
1509             PrintConv => 'ConvertDuration($val)',
1510             },
1511             Duration2 => {
1512             Name => 'Duration',
1513             Require => {
1514             0 => 'RIFF:AvgBytesPerSec',
1515             1 => 'FileSize',
1516             },
1517             Desire => {
1518             # check FrameCount because this calculation only applies
1519             # to audio-only files (eg. WAV)
1520             2 => 'FrameCount',
1521             3 => 'VideoFrameCount',
1522             },
1523             # (can't calculate duration like this for compressed audio types)
1524             RawConv => q{
1525             return undef if $$self{VALUE}{FileType} =~ /^(LA|OFR|PAC|WV)$/;
1526             return(($val[0] and not ($val[2] or $val[3])) ? $val[1] / $val[0] : undef);
1527             },
1528             PrintConv => 'ConvertDuration($val)',
1529             },
1530             );
1531              
1532             # add our composite tags
1533             Image::ExifTool::AddCompositeTags('Image::ExifTool::RIFF');
1534              
1535              
1536             #------------------------------------------------------------------------------
1537             # AutoLoad our writer routines when necessary
1538             #
1539             sub AUTOLOAD
1540             {
1541 1     1   7 return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
1542             }
1543              
1544             #------------------------------------------------------------------------------
1545             # Convert RIFF date to EXIF format
1546             my %monthNum = (
1547             Jan=>1, Feb=>2, Mar=>3, Apr=>4, May=>5, Jun=>6,
1548             Jul=>7, Aug=>8, Sep=>9, Oct=>10,Nov=>11,Dec=>12
1549             );
1550             sub ConvertRIFFDate($)
1551             {
1552 2     2 0 8 my $val = shift;
1553 2         14 my @part = split ' ', $val;
1554 2         7 my $mon;
1555 2 100 66     26 if (@part >= 5 and $mon = $monthNum{ucfirst(lc($part[1]))}) {
    50          
    0          
1556             # the standard AVI date format (eg. "Mon Mar 10 15:04:43 2003")
1557 1         9 $val = sprintf("%.4d:%.2d:%.2d %s", $part[4],
1558             $mon, $part[2], $part[3]);
1559             } elsif ($val =~ m{(\d{4})/\s*(\d+)/\s*(\d+)/?\s+(\d+):\s*(\d+)\s*(P?)}) {
1560             # but the Casio QV-3EX writes dates like "2001/ 1/27 1:42PM",
1561             # and the Casio EX-Z30 writes "2005/11/28/ 09:19"... doh!
1562 1 50       16 $val = sprintf("%.4d:%.2d:%.2d %.2d:%.2d:00",$1,$2,$3,$4+($6?12:0),$5);
1563             } elsif ($val =~ m{(\d{4})[-/](\d+)[-/](\d+)\s+(\d+:\d+:\d+)}) {
1564             # the Konica KD500Z writes "2002-12-16 15:35:01\0\0"
1565 0         0 $val = "$1:$2:$3 $4";
1566             }
1567 2         16 return $val;
1568             }
1569              
1570             #------------------------------------------------------------------------------
1571             # Print time
1572             # Inputs: 0) time in seconds
1573             # Returns: time string
1574             sub ConvertTimecode($)
1575             {
1576 0     0 0 0 my $val = shift;
1577 0         0 my $hr = int($val / 3600);
1578 0         0 $val -= $hr * 3600;
1579 0         0 my $min = int($val / 60);
1580 0         0 $val -= $min * 60;
1581 0         0 my $ss = sprintf('%05.2f', $val);
1582 0 0       0 if ($ss >= 60) { # handle round-off problems
1583 0         0 $ss = '00.00';
1584 0 0       0 ++$min >= 60 and $min -= 60, ++$hr;
1585             }
1586 0         0 return sprintf('%d:%.2d:%s', $hr, $min, $ss);
1587             }
1588              
1589             #------------------------------------------------------------------------------
1590             # Calculate duration of RIFF
1591             # Inputs: 0) ExifTool ref, 1/2) RIFF:FrameRate/Count, 2/3) VideoFrameRate/Count
1592             # Returns: Duration in seconds or undef
1593             # Notes: Sums duration of all sub-documents (concatenated AVI files)
1594             sub CalcDuration($@)
1595             {
1596 2     2 0 12 my ($et, @val) = @_;
1597 2         6 my $totalDuration = 0;
1598 2         5 my $subDoc = 0;
1599 2         14 my @keyList;
1600 2         6 for (;;) {
1601             # this is annoying. Apparently (although I couldn't verify this), FrameCount
1602             # in the RIFF header includes multiple video tracks if they exist (eg. with the
1603             # FujiFilm REAL 3D AVI's), but the video stream information isn't reliable for
1604             # some cameras (eg. Olympus FE models), so use the video stream information
1605             # only if the RIFF header duration is 2 to 3 times longer
1606 2         3 my $dur1;
1607 2 50       13 $dur1 = $val[1] / $val[0] if $val[0];
1608 2 50 33     14 if ($val[2] and $val[3]) {
1609 2         6 my $dur2 = $val[3] / $val[2];
1610 2         6 my $rat = $dur1 / $dur2;
1611 2 50 33     11 $dur1 = $dur2 if $rat > 1.9 and $rat < 3.1;
1612             }
1613 2 50       8 $totalDuration += $dur1 if defined $dur1;
1614 2 50       12 last unless $subDoc++ < $$et{DOC_COUNT};
1615             # get tag values for next sub-document
1616 0         0 my @tags = qw(FrameRate FrameCount VideoFrameRate VideoFrameCount);
1617 0         0 my $rawValue = $$et{VALUE};
1618 0         0 my ($i, $j, $key, $keys);
1619 0         0 for ($i=0; $i<@tags; ++$i) {
1620 0 0       0 if ($subDoc == 1) {
1621             # generate list of available keys for each tag
1622 0         0 $keys = $keyList[$i] = [ ];
1623 0         0 for ($j=0; ; ++$j) {
1624 0         0 $key = $tags[$i];
1625 0 0       0 $key .= " ($j)" if $j;
1626 0 0       0 last unless defined $$rawValue{$key};
1627 0         0 push @$keys, $key;
1628             }
1629             } else {
1630 0         0 $keys = $keyList[$i];
1631             }
1632             # find key for tag in this sub-document
1633 0         0 my $grp = "Doc$subDoc";
1634 0 0       0 $grp .= ":RIFF" if $i < 2; # (tags 0 and 1 also in RIFF group)
1635 0         0 $key = $et->GroupMatches($grp, $keys);
1636 0 0       0 $val[$i] = $key ? $$rawValue{$key} : undef;
1637             }
1638 0 0 0     0 last unless defined $val[0] and defined $val[1]; # (Require'd tags)
1639             }
1640 2         23 return $totalDuration;
1641             }
1642              
1643             #------------------------------------------------------------------------------
1644             # Process stream data
1645             # Inputs: 0) ExifTool object ref, 1) dirInfo reference, 2) tag table ref
1646             # Returns: 1 on success
1647             sub ProcessStreamData($$$)
1648             {
1649 0     0 0 0 my ($et, $dirInfo, $tagTbl) = @_;
1650 0         0 my $dataPt = $$dirInfo{DataPt};
1651 0         0 my $start = $$dirInfo{DirStart};
1652 0         0 my $size = $$dirInfo{DirLen};
1653 0 0       0 return 0 if $size < 4;
1654 0 0       0 if ($et->Options('Verbose')) {
1655 0         0 $et->VerboseDir($$dirInfo{DirName}, 0, $size);
1656             }
1657 0         0 my $tag = substr($$dataPt, $start, 4);
1658 0         0 my $tagInfo = $et->GetTagInfo($tagTbl, $tag);
1659 0 0       0 unless ($tagInfo) {
1660 0         0 $tagInfo = $et->GetTagInfo($tagTbl, 'unknown');
1661 0 0       0 return 1 unless $tagInfo;
1662             }
1663 0         0 my $subdir = $$tagInfo{SubDirectory};
1664 0 0       0 if ($$tagInfo{SubDirectory}) {
1665 0   0     0 my $offset = $$subdir{Start} || 0;
1666 0         0 my $baseShift = $$dirInfo{DataPos} + $$dirInfo{DirStart} + $offset;
1667             my %subdirInfo = (
1668             DataPt => $dataPt,
1669             DataPos => $$dirInfo{DataPos} - $baseShift,
1670             Base => ($$dirInfo{Base} || 0) + $baseShift,
1671             DataLen => $$dirInfo{DataLen},
1672             DirStart=> $$dirInfo{DirStart} + $offset,
1673             DirLen => $$dirInfo{DirLen} - $offset,
1674             DirName => $$subdir{DirName},
1675             Parent => $$dirInfo{DirName},
1676 0   0     0 );
1677 0 0       0 unless ($offset) {
1678             # allow processing of 2nd directory at the same address
1679 0         0 my $addr = $subdirInfo{DirStart} + $subdirInfo{DataPos} + $subdirInfo{Base};
1680 0         0 delete $$et{PROCESSED}{$addr}
1681             }
1682             # (we could set FIRST_EXIF_POS to $subdirInfo{Base} here to make
1683             # htmlDump offsets relative to EXIF base if we wanted...)
1684 0         0 my $subTable = GetTagTable($$subdir{TagTable});
1685 0         0 $et->ProcessDirectory(\%subdirInfo, $subTable);
1686             } else {
1687             $et->HandleTag($tagTbl, $tag, undef,
1688             DataPt => $dataPt,
1689             DataPos => $$dirInfo{DataPos},
1690 0         0 Start => $start,
1691             Size => $size,
1692             TagInfo => $tagInfo,
1693             );
1694             }
1695 0         0 return 1;
1696             }
1697              
1698             #------------------------------------------------------------------------------
1699             # Make tag information hash for unknown tag
1700             # Inputs: 0) Tag table ref, 1) tag ID
1701             sub MakeTagInfo($$)
1702             {
1703 0     0 0 0 my ($tagTbl, $tag) = @_;
1704 0         0 my $name = $tag;
1705 0         0 my $n = ($name =~ s/([\x00-\x1f\x7f-\xff])/'x'.unpack('H*',$1)/eg);
  0         0  
1706             # print in hex if tag is numerical
1707 0 0       0 $name = sprintf('0x%.4x',unpack('N',$tag)) if $n > 2;
1708 0         0 AddTagToTable($tagTbl, $tag, {
1709             Name => "Unknown_$name",
1710             Description => "Unknown $name",
1711             Unknown => 1,
1712             Binary => 1,
1713             });
1714             }
1715              
1716             #------------------------------------------------------------------------------
1717             # Process RIFF chunks
1718             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
1719             # Returns: 1 on success
1720             sub ProcessChunks($$$)
1721             {
1722 13     13 0 31 my ($et, $dirInfo, $tagTbl) = @_;
1723 13         28 my $dataPt = $$dirInfo{DataPt};
1724 13         23 my $start = $$dirInfo{DirStart};
1725 13         26 my $size = $$dirInfo{DirLen};
1726 13         22 my $end = $start + $size;
1727 13   50     34 my $base = $$dirInfo{Base} || 0;
1728 13         42 my $verbose = $et->Options('Verbose');
1729 13         35 my $unknown = $et->Options('Unknown');
1730 13         30 my $charset = $et->Options('CharsetRIFF');
1731              
1732 13 50       186 unless ($charset) {
1733 13 50 33     95 if ($$et{CodePage}) {
    50          
1734 0         0 $charset = $$et{CodePage};
1735             } elsif (defined $charset and $charset eq '0') {
1736 13         31 $charset = 'Latin';
1737             }
1738             }
1739              
1740 13 50       30 $et->VerboseDir($$dirInfo{DirName}, 0, $size) if $verbose;
1741              
1742 13         34 while ($start + 8 < $end) {
1743 34         75 my $tag = substr($$dataPt, $start, 4);
1744 34         94 my $len = Get32u($dataPt, $start + 4);
1745 34         62 $start += 8;
1746 34 50       80 if ($start + $len > $end) {
1747 0         0 $et->Warn("Bad $tag chunk");
1748 0         0 return 0;
1749             }
1750 34 100 66     99 if ($tag eq 'LIST' and $len >= 4) {
1751 5         15 $tag .= '_' . substr($$dataPt, $start, 4);
1752 5         12 $len -= 4;
1753 5         9 $start += 4;
1754             }
1755 34         82 my $tagInfo = $et->GetTagInfo($tagTbl, $tag);
1756 34         58 my $baseShift = 0;
1757 34         49 my $val;
1758 34 100 33     83 if ($tagInfo) {
    50          
1759 28 100       153 if ($$tagInfo{SubDirectory}) {
    100          
1760             # adjust base if necessary (needed for Ricoh maker notes)
1761 17         36 my $newBase = $tagInfo->{SubDirectory}{Base};
1762 17 100       42 if (defined $newBase) {
1763             # different than your average Base eval...
1764             # here we use an absolute $start address
1765 1         7 $start += $base;
1766             #### eval Base ($start)
1767 1         53 $newBase = eval $newBase;
1768 1         5 $baseShift = $newBase - $base;
1769 1         2 $start -= $base;
1770             }
1771             } elsif (not $$tagInfo{Binary}) {
1772 10   66     46 my $format = $$tagInfo{Format} || $$tagTbl{FORMAT};
1773 10 100 66     37 if ($format and $format eq 'string') {
1774 3         8 $val = substr($$dataPt, $start, $len);
1775 3         24 $val =~ s/\0+$//; # remove trailing nulls from strings
1776             # decode if necessary
1777 3 50       27 $val = $et->Decode($val, $charset) if $charset;
1778             }
1779             }
1780             } elsif ($verbose or $unknown) {
1781 0         0 MakeTagInfo($tagTbl, $tag);
1782             }
1783             $et->HandleTag($tagTbl, $tag, $val,
1784             DataPt => $dataPt,
1785 34         175 DataPos => $$dirInfo{DataPos} - $baseShift,
1786             Start => $start,
1787             Size => $len,
1788             Base => $base + $baseShift,
1789             Addr => $base + $baseShift + $start,
1790             );
1791 34 100       94 ++$len if $len & 0x01; # must account for padding if odd number of bytes
1792 34         94 $start += $len;
1793             }
1794 13         36 return 1;
1795             }
1796              
1797             #------------------------------------------------------------------------------
1798             # Process BikeBro SGLT chunk (accelerometer data) (ref PH)
1799             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
1800             # Returns: 1 on success
1801             sub ProcessSGLT($$$)
1802             {
1803 0     0 0 0 my ($et, $dirInfo, $tagTbl) = @_;
1804 0         0 my $dataPt = $$dirInfo{DataPt};
1805 0         0 my $dataLen = length $$dataPt;
1806 0         0 my $ee = $et->Options('ExtractEmbedded');
1807 0         0 my $pos;
1808             # example accelerometer record:
1809             # 0 1 2 3 4 5 6 7
1810             # 00 00 00 24 02 00 00 01 17 04 00 00 00 00 00 00 00 00 9b 02
1811             # frame------ ?? Xs X---------- Ys Y---------- Zs Z----------
1812 0         0 $$et{SET_GROUP0} = $$et{SET_GROUP1} = 'RIFF';
1813 0         0 for ($pos=0; $pos<=$dataLen-20; $pos+=20) {
1814 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
1815 0         0 my $buff = substr($$dataPt, $pos);
1816 0         0 my @a = unpack('NCCNCNCN', $buff);
1817 0 0       0 my @acc = ($a[3]*($a[2]?-1:1)/1e5, $a[5]*($a[4]?-1:1)/1e5, $a[7]*($a[6]?-1:1)/1e5);
    0          
    0          
1818 0         0 $et->HandleTag($tagTbl, FrameNumber => $a[0]);
1819 0         0 $et->HandleTag($tagTbl, Accelerometer => "@acc");
1820 0 0       0 unless ($ee) {
1821 0         0 $et->Warn('Use ExtractEmbedded option to extract all accelerometer data', 3);
1822 0         0 last;
1823             }
1824             }
1825 0         0 delete $$et{SET_GROUP0};
1826 0         0 delete $$et{SET_GROUP1};
1827 0         0 $$et{DOC_NUM} = 0;
1828 0         0 return 0;
1829             }
1830              
1831             #------------------------------------------------------------------------------
1832             # Process BikeBro SLLT chunk (GPS information) (ref PH)
1833             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
1834             # Returns: 1 on success
1835             sub ProcessSLLT($$$)
1836             {
1837 0     0 0 0 my ($et, $dirInfo, $tagTbl) = @_;
1838 0         0 my $dataPt = $$dirInfo{DataPt};
1839 0         0 my $dataLen = length $$dataPt;
1840 0         0 my $ee = $et->Options('ExtractEmbedded');
1841 0         0 my $pos;
1842             # example GPS record:
1843             # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1844             # 00 00 00 17 01 00 00 03 fa 21 ec 00 35 01 6e c0 06 00 08 00 62 10 0b 1b 07 e2 03 0e 57 4e
1845             # frame------ ?? lonDD lonDDDDDDDD latDD latDDDDDDDD alt-- spd-- hr mn sc yr--- mn dy EW NS
1846 0         0 $$et{SET_GROUP0} = $$et{SET_GROUP1} = 'RIFF';
1847 0         0 for ($pos=0; $pos<=$dataLen-30; $pos+=30) {
1848 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
1849 0         0 my $buff = substr($$dataPt, $pos);
1850 0         0 my @a = unpack('NCnNnNnnCCCnCCaa', $buff);
1851             # - is $a[1] perhaps GPSStatus? (only seen 1, or perhaps record type 1=GPS, 2=acc?)
1852 0         0 my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', @a[11..13, 8..10]);
1853 0         0 $et->HandleTag($tagTbl, FrameNumber => $a[0]);
1854 0         0 $et->HandleTag($tagTbl, GPSDateTime => $time);
1855 0 0       0 $et->HandleTag($tagTbl, GPSLatitude => ($a[4] + $a[5]/1e8) * ($a[15] eq 'S' ? -1 : 1));
1856 0 0       0 $et->HandleTag($tagTbl, GPSLongitude => ($a[2] + $a[3]/1e8) * ($a[14] eq 'W' ? -1 : 1));
1857 0         0 $et->HandleTag($tagTbl, GPSAltitude => $a[6]);
1858 0         0 $et->HandleTag($tagTbl, GPSSpeed => $a[7]);
1859 0         0 $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
1860 0 0       0 unless ($ee) {
1861 0         0 $et->Warn('Use ExtractEmbedded option to extract timed GPS', 3);
1862 0         0 last;
1863             }
1864             }
1865 0         0 delete $$et{SET_GROUP0};
1866 0         0 delete $$et{SET_GROUP1};
1867 0         0 $$et{DOC_NUM} = 0;
1868 0         0 return 1;
1869             }
1870              
1871             #------------------------------------------------------------------------------
1872             # Process Lucas streaming GPS information (Lucas LK-7900 Ace) (ref PH)
1873             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
1874             # Returns: 1 on success
1875             sub ProcessLucas($$$)
1876             {
1877 0     0 0 0 my ($et, $dirInfo, $tagTbl) = @_;
1878 0         0 my $dataPt = $$dirInfo{DataPt};
1879 0         0 my $dataLen = length $$dataPt;
1880              
1881 0 0       0 unless ($et->Options('ExtractEmbedded')) {
1882 0         0 $et->Warn('Use ExtractEmbedded option to extract timed GPS', 3);
1883 0         0 return 1;
1884             }
1885 0         0 my %recLen = ( # record lengths (not including 4-byte ID)
1886             '0GDA' => 24,
1887             '0GPS' => 48,
1888             );
1889 0         0 my ($date,$time,$lat,$lon,$alt,$spd,$sat,$dop,$ew,$ns);
1890 0         0 $$et{SET_GROUP0} = $$et{SET_GROUP1} = 'RIFF';
1891 0         0 while ($$dataPt =~ /(0GDA|0GPS)/g) {
1892 0         0 my ($rec, $pos) = ($1, pos $$dataPt);
1893 0 0       0 $pos + $recLen{$rec} > $dataLen and $et->Warn("Truncated $1 record"), last;
1894 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
1895             # records start with int64u sample date/time in ms since 1970
1896 0         0 $et->HandleTag($tagTbl, SampleDateTime => Get64u($dataPt, $pos) / 1000);
1897 0 0       0 if ($rec eq '0GPS') {
1898 0         0 my $len = Get32u($dataPt, $pos+8);
1899 0         0 my $endPos = $pos + $recLen{$rec} + $len;
1900 0 0       0 $endPos > $dataLen and $et->Warn('Truncated 0GPS record'), last;
1901 0         0 my $buff = substr($$dataPt, $pos+$recLen{$rec}, $len);
1902 0         0 while ($buff =~ /\$(GC|GA),(\d+),/g) {
1903 0         0 my $p = pos $buff;
1904 0         0 $time = $2;
1905 0 0       0 if ($1 eq 'GC') {
1906             # time date dist ? sat dop alt A
1907             # $GC,052350,180914,0000955,1,08,1.1,0017,,A*45\x0d\x0a\0
1908 0 0       0 if ($buff =~ /\G(\d+),\d*,\d*,(\d+),([-\d.]+),(\d+),\d*,A/g) {
1909 0         0 ($date,$sat,$dop,$alt) = ($1,$2,$3,$4);
1910             }
1911             } else {
1912             # time A lat lon spd N W
1913             # $GA,052351,A,0949.6626,07635.4439,049,N,E,*4C\x0d\x0a\0
1914 0 0       0 if ($buff =~ /\GA,([\d.]+),([\d.]+),(\d+),([NS]),([EW])/g) {
1915 0         0 ($lat,$lon,$spd,$ns,$ew) = ($1,$2,$3,$4,$5,$6);
1916             # lat/long are in DDDMM.MMMM format
1917 0         0 my $deg = int($lat / 100);
1918 0         0 $lat = $deg + ($lat - $deg * 100) / 60;
1919 0         0 $deg = int($lon / 100);
1920 0         0 $lon = $deg + ($lon - $deg * 100) / 60;
1921 0 0       0 $lat *= -1 if $ns eq 'S';
1922 0 0       0 $lon *= -1 if $ew eq 'W';
1923             }
1924             }
1925             # look ahead to next NMEA-like sentence, and store the fix
1926             # now only if the next sentence is not at the same time
1927 0 0       0 if ($buff !~ /\$(GC|GA),$time,/g) {
1928 0         0 pos($$dataPt) = $endPos;
1929 0 0 0     0 if ($$dataPt !~ /\$(GC|GA),(\d+)/ or $1 ne $time) {
1930 0         0 $time =~ s/(\d{2})(\d{2})(\d{2})/$1:$2:$3Z/;
1931 0 0       0 if ($date) {
1932 0         0 $date =~ s/(\d{2})(\d{2})(\d{2})/20$3:$2:$1/;
1933 0         0 $et->HandleTag($tagTbl, GPSDateTime => "$date $time");
1934             } else {
1935 0         0 $et->HandleTag($tagTbl, GPSTimeStamp => $time);
1936             }
1937 0 0       0 if (defined $lat) {
1938 0         0 $et->HandleTag($tagTbl, GPSLatitude => $lat);
1939 0         0 $et->HandleTag($tagTbl, GPSLongitude => $lon);
1940 0         0 $et->HandleTag($tagTbl, GPSSpeed => $spd);
1941             }
1942 0 0       0 if (defined $alt) {
1943 0         0 $et->HandleTag($tagTbl, GPSAltitude => $alt);
1944 0         0 $et->HandleTag($tagTbl, GPSSatellites => $sat);
1945 0         0 $et->HandleTag($tagTbl, GPSDOP => $dop);
1946             }
1947 0         0 undef $lat;
1948 0         0 undef $alt;
1949             }
1950             }
1951 0         0 pos($buff) = $p;
1952             }
1953 0         0 $pos += $len;
1954             } else { # this is an accelerometer (0GDA) record
1955             # record has 4 more int32s values (the last is always 57 or 58 --
1956             # maybe related to sample time in ms? -- not extracted)
1957 0         0 my @acc = unpack('x'.($pos+8).'V3', $$dataPt);
1958             # change to signed integer and divide by 256
1959 0 0       0 map { $_ = $_ - 4294967296 if $_ >= 0x80000000; $_ /= 256 } @acc;
  0         0  
  0         0  
1960 0         0 $et->HandleTag($tagTbl, Accelerometer => "@acc");
1961             }
1962 0         0 pos($$dataPt) = $pos + $recLen{$rec};
1963             }
1964 0         0 delete $$et{SET_GROUP0};
1965 0         0 delete $$et{SET_GROUP1};
1966 0         0 $$et{DOC_NUM} = 0;
1967 0         0 return 1;
1968             }
1969              
1970             #------------------------------------------------------------------------------
1971             # Extract information from a RIFF file
1972             # Inputs: 0) ExifTool object reference, 1) DirInfo reference
1973             # Returns: 1 on success, 0 if this wasn't a valid RIFF file
1974             sub ProcessRIFF($$)
1975             {
1976 7     7 0 19 my ($et, $dirInfo) = @_;
1977 7         19 my $raf = $$dirInfo{RAF};
1978 7         17 my ($buff, $buf2, $type, $mime, $err, $rf64);
1979 7         28 my $verbose = $et->Options('Verbose');
1980 7         28 my $unknown = $et->Options('Unknown');
1981 7         23 my $validate = $et->Options('Validate');
1982 7         35 my $ee = $et->Options('ExtractEmbedded');
1983              
1984             # verify this is a valid RIFF file
1985 7 50       29 return 0 unless $raf->Read($buff, 12) == 12;
1986 7 50       66 if ($buff =~ /^(RIFF|RF64)....(.{4})/s) {
1987 7         31 $type = $riffType{$2};
1988 7 50       29 $rf64 = 1 if $1 eq 'RF64';
1989             } else {
1990             # minimal support for a few obscure lossless audio formats...
1991 0 0 0     0 return 0 unless $buff =~ /^(LA0[234]|OFR |LPAC|wvpk)/ and $raf->Read($buf2, 1024);
1992 0         0 $type = $riffType{$1};
1993 0         0 $buff .= $buf2;
1994 0 0 0     0 return 0 unless $buff =~ /WAVE(.{4})?fmt /sg and $raf->Seek(pos($buff) - 4, 0);
1995             }
1996 7 50       30 $$raf{NoBuffer} = 1 if $et->Options('FastScan'); # disable buffering in FastScan mode
1997 7 50       43 $mime = $riffMimeType{$type} if $type;
1998 7         46 $et->SetFileType($type, $mime);
1999 7 50       35 $$et{VALUE}{FileType} .= ' (RF64)' if $rf64;
2000 7         22 $$et{RIFFStreamType} = ''; # initialize stream type
2001 7         20 $$et{RIFFStreamCodec} = []; # initialize codec array
2002 7         33 SetByteOrder('II');
2003 7         28 my $riffEnd = Get32u(\$buff, 4) + 8;
2004 7         18 $riffEnd += $riffEnd & 0x01; # (account for padding)
2005 7         22 my $tagTbl = GetTagTable('Image::ExifTool::RIFF::Main');
2006 7         15 my $pos = 12;
2007             #
2008             # Read chunks in RIFF image
2009             #
2010 7         12 for (;;) {
2011 39         148 my $num = $raf->Read($buff, 8);
2012 39 100       115 if ($num < 8) {
2013 7 50       33 $err = 1 if $num;
2014 7 50 33     27 $et->Warn('Incorrect RIFF chunk size' . " $pos vs. $riffEnd") if $validate and $pos != $riffEnd;
2015 7         15 last;
2016             }
2017 32         54 $pos += 8;
2018 32         125 my ($tag, $len) = unpack('a4V', $buff);
2019             # tweak WEBP type if this is an extended WebP
2020 32 100 66     131 $et->OverrideFileType('Extended WEBP',undef,'webp') if $tag eq 'VP8X' and $type eq 'WEBP';
2021             # special case: construct new tag name from specific LIST type
2022 32 100 66     126 if ($tag eq 'LIST') {
    50 33        
2023 10 50       29 $raf->Read($buff, 4) == 4 or $err=1, last;
2024 10         19 $pos += 4;
2025 10         27 $tag .= "_$buff";
2026 10         21 $len -= 4; # already read 4 bytes (the LIST type)
2027             } elsif ($tag eq 'data' and $len == 0xffffffff and $$et{DataSize64}) {
2028 0         0 $len = $$et{DataSize64};
2029             }
2030 32         174 $et->VPrint(0, "RIFF '${tag}' chunk ($len bytes of data):\n");
2031 32 100       90 if ($len <= 0) {
2032 3 50       18 if ($len < 0) {
    50          
2033 0         0 $et->Warn('Invalid chunk length');
2034             } elsif ($tag eq "\0\0\0\0") {
2035             # avoid reading through corupted files filled with nulls because it takes forever
2036 0         0 $et->Warn('Encountered empty null chunk. Processing aborted');
2037             } else {
2038 3         7 next;
2039             }
2040             }
2041             # stop when we hit the audio data or AVI index or AVI movie data
2042             # --> no more because Adobe Bridge stores XMP after this!!
2043             # (so now we only do this on the FastScan option)
2044 29 0 0     87 if ($et->Options('FastScan') and ($tag eq 'data' or $tag eq 'idx1' or
      33        
2045             ($tag eq 'LIST_movi' and not $ee)))
2046             {
2047 0         0 $et->VPrint(0, "(end of parsing)\n");
2048 0         0 last;
2049             }
2050             # RIFF chunks are padded to an even number of bytes
2051 29         79 my $len2 = $len + ($len & 0x01);
2052             # change name of stream txts data depending on the Codec
2053 29 50 33     78 if ($ee and $tag =~ /^(\d{2})tx$/) {
2054 0   0     0 $tag = 'tx_' . ($$et{RIFFStreamCodec}[$1] || 'Unknown');
2055 0 0       0 $tag = "tx_Unknown" unless defined $$tagTbl{$tag};
2056 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2057             }
2058 29         90 my $tagInfo = $$tagTbl{$tag};
2059             # (in LIST_movi chunk: ##db = uncompressed DIB, ##dc = compressed DIB, ##wb = audio data)
2060 29 50 0     91 if ($tagInfo or (($verbose or $unknown) and $tag !~ /^(data|idx1|LIST_movi|RIFF|\d{2}(db|dc|wb))$/)) {
    0 0        
    0 33        
      0        
2061 29 50       86 $raf->Read($buff, $len2) == $len2 or $err=1, last;
2062 29         48 my $setGroups;
2063 29 50 66     206 if ($tagInfo and ref $tagInfo eq 'HASH' and $$tagInfo{SetGroups}) {
      66        
2064 0         0 $setGroups = $$et{SET_GROUP0} = $$et{SET_GROUP1} = $$tagInfo{SetGroups};
2065             }
2066 29 0 0     80 MakeTagInfo($tagTbl, $tag) if not $tagInfo and ($verbose or $unknown);
      33        
2067 29         121 $et->HandleTag($tagTbl, $tag, $buff,
2068             DataPt => \$buff,
2069             DataPos => 0, # (relative to Base)
2070             Start => 0,
2071             Size => $len,
2072             Base => $pos,
2073             );
2074 29 50       82 if ($setGroups) {
2075 0         0 delete $$et{SET_GROUP0};
2076 0         0 delete $$et{SET_GROUP1};
2077             }
2078 29 50       85 delete $$et{DOC_NUM} if $ee;
2079             } elsif ($tag eq 'RIFF') {
2080 0 0 0     0 $et->Warn('Incorrect RIFF chunk size') if $validate and $pos - 8 != $riffEnd;
2081 0         0 $riffEnd += $len2 + 8;
2082             # don't read into RIFF chunk (eg. concatenated video file)
2083 0 0       0 $raf->Read($buff, 4) == 4 or $err=1, last; # (skip RIFF type word)
2084 0         0 $pos += 4;
2085             # extract information from remaining file as an embedded file
2086 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2087 0         0 next; # (must not increment $pos)
2088             } elsif ($tag eq 'LIST_movi' and $ee) {
2089 0         0 next; # parse into movi chunk
2090             } else {
2091 0 0 0     0 if ($len > 0x7fffffff and not $et->Options('LargeFileSupport')) {
2092 0         0 $et->Warn("Stopped parsing at large $tag chunk (LargeFileSupport not set)");
2093 0         0 last;
2094             }
2095 0 0 0     0 if ($validate and $len2) {
2096             # (must actually try to read something after seeking to detect error)
2097 0 0 0     0 $raf->Seek($len2-1, 1) and $raf->Read($buff, 1) == 1 or $err = 1, last;
2098             } else {
2099 0 0       0 $raf->Seek($len2, 1) or $err=1, last;
2100             }
2101             }
2102 29         61 $pos += $len2;
2103             }
2104 7         22 delete $$et{DOC_NUM};
2105 7 50       21 $err and $et->Warn('Error reading RIFF file (corrupted?)');
2106 7         34 return 1;
2107             }
2108              
2109             1; # end
2110              
2111             __END__