- Timestamp:
- 2011-06-01T12:33:42+12:00 (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone2/perllib/cpan/Image/ExifTool/Flash.pm
r16842 r24107 6 6 # Revisions: 05/16/2006 - P. Harvey Created 7 7 # 06/07/2007 - PH Added support for FLV (Flash Video) files 8 # 10/23/2008 - PH Added support for XMP in FLV and SWF 8 9 # 9 10 # References: 1) http://www.the-labs.com/MacromediaFlash/SWF-Spec/SWFfileformat.html 10 11 # 2) http://sswf.sourceforge.net/SWFalexref.html 11 12 # 3) http://osflash.org/flv/ 13 # 4) http://www.irisa.fr/texmex/people/dufouil/ffmpegdoxy/flv_8h.html 14 # 5) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf (Oct 2008) 15 # 6) http://www.adobe.com/devnet/swf/pdf/swf_file_format_spec_v9.pdf 16 # 7) http://help.adobe.com/en_US/FlashMediaServer/3.5_Deving/WS5b3ccc516d4fbf351e63e3d11a0773d56e-7ff6.html 17 # 8) http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf 12 18 # 13 19 # Notes: I'll add AMF3 support if someone sends me a FLV with AMF3 data … … 21 27 use Image::ExifTool::FLAC; 22 28 23 $VERSION = '1.0 2';29 $VERSION = '1.09'; 24 30 25 31 sub ProcessMeta($$$;$); 32 33 # Meta packets that we process 34 my %processMetaPacket = ( onMetaData => 1, onXMPData => 1 ); 26 35 27 36 # information extracted from SWF header 28 37 %Image::ExifTool::Flash::Main = ( 29 38 GROUPS => { 2 => 'Video' }, 39 VARS => { ALPHA_FIRST => 1 }, 30 40 NOTES => q{ 31 The information below is extracted from the header of SWF (Shockwave Flash)32 files.41 The information below is extracted from SWF (Shockwave Flash) files. Tags 42 with string ID's represent information extracted from the file header. 33 43 }, 34 44 FlashVersion => { }, … … 40 50 Duration => { 41 51 Notes => 'calculated from FrameRate and FrameCount', 42 PrintConv => 'sprintf("%.2f sec",$val)', 52 PrintConv => 'ConvertDuration($val)', 53 }, 54 69 => { 55 Name => 'FileAttributes', 56 PrintConv => { BITMASK => { 57 0 => 'UseNetwork', 58 3 => 'ActionScript3', 59 4 => 'HasMetadata', 60 } }, 61 }, 62 77 => { 63 Name => 'XMP', 64 SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' }, 43 65 }, 44 66 ); … … 74 96 Name => 'AudioEncoding', 75 97 PrintConv => { 76 0 => ' Uncompressed',98 0 => 'PCM-BE (uncompressed)', # PCM-BE according to ref 4 77 99 1 => 'ADPCM', 78 100 2 => 'MP3', 79 5 => 'Nellymoser 8kHz mono', 101 3 => 'PCM-LE (uncompressed)', #4 102 4 => 'Nellymoser 16kHz Mono', #8 103 5 => 'Nellymoser 8kHz Mono', 80 104 6 => 'Nellymoser', 105 7 => 'G.711 A-law logarithmic PCM', #8 106 8 => 'G.711 mu-law logarithmic PCM', #8 107 # (9 is reserved, ref 8) 108 10 => 'AAC', #8 109 11 => 'Speex', #8 110 13 => 'MP3 8-Khz', #8 111 15 => 'Device-specific sound', #8 81 112 }, 82 113 }, … … 91 122 }, 92 123 'Bit6' => { 93 Name => 'Audio SampleBits',124 Name => 'AudioBitsPerSample', 94 125 ValueConv => '8 * ($val + 1)', 95 126 }, … … 112 143 Name => 'VideoEncoding', 113 144 PrintConv => { 145 1 => 'JPEG', #8 114 146 2 => 'Sorensen H.263', 115 3 => 'Screen video',147 3 => 'Screen Video', 116 148 4 => 'On2 VP6', 149 5 => 'On2 VP6 Alpha', #3 150 6 => 'Screen Video 2', #3 151 7 => 'H.264', #7 (called "AVC" by ref 8) 117 152 }, 118 153 }, … … 132 167 Groups => { 2 => 'Audio' }, 133 168 ValueConv => '$val * 1000', 134 PrintConv => ' int($val + 0.5)',169 PrintConv => 'ConvertBitrate($val)', 135 170 }, 136 171 'audiodelay' => { Name => 'AudioDelay', Groups => { 2 => 'Audio' } }, … … 138 173 'audiosamplesize'=>{ Name => 'AudioSampleSize', Groups => { 2 => 'Audio' } }, 139 174 'audiosize' => { Name => 'AudioSize', Groups => { 2 => 'Audio' } }, 175 'bytelength' => 'ByteLength', # (youtube) 176 'canseekontime' => 'CanSeekOnTime', # (youtube) 140 177 'canSeekToEnd' => 'CanSeekToEnd', 141 178 'creationdate' => { … … 145 182 ValueConv => '$val=~s/\s+$//; $val', # trim trailing whitespace 146 183 }, 184 'createdby' => 'CreatedBy', #7 147 185 'cuePoints' => { 148 186 Name => 'CuePoint', … … 152 190 'duration' => { 153 191 Name => 'Duration', 154 PrintConv => ' sprintf("%.3fs",$val)',192 PrintConv => 'ConvertDuration($val)', 155 193 }, 156 194 'filesize' => 'FileSizeBytes', … … 165 203 'hasVideo' => 'HasVideo', 166 204 'height' => 'ImageHeight', 205 'httphostheader'=> 'HTTPHostHeader', # (youtube) 167 206 'keyframesTimes'=> 'KeyFramesTimes', 168 207 'keyframesFilepositions' => 'KeyFramePositions', … … 175 214 PrintConv => '$self->ConvertDateTime($val)', 176 215 }, 216 'purl' => 'URL', # (youtube) (what does P mean?) 217 'pmsg' => 'Message', # (youtube) (what does P mean?) 218 'sourcedata' => 'SourceData', # (youtube) 219 'starttime' => { # (youtube) 220 Name => 'StartTime', 221 PrintConv => 'ConvertDuration($val)', 222 }, 177 223 'stereo' => { Name => 'Stereo', Groups => { 2 => 'Audio' } }, 224 'totalduration' => { # (youtube) 225 Name => 'TotalDuration', 226 PrintConv => 'ConvertDuration($val)', 227 }, 228 'totaldatarate' => { # (youtube) 229 Name => 'TotalDataRate', 230 ValueConv => '$val * 1000', 231 PrintConv => 'int($val + 0.5)', 232 }, 233 'totalduration' => 'TotalDuration', 178 234 'videocodecid' => 'VideoCodecID', 179 235 'videodatarate' => { 180 236 Name => 'VideoBitrate', 181 237 ValueConv => '$val * 1000', 182 PrintConv => ' int($val + 0.5)',238 PrintConv => 'ConvertBitrate($val)', 183 239 }, 184 240 'videosize' => 'VideoSize', 185 241 'width' => 'ImageWidth', 242 # tags in 'onXMPData' packets 243 'liveXML' => { #5 244 Name => 'XMP', 245 SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' }, 246 }, 186 247 ); 187 248 … … 234 295 my $dirLen = $$dirInfo{DirLen} || length($$dataPt); 235 296 my $pos = $$dirInfo{Pos} || 0; 236 my $verbose = $exifTool->Options('Verbose');237 297 my ($type, $val, $rec); 238 298 … … 312 372 # switch to subdirectory table if necessary 313 373 if ($tagInfo and $$tagInfo{SubDirectory}) { 314 $tag = $$tagInfo{Name}; # use our name for the tag 315 $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable}); 374 my $subTable = $tagInfo->{SubDirectory}->{TagTable}; 375 # descend into Flash SubDirectory 376 if ($subTable =~ /^Image::ExifTool::Flash::/) { 377 $tag = $$tagInfo{Name}; # use our name for the tag 378 $subTablePtr = GetTagTable($subTable); 379 } 316 380 } 317 381 # get object value … … 332 396 if (not $$subTablePtr{$tag} and $tag =~ /^\w+$/) { 333 397 Image::ExifTool::AddTagToTable($subTablePtr, $tag, { Name => ucfirst($tag) }); 334 $ verbose > 1 and $exifTool->VPrint(1, " | (adding $tag)\n");398 $exifTool->VPrint(1, " | (adding $tag)\n"); 335 399 } 336 400 $exifTool->HandleTag($subTablePtr, $tag, $v, … … 383 447 last if $single; # all done if extracting single value 384 448 unless ($isStruct{$type}) { 385 # only process "onMetaData"Meta packets449 # only process certain Meta packets 386 450 if ($type == 0x02 and not $rec) { 387 my $verb = ($val eq 'onMetaData')? 'processing' : 'ignoring';451 my $verb = $processMetaPacket{$val} ? 'processing' : 'ignoring'; 388 452 $exifTool->VPrint(0, " | ($verb $val information)\n"); 389 last unless $ val eq 'onMetaData';453 last unless $processMetaPacket{$val}; 390 454 } else { 391 455 # give verbose indication if we ignore a lone value … … 477 541 478 542 #------------------------------------------------------------------------------ 543 # Read data from possibly compressed file 544 # Inputs: 0) RAF reference, 1) data buffer, 2) bytes to read, 2) compressed flag 545 # Returns: number of bytes read (may be greater than requested bytes if compressed) 546 # - concatenates data to current buffer 547 # - updates compressed flag with reference to inflate object for future calls 548 # (or sets to error message and returns zero on error) 549 sub ReadCompressed($$$$) 550 { 551 my ($raf, $len, $inflate) = ($_[0], $_[2], $_[3]); 552 my $buff; 553 unless ($raf->Read($buff, $len)) { 554 $_[3] = 'Error reading file'; 555 return 0; 556 } 557 # uncompress if necessary 558 if ($inflate) { 559 unless (ref $inflate) { 560 unless (eval 'require Compress::Zlib') { 561 $_[3] = 'Install Compress::Zlib to extract compressed information'; 562 return 0; 563 } 564 $inflate = Compress::Zlib::inflateInit(); 565 unless ($inflate) { 566 $_[3] = 'Error initializing inflate for Flash data'; 567 return 0; 568 } 569 $_[3] = $inflate; # pass inflate object back to caller 570 } 571 my $tmp = $buff; 572 $buff = ''; 573 # read 64 more bytes at a time and inflate until we get enough uncompressed data 574 for (;;) { 575 my ($dat, $stat) = $inflate->inflate($tmp); 576 if ($stat == Compress::Zlib::Z_STREAM_END() or 577 $stat == Compress::Zlib::Z_OK()) 578 { 579 $buff .= $dat; # add inflated data to buffer 580 last if length $buff >= $len or $stat == Compress::Zlib::Z_STREAM_END(); 581 $raf->Read($tmp,64) or last; # must read a bit more data 582 } else { 583 $buff = ''; 584 last; 585 } 586 } 587 $_[3] = 'Error inflating compressed Flash data' unless length $buff; 588 } 589 $_[1] = defined $_[1] ? $_[1] . $buff : $buff; 590 return length $buff; 591 } 592 593 #------------------------------------------------------------------------------ 479 594 # Read information frame a Flash file 480 595 # Inputs: 0) ExifTool object reference, 1) Directory information reference … … 483 598 { 484 599 my ($exifTool, $dirInfo) = @_; 485 my $verbose = $exifTool->Options('Verbose');486 600 my $raf = $$dirInfo{RAF}; 487 my $buff;601 my ($buff, $hasMeta); 488 602 489 603 $raf->Read($buff, 8) == 8 or return 0; … … 491 605 my ($compressed, $vers) = ($1 eq 'C' ? 1 : 0, ord($2)); 492 606 493 # read the first bit of the file 494 $raf->Read($buff, 64) or return 0; 495 607 SetByteOrder('II'); 496 608 $exifTool->SetFileType(); 497 609 GetTagTable('Image::ExifTool::Flash::Main'); # make sure table is initialized … … 500 612 FoundFlashTag($exifTool, Compressed => $compressed); 501 613 502 # uncompress if necessary 503 if ($compressed) { 504 unless (eval 'require Compress::Zlib') { 505 $exifTool->Warn('Install Compress::Zlib to extract compressed information'); 506 return 1; 507 } 508 my $inflate = Compress::Zlib::inflateInit(); 509 my $tmp = $buff; 510 $buff = ''; 511 # read file 64 bytes at a time and inflate until we get enough uncompressed data 512 for (;;) { 513 unless ($inflate) { 514 $exifTool->Warn('Error inflating compressed Flash data'); 515 return 1; 516 } 517 my ($dat, $stat) = $inflate->inflate($tmp); 518 if ($stat == Compress::Zlib::Z_STREAM_END() or 519 $stat == Compress::Zlib::Z_OK()) 520 { 521 $buff .= $dat; # add inflated data to buffer 522 last if length $buff >= 64 or $stat == Compress::Zlib::Z_STREAM_END(); 523 $raf->Read($tmp,64) or last; # read some more data 524 } else { 525 undef $inflate; # issue warning the next time around 526 } 527 } 614 # read the next 64 bytes of the file (and inflate if necessary) 615 $buff = ''; 616 unless (ReadCompressed($raf, $buff, 64, $compressed)) { 617 $exifTool->Warn($compressed) if $compressed; 618 return 1; 528 619 } 620 529 621 # unpack elements of bit-packed Flash Rect structure 530 my ($nBits, $totBits, $nBytes); 531 for (;;) { 532 if (length($buff)) { 533 $nBits = unpack('C', $buff) >> 3; # bits in x1,x2,y1,y2 elements 534 $totBits = 5 + $nBits * 4; # total bits in Rect structure 535 $nBytes = int(($totBits + 7) / 8); # byte length of Rect structure 536 last if length $buff >= $nBytes + 4; # make sure header is long enough 537 } 622 my $nBits = unpack('C', $buff) >> 3; # bits in x1,x2,y1,y2 elements 623 my $totBits = 5 + $nBits * 4; # total bits in Rect structure 624 my $nBytes = int(($totBits + 7) / 8); # byte length of Rect structure 625 if (length $buff < $nBytes + 4) { 538 626 $exifTool->Warn('Truncated Flash file'); 539 627 return 1; … … 555 643 FoundFlashTag($exifTool, Duration => $vals[1] * 256 / $vals[0]) if $vals[0]; 556 644 645 # scan through the tags to find FileAttributes and XMP 646 $buff = substr($buff, $nBytes + 4); 647 for (;;) { 648 my $buffLen = length $buff; 649 last if $buffLen < 2; 650 my $code = Get16u(\$buff, 0); 651 my $pos = 2; 652 my $tag = $code >> 6; 653 my $size = $code & 0x3f; 654 $exifTool->VPrint(1, "SWF tag $tag ($size bytes):\n"); 655 last unless $tag == 69 or $tag == 77 or $hasMeta; 656 # read enough to get a complete short record 657 if ($pos + $size > $buffLen) { 658 # (read 2 extra bytes if available to get next tag word) 659 unless (ReadCompressed($raf, $buff, $size + 2, $compressed)) { 660 $exifTool->Warn($compressed) if $compressed; 661 return 1; 662 } 663 $buffLen = length $buff; 664 last if $pos + $size > $buffLen; 665 } 666 # read extended record if necessary 667 if ($size == 0x3f) { 668 last if $pos + 4 > $buffLen; 669 $size = Get32u(\$buff, $pos); 670 $pos += 4; 671 last if $size > 1000000; # don't read anything huge 672 if ($pos + $size > $buffLen) { 673 unless (ReadCompressed($raf, $buff, $size + 2, $compressed)) { 674 $exifTool->Warn($compressed) if $compressed; 675 return 1; 676 } 677 $buffLen = length $buff; 678 last if $pos + $size > $buffLen; 679 } 680 $exifTool->VPrint(1, " [extended size $size bytes]\n"); 681 } 682 if ($tag == 69) { # FileAttributes 683 last unless $size; 684 my $flags = Get8u(\$buff, $pos); 685 FoundFlashTag($exifTool, $tag => $flags); 686 last unless $flags & 0x10; # only continue if we have metadata (XMP) 687 $hasMeta = 1; 688 } elsif ($tag == 77) { # Metadata 689 my $val = substr($buff, $pos, $size); 690 FoundFlashTag($exifTool, $tag => $val); 691 last; 692 } 693 last if $pos + 2 > $buffLen; 694 $buff = substr($buff, $pos); # remove everything before the next tag 695 } 557 696 return 1; 558 697 } … … 583 722 =head1 AUTHOR 584 723 585 Copyright 2003-20 07, Phil Harvey (phil at owl.phy.queensu.ca)724 Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca) 586 725 587 726 This library is free software; you can redistribute it and/or modify it … … 598 737 =item L<http://osflash.org/flv/> 599 738 739 =item L<http://www.irisa.fr/texmex/people/dufouil/ffmpegdoxy/flv_8h.html> 740 741 =item L<http://help.adobe.com/en_US/FlashMediaServer/3.5_Deving/WS5b3ccc516d4fbf351e63e3d11a0773d56e-7ff6.html> 742 743 =item L<http://www.adobe.com/devnet/swf/pdf/swf_file_format_spec_v9.pdf> 744 745 =item L<http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf> 746 600 747 =back 601 748
Note:
See TracChangeset
for help on using the changeset viewer.