313
314
# GEOB => 'GeneralEncapsulatedObject',
314
315
# GRID => 'GroupIdentification',
315
316
# LINK => 'LinkedInformation',
316
# MCDI => 'MusicCDIdentifier',
317
MCDI => { Name => 'MusicCDIdentifier', Binary => 1 },
317
318
# MLLT => 'MPEGLocationLookupTable',
318
319
# OWNE => 'Ownership', # enc(1), _price, 00, _date(8), Seller
319
320
PCNT => 'PlayCounter',
320
321
# POPM => 'Popularimeter', # _email, 00, rating(1), counter(4-N)
321
322
# POSS => 'PostSynchronization',
325
SubDirectory => { TagTable => 'Image::ExifTool::ID3::Private' },
323
327
# RBUF => 'RecommendedBufferSize',
324
328
# RVRB => 'Reverb',
325
329
# SYLT => 'SynchronizedLyricText',
421
425
TSST => 'SetSubtitle',
428
# ID3 PRIV tags (ref PH)
429
%Image::ExifTool::ID3::Private = (
430
PROCESS_PROC => \&Image::ExifTool::ID3::ProcessPrivate,
431
GROUPS => { 1 => 'ID3', 2 => 'Audio' },
432
NOTES => 'ID3 private (PRIV) tags.',
436
TagTable => 'Image::ExifTool::XMP::Main',
440
ValueConv => 'length($val)==4 ? unpack("V",$val) : \$val',
443
ValueConv => 'length($val)==4 ? unpack("V",$val) : \$val',
424
447
# can't share tagInfo hashes between two tables, so we must make
425
448
# copies of the necessary hashes
435
458
#------------------------------------------------------------------------------
459
# Process ID3 PRIV data
460
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
461
sub ProcessPrivate($$$)
463
my ($exifTool, $dirInfo, $tagTablePtr) = @_;
464
my $dataPt = $$dirInfo{DataPt};
466
if ($$dataPt =~ /^(.*?)\0/) {
468
$start = length($tag) + 1;
473
unless ($$tagTablePtr{$tag}) {
474
$tag =~ tr{/ }{_}d; # translate '/' to '_' and remove spaces
475
$tag = 'private' unless $tag =~ /^[-\w]{1,24}$/;
476
unless ($$tagTablePtr{$tag}) {
477
Image::ExifTool::AddTagToTable($tagTablePtr, $tag,
478
{ Name => ucfirst($tag), Binary => 1 });
481
my $key = $exifTool->HandleTag($tagTablePtr, $tag, undef,
482
Size => length($$dataPt) - $start,
487
$exifTool->SetGroup1($key, $$exifTool{ID3_Ver}) if $key;
490
#------------------------------------------------------------------------------
436
491
# Print ID3v2 Genre
437
492
# Inputs: TCON or TCO frame data
438
493
# Returns: Content type with decoded genre numbers
464
519
return '' unless length $val;
465
520
my $enc = unpack('C', $val);
466
521
$val = substr($val, 1); # remove encoding byte
467
$val =~ s/\0+$//; # remove null padding if it exists
469
523
if ($enc == 0) { # ISO 8859-1
524
$val =~ s/\0+$//; # remove any null padding
470
525
$val = $exifTool->Latin2Charset($val);
471
526
@vals = split "\0", $val;
472
527
} elsif ($enc == 3) { # UTF-8
473
529
$val = $exifTool->UTF82Charset($val);
474
530
@vals = split "\0", $val;
475
531
} elsif ($enc == 1 or $enc == 2) { # UTF-16 with BOM, or UTF-16BE
476
my $bom = $val =~ s/^(\xfe\xff|\xff\xfe)// ? $1 : "\xfe\xff";
532
my $bom = "\xfe\xff";
477
533
my %order = ( "\xfe\xff" => 'MM', "\xff\xfe", => 'II' );
478
@vals = split "\0\0", $val;
479
foreach $val (@vals) {
480
$val =~ s/^$bom//; # remove BOM if it exists
481
$val = $exifTool->Unicode2Charset($val, $order{$bom});
536
# split string at null terminators on word boundaries
537
if ($val =~ s/((..)*?)\0\0//) {
540
last unless length $val > 1;
544
$bom = $1 if $v =~ s/^(\xfe\xff|\xff\xfe)//;
545
push @vals, $exifTool->Unicode2Charset($v, $order{$bom});
484
549
return "<Unknown encoding $enc> $val";
486
551
return @vals if wantarray;
501
566
#------------------------------------------------------------------------------
502
567
# Process ID3v2 information
503
# Inputs: 0) ExifTool object reference, 1) directory information reference
504
# 2) tag table reference
568
# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
505
569
sub ProcessID3v2($$$)
507
571
my ($exifTool, $dirInfo, $tagTablePtr) = @_;
534
598
last if $offset + $len > $size;
535
599
my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $id);
536
next unless $tagInfo or $verbose;
601
next unless $verbose or $exifTool->Options('Unknown');
602
$id =~ tr/-A-Za-z0-9_//dc;
603
$id = 'unknown' unless length $id;
604
unless ($$tagTablePtr{$id}) {
605
$tagInfo = { Name => "ID3_$id", Binary => 1 };
606
Image::ExifTool::AddTagToTable($tagTablePtr, $id, $tagInfo);
538
609
# decode v2.3 and v2.4 flags
603
674
# decode data in this frame
605
676
my $valLen = length($val); # actual value length (after decompression, etc)
606
if ($id =~ /^T[^X]/ or $id =~ /^(IPL|IPLS)$/) {
607
$val = DecodeString($exifTool, $val);
608
} elsif ($id =~ /^(TXX|TXXX|WXX|WXXX)$/) {
677
if ($id =~ /^(TXX|TXXX)$/) {
678
# two encoded strings separated by a null
609
679
my @vals = DecodeString($exifTool, $val);
610
680
foreach (0..1) { $vals[$_] = '' unless defined $vals[$_]; }
611
681
($val = "($vals[0]) $vals[1]") =~ s/^\(\) //;
682
} elsif ($id =~ /^T/ or $id =~ /^(IPL|IPLS)$/) {
683
$val = DecodeString($exifTool, $val);
684
} elsif ($id =~ /^(WXX|WXXX)$/) {
685
# one encoded string and one Latin string separated by a null
686
my $enc = unpack('C', $val);
688
if ($enc == 1 or $enc == 2) {
689
($val, $url) = ($val =~ /^(.(?:..)*?)\0\0(.*)/);
691
($val, $url) = ($val =~ /^(..*?)\0(.*)/);
693
unless (defined $val and defined $url) {
694
$exifTool->Warn("Invalid $id frame value");
697
$val = DecodeString($exifTool, $val);
699
$val = length($val) ? "($val) $url" : $url;
700
} elsif ($id =~ /^W/) {
701
$val =~ s/\0.*//; # truncate at null
612
702
} elsif ($id =~ /^(COM|COMM|ULT|USLT)$/) {
613
703
$valLen > 4 or $exifTool->Warn("Short $id frame"), next;
614
704
substr($val, 1, 3) = ''; # remove language code
615
705
my @vals = DecodeString($exifTool, $val);
616
706
foreach (0..1) { $vals[$_] = '' unless defined $vals[$_]; }
617
($val = "($vals[0]) $vals[1]") =~ s/^\(\) //;
707
$val = length($vals[0]) ? "($vals[0]) $vals[1]" : $vals[1];
618
708
} elsif ($id eq 'USER') {
619
709
$valLen > 4 or $exifTool->Warn('Short USER frame'), next;
620
710
substr($val, 1, 3) = ''; # remove language code
634
724
# remove header (encoding, image format or MIME type, picture type, description)
635
725
$val =~ s/$hdr//s or $exifTool->Warn("Invalid $id frame"), next;
636
726
$enc and $val =~ s/^\0//; # remove 2nd terminator if Unicode encoding
727
} elsif ($id eq 'PRIV') {
728
# save version number to set group1 name for tag later
729
$exifTool->{ID3_Ver} = $tagTablePtr->{GROUPS}->{1};
730
$exifTool->HandleTag($tagTablePtr, $id, $val);
732
} elsif (not $$tagInfo{Binary}) {
638
733
$exifTool->Warn("Don't know how to handle $id frame");