source: main/trunk/greenstone2/perllib/cpan/Image/ExifTool/Real.pm@ 34921

Last change on this file since 34921 was 34921, checked in by anupama, 3 years ago

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

File size: 26.9 KB
Line 
1#------------------------------------------------------------------------------
2# File: Real.pm
3#
4# Description: Read Real audio/video meta information
5#
6# Revisions: 05/16/2006 - P. Harvey Created
7#
8# References: 1) http://www.getid3.org/
9# 2) https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/htmfiles/rmff.htm
10#------------------------------------------------------------------------------
11
12package Image::ExifTool::Real;
13
14use strict;
15use vars qw($VERSION);
16use Image::ExifTool qw(:DataAccess :Utils);
17use Image::ExifTool::Canon;
18
19$VERSION = '1.06';
20
21sub ProcessRealMeta($$$);
22sub ProcessRealProperties($$$);
23
24# Real property types (ref PH)
25my %propertyType = (
26 0 => 'int32u',
27 2 => 'string',
28);
29
30# Real Metadata property types
31my %metadataFormat = (
32 1 => 'string', # text
33 2 => 'string', # text list
34 3 => 'flag', # 1 or 4 byte integer
35 4 => 'int32u', # 4-byte integer
36 5 => 'undef', # binary data
37 6 => 'string', # URL
38 7 => 'string', # date
39 8 => 'string', # file name
40 9 => undef, # grouping
41 10 => undef, # reference
42);
43
44# Real Metadata property flag bit descriptions
45my %metadataFlag = (
46 0 => 'Read Only',
47 1 => 'Private',
48 2 => 'Type Descriptor',
49);
50
51
52# tags used in RealMedia (RM, RV and RMVB) files
53%Image::ExifTool::Real::Media = (
54 GROUPS => { 2 => 'Video' },
55 NOTES => q{
56 These B<Tag ID>'s are Chunk ID's used in RealMedia and RealVideo (RM, RV and
57 RMVB) files.
58 },
59 CONT => { SubDirectory => { TagTable => 'Image::ExifTool::Real::ContentDescr' } },
60 MDPR => { SubDirectory => { TagTable => 'Image::ExifTool::Real::MediaProps' } },
61 PROP => { SubDirectory => { TagTable => 'Image::ExifTool::Real::Properties' } },
62 RJMD => { SubDirectory => { TagTable => 'Image::ExifTool::Real::Metadata' } },
63);
64
65# pseudo-tags used in RealAudio (RA) files
66%Image::ExifTool::Real::Audio = (
67 GROUPS => { 2 => 'Audio' },
68 NOTES => q{
69 Tags in the following table reference information extracted from various
70 versions of RealAudio (RA) files.
71 },
72 '.ra3' => { Name => 'RA3', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV3' } },
73 '.ra4' => { Name => 'RA4', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV4' } },
74 '.ra5' => { Name => 'RA5', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV5' } },
75);
76
77# pseudo-tags used in RealMedia Metafiles and RealMedia Plug-in Metafiles (RAM and RPM)
78%Image::ExifTool::Real::Metafile = (
79 GROUPS => { 2 => 'Video' },
80 NOTES => q{
81 Tags representing information extracted from Real Audio Metafile and
82 RealMedia Plug-in Metafile (RAM and RPM) files.
83 },
84 txt => 'Text',
85 url => 'URL',
86);
87
88%Image::ExifTool::Real::Properties = (
89 GROUPS => { 1 => 'Real-PROP', 2 => 'Video' },
90 PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
91 VARS => { ID_LABEL => 'Sequence' },
92 FORMAT => 'int32u',
93 0 => { Name => 'MaxBitrate', PrintConv => 'ConvertBitrate($val)' },
94 1 => { Name => 'AvgBitrate', PrintConv => 'ConvertBitrate($val)' },
95 2 => 'MaxPacketSize',
96 3 => 'AvgPacketSize',
97 4 => 'NumPackets',
98 5 => { Name => 'Duration', ValueConv => '$val / 1000', PrintConv => 'ConvertDuration($val)' },
99 6 => { Name => 'Preroll', ValueConv => '$val / 1000', PrintConv => 'ConvertDuration($val)' },
100 7 => { Name => 'IndexOffset', Unknown => 1 },
101 8 => { Name => 'DataOffset', Unknown => 1 },
102 9 => { Name => 'NumStreams', Format => 'int16u' },
103 10 => {
104 Name => 'Flags',
105 Format => 'int16u',
106 PrintConv => { BITMASK => {
107 0 => 'Allow Recording',
108 1 => 'Perfect Play',
109 2 => 'Live',
110 3 => 'Allow Download', #PH (from rmeditor dump)
111 } },
112 },
113);
114
115%Image::ExifTool::Real::MediaProps = (
116 GROUPS => { 1 => 'Real-MDPR', 2 => 'Video' },
117 PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
118 VARS => { ID_LABEL => 'Sequence' },
119 FORMAT => 'int32u',
120 PRIORITY => 0, # first stream takes priority
121 0 => { Name => 'StreamNumber', Format => 'int16u' },
122 1 => { Name => 'StreamMaxBitrate', PrintConv => 'ConvertBitrate($val)' },
123 2 => { Name => 'StreamAvgBitrate', PrintConv => 'ConvertBitrate($val)' },
124 3 => { Name => 'StreamMaxPacketSize' },
125 4 => { Name => 'StreamAvgPacketSize' },
126 5 => { Name => 'StreamStartTime' },
127 6 => { Name => 'StreamPreroll', ValueConv => '$val / 1000', PrintConv => 'ConvertDuration($val)' },
128 7 => { Name => 'StreamDuration',ValueConv => '$val / 1000', PrintConv => 'ConvertDuration($val)' },
129 8 => { Name => 'StreamNameLen', Format => 'int8u', Unknown => 1 },
130 9 => { Name => 'StreamName', Format => 'string[$val{8}]' },
131 10 => { Name => 'StreamMimeLen', Format => 'int8u', Unknown => 1 },
132 11 => {
133 Name => 'StreamMimeType',
134 Format => 'string[$val{10}]',
135 RawConv => '$self->{RealStreamMime} = $val',
136 },
137 12 => { Name => 'FileInfoLen', Unknown => 1 },
138 13 => {
139 Name => 'FileInfoLen2',
140 # if this condition fails, subsequent tags will not be processed
141 Condition => '$self->{RealStreamMime} eq "logical-fileinfo"',
142 Unknown => 1,
143 },
144 14 => {
145 Name => 'FileInfoVersion',
146 Format => 'int16u',
147 },
148 15 => {
149 Name => 'PhysicalStreams',
150 Format => 'int16u',
151 Unknown => 1,
152 },
153 16 => {
154 Name => 'PhysicalStreamNumbers',
155 Format => 'int16u[$val{15}]',
156 Unknown => 1,
157 },
158 17 => {
159 Name => 'DataOffsets',
160 Format => 'int32u[$val{15}]',
161 Unknown => 1,
162 },
163 18 => {
164 Name => 'NumRules',
165 Format => 'int16u',
166 Unknown => 1,
167 },
168 19 => {
169 Name => 'PhysicalStreamNumberMap',
170 Format => 'int16u[$val{18}]',
171 Unknown => 1,
172 },
173 20 => {
174 Name => 'NumProperties',
175 Format => 'int16u',
176 Unknown => 1,
177 },
178 21 => {
179 Name => 'FileInfoProperties',
180 Format => 'undef[$val{13}-$val{15}*6-$val{18}*2-12]',
181 SubDirectory => { TagTable => 'Image::ExifTool::Real::FileInfo' },
182 },
183);
184
185# Observed FileInfo properties (ref PH)
186%Image::ExifTool::Real::FileInfo = (
187 GROUPS => { 1 => 'Real-MDPR', 2 => 'Video' },
188 PROCESS_PROC => \&ProcessRealProperties,
189 NOTES => q{
190 The following tags have been observed in the FileInfo properties, but any
191 other existing information will also be extracted.
192 },
193 Indexable => { PrintConv => { 0 => 'False', 1 => 'True' } },
194 Keywords => { },
195 Description => { },
196 'File ID' => { Name => 'FileID' },
197 'Content Rating' => {
198 Name => 'ContentRating',
199 PrintConv => {
200 0 => 'No Rating',
201 1 => 'All Ages',
202 2 => 'Older Children',
203 3 => 'Younger Teens',
204 4 => 'Older Teens',
205 5 => 'Adult Supervision Recommended',
206 6 => 'Adults Only',
207 },
208 },
209 Audiences => { },
210 audioMode => { Name => 'AudioMode' },
211 'Creation Date' => {
212 Name => 'CreateDate',
213 Groups => { 2 => 'Time' },
214 ValueConv => q{
215 $val =~ m{(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)} ?
216 sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$3,$2,$1,$4,$5,$6) : $val
217 },
218 PrintConv => '$self->ConvertDateTime($val)',
219 },
220 'Generated By' => { Name => 'Software' },
221 'Modification Date' => {
222 Name => 'ModifyDate',
223 Groups => { 2 => 'Time' },
224 ValueConv => q{
225 $val =~ m{(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)} ?
226 sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$3,$2,$1,$4,$5,$6) : $val
227 },
228 PrintConv => '$self->ConvertDateTime($val)',
229 },
230 'Target Audiences' => { Name => 'TargetAudiences' },
231 'Audio Format' => { Name => 'AudioFormat' },
232 'Video Quality' => { Name => 'VideoQuality' },
233 videoMode => { Name => 'VideoMode' },
234);
235
236%Image::ExifTool::Real::ContentDescr = (
237 GROUPS => { 1 => 'Real-CONT', 2 => 'Video' },
238 PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
239 VARS => { ID_LABEL => 'Sequence' },
240 FORMAT => 'int16u',
241 0 => { Name => 'TitleLen', Unknown => 1 },
242 1 => { Name => 'Title', Format => 'string[$val{0}]' },
243 2 => { Name => 'AuthorLen', Unknown => 1 },
244 3 => { Name => 'Author', Format => 'string[$val{2}]', Groups => { 2 => 'Author' } },
245 4 => { Name => 'CopyrightLen', Unknown => 1 },
246 5 => { Name => 'Copyright', Format => 'string[$val{4}]', Groups => { 2 => 'Author' } },
247 6 => { Name => 'CommentLen', Unknown => 1 },
248 7 => { Name => 'Comment', Format => 'string[$val{6}]' },
249);
250
251# Real RJMD meta information (ref PH)
252%Image::ExifTool::Real::Metadata = (
253 GROUPS => { 1 => 'Real-RJMD', 2 => 'Video' },
254 PROCESS_PROC => \&ProcessRealMeta,
255 NOTES => q{
256 The tags below represent information which has been observed in the Real
257 Metadata format, but ExifTool will extract any information it finds in this
258 format. (As far as I can tell from the referenced documentation, string
259 values should be plain text, but this is not the case for the only sample
260 file I have been able to obtain containing this information. These tags
261 could also be split into separate sub-directories, but this will wait until
262 I have better documentation or a more complete set of samples.)
263 },
264 'Album/Name' => 'AlbumName',
265 'Track/Category' => 'TrackCategory',
266 'Track/Comments' => 'TrackComments',
267 'Track/Lyrics' => 'TrackLyrics',
268);
269
270%Image::ExifTool::Real::AudioV3 = (
271 GROUPS => { 1 => 'Real-RA3', 2 => 'Audio' },
272 PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
273 VARS => { ID_LABEL => 'Sequence' },
274 FORMAT => 'int8u',
275 0 => { Name => 'Channels', Format => 'int16u' },
276 1 => { Name => 'Unknown', Format => 'int16u[3]', Unknown => 1 },
277 2 => { Name => 'BytesPerMinute', Format => 'int16u' },
278 3 => { Name => 'AudioBytes', Format => 'int32u' },
279 4 => { Name => 'TitleLen', Unknown => 1 },
280 5 => { Name => 'Title', Format => 'string[$val{4}]' },
281 6 => { Name => 'ArtistLen', Unknown => 1 },
282 7 => { Name => 'Artist', Format => 'string[$val{6}]', Groups => { 2 => 'Author' } },
283 8 => { Name => 'CopyrightLen', Unknown => 1 },
284 9 => { Name => 'Copyright', Format => 'string[$val{8}]', Groups => { 2 => 'Author' } },
285 10 => { Name => 'CommentLen', Unknown => 1 },
286 11 => { Name => 'Comment', Format => 'string[$val{10}]' },
287);
288
289%Image::ExifTool::Real::AudioV4 = (
290 GROUPS => { 1 => 'Real-RA4', 2 => 'Audio' },
291 PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
292 VARS => { ID_LABEL => 'Sequence' },
293 FORMAT => 'int16u',
294 0 => { Name => 'FourCC1', Format => 'undef[4]', Unknown => 1 },
295 1 => { Name => 'AudioFileSize', Format => 'int32u', Unknown => 1 },
296 2 => { Name => 'Version2', Unknown => 1 },
297 3 => { Name => 'HeaderSize', Format => 'int32u', Unknown => 1 },
298 4 => { Name => 'CodecFlavorID', Unknown => 1 },
299 5 => { Name => 'CodedFrameSize', Format => 'int32u', Unknown => 1 },
300 6 => { Name => 'AudioBytes', Format => 'int32u' },
301 7 => { Name => 'BytesPerMinute', Format => 'int32u' },
302 8 => { Name => 'Unknown', Format => 'int32u', Unknown => 1 },
303 9 => { Name => 'SubPacketH', Unknown => 1 },
304 10 => 'AudioFrameSize',
305 11 => { Name => 'SubPacketSize', Unknown => 1 },
306 12 => { Name => 'Unknown', Unknown => 1 },
307 13 => 'SampleRate',
308 14 => { Name => 'Unknown', Unknown => 1 },
309 15 => 'BitsPerSample',
310 16 => 'Channels',
311 17 => { Name => 'FourCC2Len', Format => 'int8u', Unknown => 1 },
312 18 => { Name => 'FourCC2', Format => 'undef[4]', Unknown => 1 },
313 19 => { Name => 'FourCC3Len', Format => 'int8u', Unknown => 1 },
314 20 => { Name => 'FourCC3', Format => 'undef[4]', Unknown => 1 },
315 21 => { Name => 'Unknown', Format => 'int8u', Unknown => 1 },
316 22 => { Name => 'Unknown', Unknown => 1 },
317 23 => { Name => 'TitleLen', Format => 'int8u', Unknown => 1 },
318 24 => { Name => 'Title', Format => 'string[$val{23}]' },
319 25 => { Name => 'ArtistLen', Format => 'int8u', Unknown => 1 },
320 26 => { Name => 'Artist', Format => 'string[$val{25}]', Groups => { 2 => 'Author' } },
321 27 => { Name => 'CopyrightLen', Format => 'int8u', Unknown => 1 },
322 28 => { Name => 'Copyright', Format => 'string[$val{27}]', Groups => { 2 => 'Author' } },
323 29 => { Name => 'CommentLen', Format => 'int8u', Unknown => 1 },
324 30 => { Name => 'Comment', Format => 'string[$val{29}]' },
325);
326
327%Image::ExifTool::Real::AudioV5 = (
328 GROUPS => { 1 => 'Real-RA5', 2 => 'Audio' },
329 PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
330 VARS => { ID_LABEL => 'Sequence' },
331 FORMAT => 'int16u',
332 0 => { Name => 'FourCC1', Format => 'undef[4]', Unknown => 1 },
333 1 => { Name => 'AudioFileSize', Format => 'int32u', Unknown => 1 },
334 2 => { Name => 'Version2', Unknown => 1 },
335 3 => { Name => 'HeaderSize', Format => 'int32u', Unknown => 1 },
336 4 => { Name => 'CodecFlavorID', Unknown => 1 },
337 5 => { Name => 'CodedFrameSize', Format => 'int32u', Unknown => 1 },
338 6 => { Name => 'AudioBytes', Format => 'int32u' },
339 7 => { Name => 'BytesPerMinute', Format => 'int32u' },
340 8 => { Name => 'Unknown', Format => 'int32u', Unknown => 1 },
341 9 => { Name => 'SubPacketH', Unknown => 1 },
342 10 => { Name => 'FrameSize', Unknown => 1 },
343 11 => { Name => 'SubPacketSize', Unknown => 1 },
344 12 => 'SampleRate',
345 13 => { Name => 'SampleRate2', Unknown => 1 },
346 14 => { Name => 'BitsPerSample', Format => 'int32u' },
347 15 => 'Channels',
348 16 => { Name => 'Genr', Format => 'int32u', Unknown => 1 },
349 17 => { Name => 'FourCC3', Format => 'undef[4]', Unknown => 1 },
350);
351
352#------------------------------------------------------------------------------
353# Process Real NameValueProperties
354# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table ref
355# Returns: 1 on success
356sub ProcessRealProperties($$$)
357{
358 my ($et, $dirInfo, $tagTablePtr) = @_;
359 my $dataPt = $$dirInfo{DataPt};
360 my $dirLen = $$dirInfo{DirLen};
361 my $pos = $$dirInfo{DirStart};
362 my $verbose = $et->Options('Verbose');
363
364 $verbose and $et->VerboseDir('RealProperties', undef, $dirLen);
365
366 while ($pos + 6 <= $dirLen) {
367
368 # get property size and version
369 my ($size, $vers) = unpack("x${pos}Nn", $$dataPt);
370 last if $size < 6;
371 unless ($vers == 0) {
372 $pos += $size;
373 next;
374 }
375 $pos += 6;
376
377 my $tagLen = unpack("x${pos}C", $$dataPt);
378 ++$pos;
379
380 last if $pos + $tagLen > $dirLen;
381 my $tag = substr($$dataPt, $pos, $tagLen);
382 $pos += $tagLen;
383
384 last if $pos + 6 > $dirLen;
385 my ($type, $valLen) = unpack("x${pos}Nn", $$dataPt);
386 $pos += 6;
387
388 last if $pos + $valLen > $dirLen;
389 my $format = $propertyType{$type} || 'undef';
390 my $count = int($valLen / Image::ExifTool::FormatSize($format));
391 my $val = ReadValue($dataPt, $pos, $format, $count, $dirLen-$pos);
392
393 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
394 unless ($tagInfo) {
395 my $tagName;
396 ($tagName = $tag) =~ s/\s+//g;
397 next unless $tagName =~ /^\w+$/; # ignore crazy names
398 $tagInfo = { Name => ucfirst($tagName) };
399 AddTagToTable($tagTablePtr, $tag, $tagInfo);
400 }
401 if ($verbose) {
402 $et->VerboseInfo($tag, $tagInfo,
403 Table => $tagTablePtr,
404 Value => $val,
405 DataPt => $dataPt,
406 Size => $valLen,
407 Start => $pos,
408 Addr => $pos + $$dirInfo{DataPos},
409 Format => $format,
410 Count => $count,
411 );
412 }
413 $et->FoundTag($tagInfo, $val);
414 $pos += $valLen;
415 }
416 return 1;
417}
418
419#------------------------------------------------------------------------------
420# Process Real metadata properties
421# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table ref
422# Returns: 1 on success
423sub ProcessRealMeta($$$)
424{
425 my ($et, $dirInfo, $tagTablePtr) = @_;
426 my $dataPt = $$dirInfo{DataPt};
427 my $dataPos = $$dirInfo{DataPos};
428 my $pos = $$dirInfo{DirStart};
429 my $dirEnd = $pos + $$dirInfo{DirLen};
430 my $verbose = $et->Options('Verbose');
431 my $prefix = $$dirInfo{Prefix} || '';
432 $prefix and $prefix .= '/';
433
434 $verbose and $et->VerboseDir('RealMetadata', undef, $$dirInfo{DirLen});
435
436 for (;;) {
437 last if $pos + 28 > $dirEnd;
438 # extract fixed-position metadata structure members
439 my ($size, $type, $flags, $valuePos, $subPropPos, $numSubProps, $nameLen)
440 = unpack("x${pos}N7", $$dataPt);
441 # make pointers relative to data start
442 $valuePos += $pos;
443 $subPropPos += $pos;
444 # validate what we have read so far
445 last if $pos + $size > $dirEnd;
446 last if $pos + 28 + $nameLen > $dirEnd;
447 last if $valuePos < $pos + 28 + $nameLen;
448 last if $valuePos + 4 > $dirEnd;
449 my $tag = substr($$dataPt, $pos + 28, $nameLen);
450 $tag =~ s/\0.*//s; # truncate at null
451 $tag = $prefix . $tag;
452 my $valueLen = unpack("x${valuePos}N", $$dataPt);
453 $valuePos += 4; # point at value itself
454 last if $valuePos + $valueLen > $dirEnd;
455
456 my $format = $metadataFormat{$type};
457 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
458 unless ($tagInfo) {
459 my $tagName = $tag;
460 $tagName =~ tr/A-Za-z0-9//dc;
461 $tagInfo = { Name => ucfirst($tagName) };
462 AddTagToTable($tagTablePtr, $tag, $tagInfo);
463 }
464 if ($verbose) {
465 $format = 'undef' unless defined $format;
466 $flags = Image::ExifTool::DecodeBits($flags, \%metadataFlag);
467 }
468 if ($valueLen and $format) {
469 # (a flag can be 1 or 4 bytes)
470 if ($format eq 'flag') {
471 $format = ($valueLen == 4) ? 'int32u' : 'int8u';
472 } elsif ($type == 7 and $tagInfo) {
473 # add PrintConv and ValueConv for "date" type
474 $$tagInfo{ValueConv} or $$tagInfo{ValueConv} = q{
475 $val =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/ ?
476 sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$1,$2,$3,$4,$5,$6) :
477 $val;
478 };
479 $$tagInfo{PrintConv} or $$tagInfo{PrintConv} = '$self->ConvertDateTime($val)';
480 }
481 my $count = int($valueLen / Image::ExifTool::FormatSize($format));
482 my $val = ReadValue($dataPt, $valuePos, $format, $count, $dirEnd-$valuePos);
483 $et->HandleTag($tagTablePtr, $tag, $val,
484 DataPt => $dataPt,
485 DataPos => $dataPos,
486 Start => $valuePos,
487 Size => $valueLen,
488 Format => "type=$type, flags=$flags",
489 );
490 }
491 # extract sub-properties
492 if ($numSubProps) {
493 my $dirStart = $valuePos + $valueLen + $numSubProps * 8;
494 my %dirInfo = (
495 DataPt => $dataPt,
496 DataPos => $dataPos,
497 DirStart => $dirStart,
498 DirLen => $pos + $size - $dirStart,
499 Prefix => $tag,
500 );
501 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
502 }
503 $pos += $size; # step to next Metadata structure
504 }
505 unless ($pos == $dirEnd) {
506 $et->Warn('Format error in Real Metadata');
507 return 0;
508 }
509 return 1;
510}
511
512#------------------------------------------------------------------------------
513# Read information frame a Real file
514# Inputs: 0) ExifTool object reference, 1) Directory information reference
515# Returns: 1 on success, 0 if this wasn't a valid Real file
516sub ProcessReal($$)
517{
518 my ($et, $dirInfo) = @_;
519 my $raf = $$dirInfo{RAF};
520 my ($buff, $tag, $vers, $extra, @mimeTypes, %dirCount);
521
522 $raf->Read($buff, 8) == 8 or return 0;
523 $buff =~ m{^(\.RMF|\.ra\xfd|pnm://|rtsp://|http://)} or return 0;
524
525 my $fast3 = $$et{OPTIONS}{FastScan} && $$et{OPTIONS}{FastScan} == 3;
526 my ($type, $tagTablePtr);
527 if ($1 eq '.RMF') {
528 $tagTablePtr = GetTagTable('Image::ExifTool::Real::Media');
529 $type = 'RM';
530 } elsif ($1 eq ".ra\xfd") {
531 $tagTablePtr = GetTagTable('Image::ExifTool::Real::Audio');
532 $type = 'RA';
533 } else {
534 $tagTablePtr = GetTagTable('Image::ExifTool::Real::Metafile');
535 my $ext = $$et{FILE_EXT};
536 $type = ($ext and $ext eq 'RPM') ? 'RPM' : 'RAM';
537 require Image::ExifTool::PostScript;
538 local $/ = Image::ExifTool::PostScript::GetInputRecordSeparator($raf) || "\n";
539 $raf->Seek(0,0);
540 while ($raf->ReadLine($buff)) {
541 last if length $buff > 256;
542 next unless $buff ;
543 chomp $buff;
544 if ($type) {
545 # must be a Real file type if protocol is http
546 return 0 if $buff =~ /^http/ and $buff !~ /\.(ra|rm|rv|rmvb|smil)$/i;
547 $et->SetFileType($type);
548 return 1 if $fast3;
549 undef $type;
550 }
551 # save URL or Text from RAM file
552 my $tag = $buff =~ m{^[a-z]{3,4}://} ? 'url' : 'txt';
553 $et->HandleTag($tagTablePtr, $tag, $buff);
554 }
555 return 1;
556 }
557
558 $et->SetFileType($type);
559 return 1 if $fast3;
560 SetByteOrder('MM');
561 my $verbose = $et->Options('Verbose');
562#
563# Process RealAudio file
564#
565 if ($type eq 'RA') {
566 ($vers, $extra) = unpack('x4nn', $buff);
567 $tag = ".ra$vers";
568 my $fpos = $raf->Tell();
569 unless ($raf->Read($buff, 512)) {
570 $et->Warn('Error reading audio header');
571 return 1;
572 }
573 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
574 if ($verbose > 2) {
575 $et->VerboseInfo($tag, $tagInfo, DataPt => \$buff, DataPos => $fpos);
576 }
577 if ($tagInfo) {
578 my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
579 my %dirInfo = (
580 DataPt => \$buff,
581 DataPos => $fpos,
582 DirLen => length $buff,
583 DirStart => 0,
584 );
585 $et->ProcessDirectory(\%dirInfo, $subTablePtr);
586 } else {
587 $et->Warn('Unsupported RealAudio version');
588 }
589 return 1;
590 }
591#
592# Process RealMedia file
593#
594 # skip the rest of the RM header
595 my $size = unpack('x4N', $buff);
596 unless ($raf->Seek($size - 8, 1)) {
597 $et->Warn('Error seeking in file');
598 return 0;
599 }
600
601 # Process RealMedia chunks
602 for (;;) {
603 $raf->Read($buff, 10) == 10 or last;
604 ($tag, $size, $vers) = unpack('a4Nn', $buff);
605 last if $tag eq "\0\0\0\0";
606 if ($verbose) {
607 $et->VPrint(0, "$tag chunk ($size bytes):\n");
608 } else {
609 last if $tag eq 'DATA'; # stop normal parsing at DATA tag
610 }
611 if ($size & 0x80000000) {
612 $et->Warn('Bad chunk header');
613 last;
614 }
615 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
616 if ($tagInfo and $$tagInfo{SubDirectory}) {
617 my $fpos = $raf->Tell();
618 unless ($raf->Read($buff, $size-10) == $size-10) {
619 $et->Warn("Error reading $tag chunk");
620 last;
621 }
622 if ($verbose > 2) {
623 $et->VerboseInfo($tag, $tagInfo, DataPt => \$buff, DataPos => $fpos);
624 }
625 my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
626 my %dirInfo = (
627 DataPt => \$buff,
628 DataPos => $fpos,
629 DirLen => length $buff,
630 DirStart => 0,
631 );
632 if ($dirCount{$tag}) {
633 $$et{SET_GROUP1} = '+' . ++$dirCount{$tag};
634 } else {
635 $dirCount{$tag} = 1;
636 }
637 $et->ProcessDirectory(\%dirInfo, $subTablePtr);
638 delete $$et{SET_GROUP1};
639 # keep track of stream MIME types
640 my $mime = $$et{RealStreamMime};
641 if ($mime) {
642 delete $$et{RealStreamMime};
643 $mime =~ s/\0.*//s;
644 push @mimeTypes, $mime unless $mime =~ /^logical-/;
645 }
646 } else {
647 unless ($raf->Seek($size-10, 1)) {
648 $et->Warn('Error seeking in file');
649 last;
650 }
651 }
652 }
653 # override MIMEType with stream MIME type if we only have one stream
654 if (@mimeTypes == 1 and length $mimeTypes[0]) {
655 $$et{VALUE}{MIMEType} = $mimeTypes[0];
656 $et->VPrint(0, " MIMEType = $mimeTypes[0]\n");
657 }
658#
659# Process footer containing Real metadata and ID3 information
660#
661 if ($raf->Seek(-140, 2) and $raf->Read($buff, 12) == 12 and $buff =~ /^RMJE/) {
662 my $metaSize = unpack('x8N', $buff);
663 if ($raf->Seek(-$metaSize-12, 1) and
664 $raf->Read($buff, $metaSize) == $metaSize and
665 $buff =~ /^RJMD/)
666 {
667 my %dirInfo = (
668 DataPt => \$buff,
669 DataPos => $raf->Tell() - $metaSize,
670 DirStart => 8,
671 DirLen => length($buff) - 8,
672 );
673 my $tagTablePtr = GetTagTable('Image::ExifTool::Real::Metadata');
674 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
675 } else {
676 $et->Warn('Bad metadata footer');
677 }
678 if ($raf->Seek(-128, 2) and $raf->Read($buff, 128) == 128 and $buff =~ /^TAG/) {
679 $et->VPrint(0, "ID3v1:\n");
680 my %dirInfo = (
681 DataPt => \$buff,
682 DirStart => 0,
683 DirLen => length($buff),
684 );
685 my $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v1');
686 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
687 }
688 }
689 return 1;
690}
691
6921; # end
693
694__END__
695
696=head1 NAME
697
698Image::ExifTool::Real - Read Real audio/video meta information
699
700=head1 SYNOPSIS
701
702This module is used by Image::ExifTool
703
704=head1 DESCRIPTION
705
706This module contains the routines required by Image::ExifTool to read meta
707information in RealAudio (RA), RealMedia (RM, RV and RMVB) and RealMedia
708Metafile (RAM and RPM) files.
709
710=head1 NOTES
711
712There must be a bug in the software that wrote the Metadata used in the test
713file t/images/Real.rm because the TrackLyricsDataSize word is written
714little-endian, but the Real format is big-endian.
715
716=head1 AUTHOR
717
718Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
719
720This library is free software; you can redistribute it and/or modify it
721under the same terms as Perl itself.
722
723=head1 REFERENCES
724
725=over 4
726
727=item L<http://www.getid3.org/>
728
729=item L<https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/htmfiles/rmff.htm>
730
731=back
732
733=head1 SEE ALSO
734
735L<Image::ExifTool::TagNames/Real Tags>,
736L<Image::ExifTool(3pm)|Image::ExifTool>
737
738=cut
739
Note: See TracBrowser for help on using the repository browser.