123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 |
- #------------------------------------------------------------------------------
- # File: Real.pm
- #
- # Description: Read Real audio/video meta information
- #
- # Revisions: 05/16/2006 - P. Harvey Created
- #
- # References: 1) http://www.getid3.org/
- # 2) https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/htmfiles/rmff.htm
- #------------------------------------------------------------------------------
- package Image::ExifTool::Real;
- use strict;
- use vars qw($VERSION);
- use Image::ExifTool qw(:DataAccess :Utils);
- use Image::ExifTool::Canon;
- $VERSION = '1.06';
- sub ProcessRealMeta($$$);
- sub ProcessRealProperties($$$);
- # Real property types (ref PH)
- my %propertyType = (
- 0 => 'int32u',
- 2 => 'string',
- );
- # Real Metadata property types
- my %metadataFormat = (
- 1 => 'string', # text
- 2 => 'string', # text list
- 3 => 'flag', # 1 or 4 byte integer
- 4 => 'int32u', # 4-byte integer
- 5 => 'undef', # binary data
- 6 => 'string', # URL
- 7 => 'string', # date
- 8 => 'string', # file name
- 9 => undef, # grouping
- 10 => undef, # reference
- );
- # Real Metadata property flag bit descriptions
- my %metadataFlag = (
- 0 => 'Read Only',
- 1 => 'Private',
- 2 => 'Type Descriptor',
- );
- # tags used in RealMedia (RM, RV and RMVB) files
- %Image::ExifTool::Real::Media = (
- GROUPS => { 2 => 'Video' },
- NOTES => q{
- These B<Tag ID>'s are Chunk ID's used in RealMedia and RealVideo (RM, RV and
- RMVB) files.
- },
- CONT => { SubDirectory => { TagTable => 'Image::ExifTool::Real::ContentDescr' } },
- MDPR => { SubDirectory => { TagTable => 'Image::ExifTool::Real::MediaProps' } },
- PROP => { SubDirectory => { TagTable => 'Image::ExifTool::Real::Properties' } },
- RJMD => { SubDirectory => { TagTable => 'Image::ExifTool::Real::Metadata' } },
- );
- # pseudo-tags used in RealAudio (RA) files
- %Image::ExifTool::Real::Audio = (
- GROUPS => { 2 => 'Audio' },
- NOTES => q{
- Tags in the following table reference information extracted from various
- versions of RealAudio (RA) files.
- },
- '.ra3' => { Name => 'RA3', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV3' } },
- '.ra4' => { Name => 'RA4', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV4' } },
- '.ra5' => { Name => 'RA5', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV5' } },
- );
- # pseudo-tags used in RealMedia Metafiles and RealMedia Plug-in Metafiles (RAM and RPM)
- %Image::ExifTool::Real::Metafile = (
- GROUPS => { 2 => 'Video' },
- NOTES => q{
- Tags representing information extracted from Real Audio Metafile and
- RealMedia Plug-in Metafile (RAM and RPM) files.
- },
- txt => 'Text',
- url => 'URL',
- );
- %Image::ExifTool::Real::Properties = (
- GROUPS => { 1 => 'Real-PROP', 2 => 'Video' },
- PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
- VARS => { ID_LABEL => 'Sequence' },
- FORMAT => 'int32u',
- 0 => { Name => 'MaxBitrate', PrintConv => 'ConvertBitrate($val)' },
- 1 => { Name => 'AvgBitrate', PrintConv => 'ConvertBitrate($val)' },
- 2 => 'MaxPacketSize',
- 3 => 'AvgPacketSize',
- 4 => 'NumPackets',
- 5 => { Name => 'Duration', ValueConv => '$val / 1000', PrintConv => 'ConvertDuration($val)' },
- 6 => { Name => 'Preroll', ValueConv => '$val / 1000', PrintConv => 'ConvertDuration($val)' },
- 7 => { Name => 'IndexOffset', Unknown => 1 },
- 8 => { Name => 'DataOffset', Unknown => 1 },
- 9 => { Name => 'NumStreams', Format => 'int16u' },
- 10 => {
- Name => 'Flags',
- Format => 'int16u',
- PrintConv => { BITMASK => {
- 0 => 'Allow Recording',
- 1 => 'Perfect Play',
- 2 => 'Live',
- 3 => 'Allow Download', #PH (from rmeditor dump)
- } },
- },
- );
- %Image::ExifTool::Real::MediaProps = (
- GROUPS => { 1 => 'Real-MDPR', 2 => 'Video' },
- PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
- VARS => { ID_LABEL => 'Sequence' },
- FORMAT => 'int32u',
- PRIORITY => 0, # first stream takes priority
- 0 => { Name => 'StreamNumber', Format => 'int16u' },
- 1 => { Name => 'StreamMaxBitrate', PrintConv => 'ConvertBitrate($val)' },
- 2 => { Name => 'StreamAvgBitrate', PrintConv => 'ConvertBitrate($val)' },
- 3 => { Name => 'StreamMaxPacketSize' },
- 4 => { Name => 'StreamAvgPacketSize' },
- 5 => { Name => 'StreamStartTime' },
- 6 => { Name => 'StreamPreroll', ValueConv => '$val / 1000', PrintConv => 'ConvertDuration($val)' },
- 7 => { Name => 'StreamDuration',ValueConv => '$val / 1000', PrintConv => 'ConvertDuration($val)' },
- 8 => { Name => 'StreamNameLen', Format => 'int8u', Unknown => 1 },
- 9 => { Name => 'StreamName', Format => 'string[$val{8}]' },
- 10 => { Name => 'StreamMimeLen', Format => 'int8u', Unknown => 1 },
- 11 => {
- Name => 'StreamMimeType',
- Format => 'string[$val{10}]',
- RawConv => '$self->{RealStreamMime} = $val',
- },
- 12 => { Name => 'FileInfoLen', Unknown => 1 },
- 13 => {
- Name => 'FileInfoLen2',
- # if this condition fails, subsequent tags will not be processed
- Condition => '$self->{RealStreamMime} eq "logical-fileinfo"',
- Unknown => 1,
- },
- 14 => {
- Name => 'FileInfoVersion',
- Format => 'int16u',
- },
- 15 => {
- Name => 'PhysicalStreams',
- Format => 'int16u',
- Unknown => 1,
- },
- 16 => {
- Name => 'PhysicalStreamNumbers',
- Format => 'int16u[$val{15}]',
- Unknown => 1,
- },
- 17 => {
- Name => 'DataOffsets',
- Format => 'int32u[$val{15}]',
- Unknown => 1,
- },
- 18 => {
- Name => 'NumRules',
- Format => 'int16u',
- Unknown => 1,
- },
- 19 => {
- Name => 'PhysicalStreamNumberMap',
- Format => 'int16u[$val{18}]',
- Unknown => 1,
- },
- 20 => {
- Name => 'NumProperties',
- Format => 'int16u',
- Unknown => 1,
- },
- 21 => {
- Name => 'FileInfoProperties',
- Format => 'undef[$val{13}-$val{15}*6-$val{18}*2-12]',
- SubDirectory => { TagTable => 'Image::ExifTool::Real::FileInfo' },
- },
- );
- # Observed FileInfo properties (ref PH)
- %Image::ExifTool::Real::FileInfo = (
- GROUPS => { 1 => 'Real-MDPR', 2 => 'Video' },
- PROCESS_PROC => \&ProcessRealProperties,
- NOTES => q{
- The following tags have been observed in the FileInfo properties, but any
- other existing information will also be extracted.
- },
- Indexable => { PrintConv => { 0 => 'False', 1 => 'True' } },
- Keywords => { },
- Description => { },
- 'File ID' => { Name => 'FileID' },
- 'Content Rating' => {
- Name => 'ContentRating',
- PrintConv => {
- 0 => 'No Rating',
- 1 => 'All Ages',
- 2 => 'Older Children',
- 3 => 'Younger Teens',
- 4 => 'Older Teens',
- 5 => 'Adult Supervision Recommended',
- 6 => 'Adults Only',
- },
- },
- Audiences => { },
- audioMode => { Name => 'AudioMode' },
- 'Creation Date' => {
- Name => 'CreateDate',
- Groups => { 2 => 'Time' },
- ValueConv => q{
- $val =~ m{(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)} ?
- sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$3,$2,$1,$4,$5,$6) : $val
- },
- PrintConv => '$self->ConvertDateTime($val)',
- },
- 'Generated By' => { Name => 'Software' },
- 'Modification Date' => {
- Name => 'ModifyDate',
- Groups => { 2 => 'Time' },
- ValueConv => q{
- $val =~ m{(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)} ?
- sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$3,$2,$1,$4,$5,$6) : $val
- },
- PrintConv => '$self->ConvertDateTime($val)',
- },
- 'Target Audiences' => { Name => 'TargetAudiences' },
- 'Audio Format' => { Name => 'AudioFormat' },
- 'Video Quality' => { Name => 'VideoQuality' },
- videoMode => { Name => 'VideoMode' },
- );
- %Image::ExifTool::Real::ContentDescr = (
- GROUPS => { 1 => 'Real-CONT', 2 => 'Video' },
- PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
- VARS => { ID_LABEL => 'Sequence' },
- FORMAT => 'int16u',
- 0 => { Name => 'TitleLen', Unknown => 1 },
- 1 => { Name => 'Title', Format => 'string[$val{0}]' },
- 2 => { Name => 'AuthorLen', Unknown => 1 },
- 3 => { Name => 'Author', Format => 'string[$val{2}]', Groups => { 2 => 'Author' } },
- 4 => { Name => 'CopyrightLen', Unknown => 1 },
- 5 => { Name => 'Copyright', Format => 'string[$val{4}]', Groups => { 2 => 'Author' } },
- 6 => { Name => 'CommentLen', Unknown => 1 },
- 7 => { Name => 'Comment', Format => 'string[$val{6}]' },
- );
- # Real RJMD meta information (ref PH)
- %Image::ExifTool::Real::Metadata = (
- GROUPS => { 1 => 'Real-RJMD', 2 => 'Video' },
- PROCESS_PROC => \&ProcessRealMeta,
- NOTES => q{
- The tags below represent information which has been observed in the Real
- Metadata format, but ExifTool will extract any information it finds in this
- format. (As far as I can tell from the referenced documentation, string
- values should be plain text, but this is not the case for the only sample
- file I have been able to obtain containing this information. These tags
- could also be split into separate sub-directories, but this will wait until
- I have better documentation or a more complete set of samples.)
- },
- 'Album/Name' => 'AlbumName',
- 'Track/Category' => 'TrackCategory',
- 'Track/Comments' => 'TrackComments',
- 'Track/Lyrics' => 'TrackLyrics',
- );
- %Image::ExifTool::Real::AudioV3 = (
- GROUPS => { 1 => 'Real-RA3', 2 => 'Audio' },
- PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
- VARS => { ID_LABEL => 'Sequence' },
- FORMAT => 'int8u',
- 0 => { Name => 'Channels', Format => 'int16u' },
- 1 => { Name => 'Unknown', Format => 'int16u[3]', Unknown => 1 },
- 2 => { Name => 'BytesPerMinute', Format => 'int16u' },
- 3 => { Name => 'AudioBytes', Format => 'int32u' },
- 4 => { Name => 'TitleLen', Unknown => 1 },
- 5 => { Name => 'Title', Format => 'string[$val{4}]' },
- 6 => { Name => 'ArtistLen', Unknown => 1 },
- 7 => { Name => 'Artist', Format => 'string[$val{6}]', Groups => { 2 => 'Author' } },
- 8 => { Name => 'CopyrightLen', Unknown => 1 },
- 9 => { Name => 'Copyright', Format => 'string[$val{8}]', Groups => { 2 => 'Author' } },
- 10 => { Name => 'CommentLen', Unknown => 1 },
- 11 => { Name => 'Comment', Format => 'string[$val{10}]' },
- );
- %Image::ExifTool::Real::AudioV4 = (
- GROUPS => { 1 => 'Real-RA4', 2 => 'Audio' },
- PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
- VARS => { ID_LABEL => 'Sequence' },
- FORMAT => 'int16u',
- 0 => { Name => 'FourCC1', Format => 'undef[4]', Unknown => 1 },
- 1 => { Name => 'AudioFileSize', Format => 'int32u', Unknown => 1 },
- 2 => { Name => 'Version2', Unknown => 1 },
- 3 => { Name => 'HeaderSize', Format => 'int32u', Unknown => 1 },
- 4 => { Name => 'CodecFlavorID', Unknown => 1 },
- 5 => { Name => 'CodedFrameSize', Format => 'int32u', Unknown => 1 },
- 6 => { Name => 'AudioBytes', Format => 'int32u' },
- 7 => { Name => 'BytesPerMinute', Format => 'int32u' },
- 8 => { Name => 'Unknown', Format => 'int32u', Unknown => 1 },
- 9 => { Name => 'SubPacketH', Unknown => 1 },
- 10 => 'AudioFrameSize',
- 11 => { Name => 'SubPacketSize', Unknown => 1 },
- 12 => { Name => 'Unknown', Unknown => 1 },
- 13 => 'SampleRate',
- 14 => { Name => 'Unknown', Unknown => 1 },
- 15 => 'BitsPerSample',
- 16 => 'Channels',
- 17 => { Name => 'FourCC2Len', Format => 'int8u', Unknown => 1 },
- 18 => { Name => 'FourCC2', Format => 'undef[4]', Unknown => 1 },
- 19 => { Name => 'FourCC3Len', Format => 'int8u', Unknown => 1 },
- 20 => { Name => 'FourCC3', Format => 'undef[4]', Unknown => 1 },
- 21 => { Name => 'Unknown', Format => 'int8u', Unknown => 1 },
- 22 => { Name => 'Unknown', Unknown => 1 },
- 23 => { Name => 'TitleLen', Format => 'int8u', Unknown => 1 },
- 24 => { Name => 'Title', Format => 'string[$val{23}]' },
- 25 => { Name => 'ArtistLen', Format => 'int8u', Unknown => 1 },
- 26 => { Name => 'Artist', Format => 'string[$val{25}]', Groups => { 2 => 'Author' } },
- 27 => { Name => 'CopyrightLen', Format => 'int8u', Unknown => 1 },
- 28 => { Name => 'Copyright', Format => 'string[$val{27}]', Groups => { 2 => 'Author' } },
- 29 => { Name => 'CommentLen', Format => 'int8u', Unknown => 1 },
- 30 => { Name => 'Comment', Format => 'string[$val{29}]' },
- );
- %Image::ExifTool::Real::AudioV5 = (
- GROUPS => { 1 => 'Real-RA5', 2 => 'Audio' },
- PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
- VARS => { ID_LABEL => 'Sequence' },
- FORMAT => 'int16u',
- 0 => { Name => 'FourCC1', Format => 'undef[4]', Unknown => 1 },
- 1 => { Name => 'AudioFileSize', Format => 'int32u', Unknown => 1 },
- 2 => { Name => 'Version2', Unknown => 1 },
- 3 => { Name => 'HeaderSize', Format => 'int32u', Unknown => 1 },
- 4 => { Name => 'CodecFlavorID', Unknown => 1 },
- 5 => { Name => 'CodedFrameSize', Format => 'int32u', Unknown => 1 },
- 6 => { Name => 'AudioBytes', Format => 'int32u' },
- 7 => { Name => 'BytesPerMinute', Format => 'int32u' },
- 8 => { Name => 'Unknown', Format => 'int32u', Unknown => 1 },
- 9 => { Name => 'SubPacketH', Unknown => 1 },
- 10 => { Name => 'FrameSize', Unknown => 1 },
- 11 => { Name => 'SubPacketSize', Unknown => 1 },
- 12 => 'SampleRate',
- 13 => { Name => 'SampleRate2', Unknown => 1 },
- 14 => { Name => 'BitsPerSample', Format => 'int32u' },
- 15 => 'Channels',
- 16 => { Name => 'Genr', Format => 'int32u', Unknown => 1 },
- 17 => { Name => 'FourCC3', Format => 'undef[4]', Unknown => 1 },
- );
- #------------------------------------------------------------------------------
- # Process Real NameValueProperties
- # Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table ref
- # Returns: 1 on success
- sub ProcessRealProperties($$$)
- {
- my ($et, $dirInfo, $tagTablePtr) = @_;
- my $dataPt = $$dirInfo{DataPt};
- my $dirLen = $$dirInfo{DirLen};
- my $pos = $$dirInfo{DirStart};
- my $verbose = $et->Options('Verbose');
- $verbose and $et->VerboseDir('RealProperties', undef, $dirLen);
- while ($pos + 6 <= $dirLen) {
- # get property size and version
- my ($size, $vers) = unpack("x${pos}Nn", $$dataPt);
- last if $size < 6;
- unless ($vers == 0) {
- $pos += $size;
- next;
- }
- $pos += 6;
- my $tagLen = unpack("x${pos}C", $$dataPt);
- ++$pos;
- last if $pos + $tagLen > $dirLen;
- my $tag = substr($$dataPt, $pos, $tagLen);
- $pos += $tagLen;
- last if $pos + 6 > $dirLen;
- my ($type, $valLen) = unpack("x${pos}Nn", $$dataPt);
- $pos += 6;
- last if $pos + $valLen > $dirLen;
- my $format = $propertyType{$type} || 'undef';
- my $count = int($valLen / Image::ExifTool::FormatSize($format));
- my $val = ReadValue($dataPt, $pos, $format, $count, $dirLen-$pos);
- my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
- unless ($tagInfo) {
- my $tagName;
- ($tagName = $tag) =~ s/\s+//g;
- next unless $tagName =~ /^\w+$/; # ignore crazy names
- $tagInfo = { Name => ucfirst($tagName) };
- AddTagToTable($tagTablePtr, $tag, $tagInfo);
- }
- if ($verbose) {
- $et->VerboseInfo($tag, $tagInfo,
- Table => $tagTablePtr,
- Value => $val,
- DataPt => $dataPt,
- Size => $valLen,
- Start => $pos,
- Addr => $pos + $$dirInfo{DataPos},
- Format => $format,
- Count => $count,
- );
- }
- $et->FoundTag($tagInfo, $val);
- $pos += $valLen;
- }
- return 1;
- }
- #------------------------------------------------------------------------------
- # Process Real metadata properties
- # Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table ref
- # Returns: 1 on success
- sub ProcessRealMeta($$$)
- {
- my ($et, $dirInfo, $tagTablePtr) = @_;
- my $dataPt = $$dirInfo{DataPt};
- my $dataPos = $$dirInfo{DataPos};
- my $pos = $$dirInfo{DirStart};
- my $dirEnd = $pos + $$dirInfo{DirLen};
- my $verbose = $et->Options('Verbose');
- my $prefix = $$dirInfo{Prefix} || '';
- $prefix and $prefix .= '/';
- $verbose and $et->VerboseDir('RealMetadata', undef, $$dirInfo{DirLen});
- for (;;) {
- last if $pos + 28 > $dirEnd;
- # extract fixed-position metadata structure members
- my ($size, $type, $flags, $valuePos, $subPropPos, $numSubProps, $nameLen)
- = unpack("x${pos}N7", $$dataPt);
- # make pointers relative to data start
- $valuePos += $pos;
- $subPropPos += $pos;
- # validate what we have read so far
- last if $pos + $size > $dirEnd;
- last if $pos + 28 + $nameLen > $dirEnd;
- last if $valuePos < $pos + 28 + $nameLen;
- last if $valuePos + 4 > $dirEnd;
- my $tag = substr($$dataPt, $pos + 28, $nameLen);
- $tag =~ s/\0.*//s; # truncate at null
- $tag = $prefix . $tag;
- my $valueLen = unpack("x${valuePos}N", $$dataPt);
- $valuePos += 4; # point at value itself
- last if $valuePos + $valueLen > $dirEnd;
- my $format = $metadataFormat{$type};
- my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
- unless ($tagInfo) {
- my $tagName = $tag;
- $tagName =~ tr/A-Za-z0-9//dc;
- $tagInfo = { Name => ucfirst($tagName) };
- AddTagToTable($tagTablePtr, $tag, $tagInfo);
- }
- if ($verbose) {
- $format = 'undef' unless defined $format;
- $flags = Image::ExifTool::DecodeBits($flags, \%metadataFlag);
- }
- if ($valueLen and $format) {
- # (a flag can be 1 or 4 bytes)
- if ($format eq 'flag') {
- $format = ($valueLen == 4) ? 'int32u' : 'int8u';
- } elsif ($type == 7 and $tagInfo) {
- # add PrintConv and ValueConv for "date" type
- $$tagInfo{ValueConv} or $$tagInfo{ValueConv} = q{
- $val =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/ ?
- sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$1,$2,$3,$4,$5,$6) :
- $val;
- };
- $$tagInfo{PrintConv} or $$tagInfo{PrintConv} = '$self->ConvertDateTime($val)';
- }
- my $count = int($valueLen / Image::ExifTool::FormatSize($format));
- my $val = ReadValue($dataPt, $valuePos, $format, $count, $dirEnd-$valuePos);
- $et->HandleTag($tagTablePtr, $tag, $val,
- DataPt => $dataPt,
- DataPos => $dataPos,
- Start => $valuePos,
- Size => $valueLen,
- Format => "type=$type, flags=$flags",
- );
- }
- # extract sub-properties
- if ($numSubProps) {
- my $dirStart = $valuePos + $valueLen + $numSubProps * 8;
- my %dirInfo = (
- DataPt => $dataPt,
- DataPos => $dataPos,
- DirStart => $dirStart,
- DirLen => $pos + $size - $dirStart,
- Prefix => $tag,
- );
- $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
- }
- $pos += $size; # step to next Metadata structure
- }
- unless ($pos == $dirEnd) {
- $et->Warn('Format error in Real Metadata');
- return 0;
- }
- return 1;
- }
- #------------------------------------------------------------------------------
- # Read information frame a Real file
- # Inputs: 0) ExifTool object reference, 1) Directory information reference
- # Returns: 1 on success, 0 if this wasn't a valid Real file
- sub ProcessReal($$)
- {
- my ($et, $dirInfo) = @_;
- my $raf = $$dirInfo{RAF};
- my ($buff, $tag, $vers, $extra, @mimeTypes, %dirCount);
- $raf->Read($buff, 8) == 8 or return 0;
- $buff =~ m{^(\.RMF|\.ra\xfd|pnm://|rtsp://|http://)} or return 0;
- my $fast3 = $$et{OPTIONS}{FastScan} && $$et{OPTIONS}{FastScan} == 3;
- my ($type, $tagTablePtr);
- if ($1 eq '.RMF') {
- $tagTablePtr = GetTagTable('Image::ExifTool::Real::Media');
- $type = 'RM';
- } elsif ($1 eq ".ra\xfd") {
- $tagTablePtr = GetTagTable('Image::ExifTool::Real::Audio');
- $type = 'RA';
- } else {
- $tagTablePtr = GetTagTable('Image::ExifTool::Real::Metafile');
- my $ext = $$et{FILE_EXT};
- $type = ($ext and $ext eq 'RPM') ? 'RPM' : 'RAM';
- require Image::ExifTool::PostScript;
- local $/ = Image::ExifTool::PostScript::GetInputRecordSeparator($raf) || "\n";
- $raf->Seek(0,0);
- while ($raf->ReadLine($buff)) {
- last if length $buff > 256;
- next unless $buff ;
- chomp $buff;
- if ($type) {
- # must be a Real file type if protocol is http
- return 0 if $buff =~ /^http/ and $buff !~ /\.(ra|rm|rv|rmvb|smil)$/i;
- $et->SetFileType($type);
- return 1 if $fast3;
- undef $type;
- }
- # save URL or Text from RAM file
- my $tag = $buff =~ m{^[a-z]{3,4}://} ? 'url' : 'txt';
- $et->HandleTag($tagTablePtr, $tag, $buff);
- }
- return 1;
- }
- $et->SetFileType($type);
- return 1 if $fast3;
- SetByteOrder('MM');
- my $verbose = $et->Options('Verbose');
- #
- # Process RealAudio file
- #
- if ($type eq 'RA') {
- ($vers, $extra) = unpack('x4nn', $buff);
- $tag = ".ra$vers";
- my $fpos = $raf->Tell();
- unless ($raf->Read($buff, 512)) {
- $et->Warn('Error reading audio header');
- return 1;
- }
- my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
- if ($verbose > 2) {
- $et->VerboseInfo($tag, $tagInfo, DataPt => \$buff, DataPos => $fpos);
- }
- if ($tagInfo) {
- my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
- my %dirInfo = (
- DataPt => \$buff,
- DataPos => $fpos,
- DirLen => length $buff,
- DirStart => 0,
- );
- $et->ProcessDirectory(\%dirInfo, $subTablePtr);
- } else {
- $et->Warn('Unsupported RealAudio version');
- }
- return 1;
- }
- #
- # Process RealMedia file
- #
- # skip the rest of the RM header
- my $size = unpack('x4N', $buff);
- unless ($raf->Seek($size - 8, 1)) {
- $et->Warn('Error seeking in file');
- return 0;
- }
- # Process RealMedia chunks
- for (;;) {
- $raf->Read($buff, 10) == 10 or last;
- ($tag, $size, $vers) = unpack('a4Nn', $buff);
- last if $tag eq "\0\0\0\0";
- if ($verbose) {
- $et->VPrint(0, "$tag chunk ($size bytes):\n");
- } else {
- last if $tag eq 'DATA'; # stop normal parsing at DATA tag
- }
- if ($size & 0x80000000) {
- $et->Warn('Bad chunk header');
- last;
- }
- my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
- if ($tagInfo and $$tagInfo{SubDirectory}) {
- my $fpos = $raf->Tell();
- unless ($raf->Read($buff, $size-10) == $size-10) {
- $et->Warn("Error reading $tag chunk");
- last;
- }
- if ($verbose > 2) {
- $et->VerboseInfo($tag, $tagInfo, DataPt => \$buff, DataPos => $fpos);
- }
- my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
- my %dirInfo = (
- DataPt => \$buff,
- DataPos => $fpos,
- DirLen => length $buff,
- DirStart => 0,
- );
- if ($dirCount{$tag}) {
- $$et{SET_GROUP1} = '+' . ++$dirCount{$tag};
- } else {
- $dirCount{$tag} = 1;
- }
- $et->ProcessDirectory(\%dirInfo, $subTablePtr);
- delete $$et{SET_GROUP1};
- # keep track of stream MIME types
- my $mime = $$et{RealStreamMime};
- if ($mime) {
- delete $$et{RealStreamMime};
- $mime =~ s/\0.*//s;
- push @mimeTypes, $mime unless $mime =~ /^logical-/;
- }
- } else {
- unless ($raf->Seek($size-10, 1)) {
- $et->Warn('Error seeking in file');
- last;
- }
- }
- }
- # override MIMEType with stream MIME type if we only have one stream
- if (@mimeTypes == 1 and length $mimeTypes[0]) {
- $$et{VALUE}{MIMEType} = $mimeTypes[0];
- $et->VPrint(0, " MIMEType = $mimeTypes[0]\n");
- }
- #
- # Process footer containing Real metadata and ID3 information
- #
- if ($raf->Seek(-140, 2) and $raf->Read($buff, 12) == 12 and $buff =~ /^RMJE/) {
- my $metaSize = unpack('x8N', $buff);
- if ($raf->Seek(-$metaSize-12, 1) and
- $raf->Read($buff, $metaSize) == $metaSize and
- $buff =~ /^RJMD/)
- {
- my %dirInfo = (
- DataPt => \$buff,
- DataPos => $raf->Tell() - $metaSize,
- DirStart => 8,
- DirLen => length($buff) - 8,
- );
- my $tagTablePtr = GetTagTable('Image::ExifTool::Real::Metadata');
- $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
- } else {
- $et->Warn('Bad metadata footer');
- }
- if ($raf->Seek(-128, 2) and $raf->Read($buff, 128) == 128 and $buff =~ /^TAG/) {
- $et->VPrint(0, "ID3v1:\n");
- my %dirInfo = (
- DataPt => \$buff,
- DirStart => 0,
- DirLen => length($buff),
- );
- my $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v1');
- $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
- }
- }
- return 1;
- }
- 1; # end
- __END__
- =head1 NAME
- Image::ExifTool::Real - Read Real audio/video meta information
- =head1 SYNOPSIS
- This module is used by Image::ExifTool
- =head1 DESCRIPTION
- This module contains the routines required by Image::ExifTool to read meta
- information in RealAudio (RA), RealMedia (RM, RV and RMVB) and RealMedia
- Metafile (RAM and RPM) files.
- =head1 NOTES
- There must be a bug in the software that wrote the Metadata used in the test
- file t/images/Real.rm because the TrackLyricsDataSize word is written
- little-endian, but the Real format is big-endian.
- =head1 AUTHOR
- Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
- This library is free software; you can redistribute it and/or modify it
- under the same terms as Perl itself.
- =head1 REFERENCES
- =over 4
- =item L<http://www.getid3.org/>
- =item L<https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/htmfiles/rmff.htm>
- =back
- =head1 SEE ALSO
- L<Image::ExifTool::TagNames/Real Tags>,
- L<Image::ExifTool(3pm)|Image::ExifTool>
- =cut
|