Index: main/trunk/greenstone2/perllib/cpan/Image/ExifTool/Flash.pm
===================================================================
--- main/trunk/greenstone2/perllib/cpan/Image/ExifTool/Flash.pm	(revision 20999)
+++ main/trunk/greenstone2/perllib/cpan/Image/ExifTool/Flash.pm	(revision 24107)
@@ -6,8 +6,14 @@
 # Revisions:    05/16/2006 - P. Harvey Created
 #               06/07/2007 - PH Added support for FLV (Flash Video) files
+#               10/23/2008 - PH Added support for XMP in FLV and SWF
 #
 # References:   1) http://www.the-labs.com/MacromediaFlash/SWF-Spec/SWFfileformat.html
 #               2) http://sswf.sourceforge.net/SWFalexref.html
 #               3) http://osflash.org/flv/
+#               4) http://www.irisa.fr/texmex/people/dufouil/ffmpegdoxy/flv_8h.html
+#               5) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf (Oct 2008)
+#               6) http://www.adobe.com/devnet/swf/pdf/swf_file_format_spec_v9.pdf
+#               7) http://help.adobe.com/en_US/FlashMediaServer/3.5_Deving/WS5b3ccc516d4fbf351e63e3d11a0773d56e-7ff6.html
+#               8) http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
 #
 # Notes:        I'll add AMF3 support if someone sends me a FLV with AMF3 data
@@ -21,14 +27,18 @@
 use Image::ExifTool::FLAC;
 
-$VERSION = '1.02';
+$VERSION = '1.09';
 
 sub ProcessMeta($$$;$);
+
+# Meta packets that we process
+my %processMetaPacket = ( onMetaData => 1, onXMPData => 1 );
 
 # information extracted from SWF header
 %Image::ExifTool::Flash::Main = (
     GROUPS => { 2 => 'Video' },
+    VARS => { ALPHA_FIRST => 1 },
     NOTES => q{
-        The information below is extracted from the header of SWF (Shockwave Flash)
-        files.
+        The information below is extracted from SWF (Shockwave Flash) files.  Tags
+        with string ID's represent information extracted from the file header.
     },
     FlashVersion => { },
@@ -40,5 +50,17 @@
     Duration => {
         Notes => 'calculated from FrameRate and FrameCount',
-        PrintConv => 'sprintf("%.2f sec",$val)',
+        PrintConv => 'ConvertDuration($val)',
+    },
+    69 => {
+        Name => 'FileAttributes',
+        PrintConv => { BITMASK => {
+            0 => 'UseNetwork',
+            3 => 'ActionScript3',
+            4 => 'HasMetadata',
+        } },
+    },
+    77 => {
+        Name => 'XMP',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
     },
 );
@@ -74,9 +96,18 @@
         Name => 'AudioEncoding',
         PrintConv => {
-            0 => 'Uncompressed',
+            0 => 'PCM-BE (uncompressed)', # PCM-BE according to ref 4
             1 => 'ADPCM',
             2 => 'MP3',
-            5 => 'Nellymoser 8kHz mono',
+            3 => 'PCM-LE (uncompressed)', #4
+            4 => 'Nellymoser 16kHz Mono', #8
+            5 => 'Nellymoser 8kHz Mono',
             6 => 'Nellymoser',
+            7 => 'G.711 A-law logarithmic PCM', #8
+            8 => 'G.711 mu-law logarithmic PCM', #8
+            # (9 is reserved, ref 8)
+            10 => 'AAC', #8
+            11 => 'Speex', #8
+            13 => 'MP3 8-Khz', #8
+            15 => 'Device-specific sound', #8
         },
     },
@@ -91,5 +122,5 @@
     },
     'Bit6' => {
-        Name => 'AudioSampleBits',
+        Name => 'AudioBitsPerSample',
         ValueConv => '8 * ($val + 1)',
     },
@@ -112,7 +143,11 @@
         Name => 'VideoEncoding',
         PrintConv => {
+            1 => 'JPEG', #8
             2 => 'Sorensen H.263',
-            3 => 'Screen video',
+            3 => 'Screen Video',
             4 => 'On2 VP6',
+            5 => 'On2 VP6 Alpha', #3
+            6 => 'Screen Video 2', #3
+            7 => 'H.264', #7 (called "AVC" by ref 8)
         },
     },
@@ -132,5 +167,5 @@
         Groups => { 2 => 'Audio' },
         ValueConv => '$val * 1000',
-        PrintConv => 'int($val + 0.5)',
+        PrintConv => 'ConvertBitrate($val)',
     },
     'audiodelay'    => { Name => 'AudioDelay',      Groups => { 2 => 'Audio' } },
@@ -138,4 +173,6 @@
     'audiosamplesize'=>{ Name => 'AudioSampleSize', Groups => { 2 => 'Audio' } },
     'audiosize'     => { Name => 'AudioSize',       Groups => { 2 => 'Audio' } },
+    'bytelength'    => 'ByteLength', # (youtube)
+    'canseekontime' => 'CanSeekOnTime', # (youtube)
     'canSeekToEnd'  => 'CanSeekToEnd',
     'creationdate'  => {
@@ -145,4 +182,5 @@
         ValueConv => '$val=~s/\s+$//; $val',    # trim trailing whitespace
     },
+    'createdby'     => 'CreatedBy', #7
     'cuePoints'     => {
         Name => 'CuePoint',
@@ -152,5 +190,5 @@
     'duration' => {
         Name => 'Duration',
-        PrintConv => 'sprintf("%.3fs",$val)',
+        PrintConv => 'ConvertDuration($val)',
     },
     'filesize'      => 'FileSizeBytes',
@@ -165,4 +203,5 @@
     'hasVideo'      => 'HasVideo',
     'height'        => 'ImageHeight',
+    'httphostheader'=> 'HTTPHostHeader', # (youtube)
     'keyframesTimes'=> 'KeyFramesTimes',
     'keyframesFilepositions' => 'KeyFramePositions',
@@ -175,13 +214,35 @@
         PrintConv => '$self->ConvertDateTime($val)',
     },
+    'purl'          => 'URL', # (youtube) (what does P mean?)
+    'pmsg'          => 'Message', # (youtube) (what does P mean?)
+    'sourcedata'    => 'SourceData', # (youtube)
+    'starttime'     => { # (youtube)
+        Name => 'StartTime',
+        PrintConv => 'ConvertDuration($val)',
+    },
     'stereo'        => { Name => 'Stereo',          Groups => { 2 => 'Audio' } },
+    'totalduration' => { # (youtube)
+        Name => 'TotalDuration',
+        PrintConv => 'ConvertDuration($val)',
+    },
+    'totaldatarate' => { # (youtube)
+        Name => 'TotalDataRate',
+        ValueConv => '$val * 1000',
+        PrintConv => 'int($val + 0.5)',
+    },
+    'totalduration' => 'TotalDuration',
     'videocodecid'  => 'VideoCodecID',
     'videodatarate' => {
         Name => 'VideoBitrate',
         ValueConv => '$val * 1000',
-        PrintConv => 'int($val + 0.5)',
+        PrintConv => 'ConvertBitrate($val)',
     },
     'videosize'     => 'VideoSize',
     'width'         => 'ImageWidth',
+    # tags in 'onXMPData' packets
+    'liveXML'       => { #5
+        Name => 'XMP',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
+    },
 );
 
@@ -234,5 +295,4 @@
     my $dirLen = $$dirInfo{DirLen} || length($$dataPt);
     my $pos = $$dirInfo{Pos} || 0;
-    my $verbose = $exifTool->Options('Verbose');
     my ($type, $val, $rec);
 
@@ -312,6 +372,10 @@
                 # switch to subdirectory table if necessary
                 if ($tagInfo and $$tagInfo{SubDirectory}) {
-                    $tag = $$tagInfo{Name}; # use our name for the tag
-                    $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                    my $subTable = $tagInfo->{SubDirectory}->{TagTable};
+                    # descend into Flash SubDirectory
+                    if ($subTable =~ /^Image::ExifTool::Flash::/) {
+                        $tag = $$tagInfo{Name}; # use our name for the tag
+                        $subTablePtr = GetTagTable($subTable);
+                    }
                 }
                 # get object value
@@ -332,5 +396,5 @@
                 if (not $$subTablePtr{$tag} and $tag =~ /^\w+$/) {
                     Image::ExifTool::AddTagToTable($subTablePtr, $tag, { Name => ucfirst($tag) });
-                    $verbose > 1 and $exifTool->VPrint(1, "  | (adding $tag)\n");
+                    $exifTool->VPrint(1, "  | (adding $tag)\n");
                 }
                 $exifTool->HandleTag($subTablePtr, $tag, $v,
@@ -383,9 +447,9 @@
         last if $single;        # all done if extracting single value
         unless ($isStruct{$type}) {
-            # only process "onMetaData" Meta packets
+            # only process certain Meta packets
             if ($type == 0x02 and not $rec) {
-                my $verb = ($val eq 'onMetaData') ? 'processing' : 'ignoring';
+                my $verb = $processMetaPacket{$val} ? 'processing' : 'ignoring';
                 $exifTool->VPrint(0, "  | ($verb $val information)\n");
-                last unless $val eq 'onMetaData';
+                last unless $processMetaPacket{$val};
             } else {
                 # give verbose indication if we ignore a lone value
@@ -477,4 +541,55 @@
 
 #------------------------------------------------------------------------------
+# Read data from possibly compressed file
+# Inputs: 0) RAF reference, 1) data buffer, 2) bytes to read, 2) compressed flag
+# Returns: number of bytes read (may be greater than requested bytes if compressed)
+# - concatenates data to current buffer
+# - updates compressed flag with reference to inflate object for future calls
+#   (or sets to error message and returns zero on error)
+sub ReadCompressed($$$$)
+{
+    my ($raf, $len, $inflate) = ($_[0], $_[2], $_[3]);
+    my $buff;
+    unless ($raf->Read($buff, $len)) {
+        $_[3] = 'Error reading file';
+        return 0;
+    }
+    # uncompress if necessary
+    if ($inflate) {
+        unless (ref $inflate) {
+            unless (eval 'require Compress::Zlib') {
+                $_[3] = 'Install Compress::Zlib to extract compressed information';
+                return 0;
+            }
+            $inflate = Compress::Zlib::inflateInit();
+            unless ($inflate) {
+                $_[3] = 'Error initializing inflate for Flash data';
+                return 0;
+            }
+            $_[3] = $inflate;   # pass inflate object back to caller
+        }
+        my $tmp = $buff;
+        $buff = '';
+        # read 64 more bytes at a time and inflate until we get enough uncompressed data
+        for (;;) {
+            my ($dat, $stat) = $inflate->inflate($tmp);
+            if ($stat == Compress::Zlib::Z_STREAM_END() or
+                $stat == Compress::Zlib::Z_OK())
+            {
+                $buff .= $dat;  # add inflated data to buffer
+                last if length $buff >= $len or $stat == Compress::Zlib::Z_STREAM_END();
+                $raf->Read($tmp,64) or last;    # must read a bit more data
+            } else {
+                $buff = '';
+                last;
+            }
+        }
+        $_[3] = 'Error inflating compressed Flash data' unless length $buff;
+    }
+    $_[1] = defined $_[1] ? $_[1] . $buff : $buff;
+    return length $buff;
+}
+
+#------------------------------------------------------------------------------
 # Read information frame a Flash file
 # Inputs: 0) ExifTool object reference, 1) Directory information reference
@@ -483,7 +598,6 @@
 {
     my ($exifTool, $dirInfo) = @_;
-    my $verbose = $exifTool->Options('Verbose');
     my $raf = $$dirInfo{RAF};
-    my $buff;
+    my ($buff, $hasMeta);
 
     $raf->Read($buff, 8) == 8 or return 0;
@@ -491,7 +605,5 @@
     my ($compressed, $vers) = ($1 eq 'C' ? 1 : 0, ord($2));
 
-    # read the first bit of the file
-    $raf->Read($buff, 64) or return 0;
-
+    SetByteOrder('II');
     $exifTool->SetFileType();
     GetTagTable('Image::ExifTool::Flash::Main');  # make sure table is initialized
@@ -500,40 +612,16 @@
     FoundFlashTag($exifTool, Compressed => $compressed);
 
-    # uncompress if necessary
-    if ($compressed) {
-        unless (eval 'require Compress::Zlib') {
-            $exifTool->Warn('Install Compress::Zlib to extract compressed information');
-            return 1;
-        }
-        my $inflate = Compress::Zlib::inflateInit();
-        my $tmp = $buff;
-        $buff = '';
-        # read file 64 bytes at a time and inflate until we get enough uncompressed data
-        for (;;) {
-            unless ($inflate) {
-                $exifTool->Warn('Error inflating compressed Flash data');
-                return 1;
-            }
-            my ($dat, $stat) = $inflate->inflate($tmp);
-            if ($stat == Compress::Zlib::Z_STREAM_END() or
-                $stat == Compress::Zlib::Z_OK())
-            {
-                $buff .= $dat;  # add inflated data to buffer
-                last if length $buff >= 64 or $stat == Compress::Zlib::Z_STREAM_END();
-                $raf->Read($tmp,64) or last;    # read some more data
-            } else {
-                undef $inflate; # issue warning the next time around
-            }
-        }
+    # read the next 64 bytes of the file (and inflate if necessary)
+    $buff = '';
+    unless (ReadCompressed($raf, $buff, 64, $compressed)) {
+        $exifTool->Warn($compressed) if $compressed;
+        return 1;
     }
+
     # unpack elements of bit-packed Flash Rect structure
-    my ($nBits, $totBits, $nBytes);
-    for (;;) {
-        if (length($buff)) {
-            $nBits = unpack('C', $buff) >> 3;    # bits in x1,x2,y1,y2 elements
-            $totBits = 5 + $nBits * 4;           # total bits in Rect structure
-            $nBytes = int(($totBits + 7) / 8);   # byte length of Rect structure
-            last if length $buff >= $nBytes + 4; # make sure header is long enough
-        }
+    my $nBits = unpack('C', $buff) >> 3;    # bits in x1,x2,y1,y2 elements
+    my $totBits = 5 + $nBits * 4;           # total bits in Rect structure
+    my $nBytes = int(($totBits + 7) / 8);   # byte length of Rect structure
+    if (length $buff < $nBytes + 4) {
         $exifTool->Warn('Truncated Flash file');
         return 1;
@@ -555,4 +643,55 @@
     FoundFlashTag($exifTool, Duration => $vals[1] * 256 / $vals[0]) if $vals[0];
 
+    # scan through the tags to find FileAttributes and XMP
+    $buff = substr($buff, $nBytes + 4);
+    for (;;) {
+        my $buffLen = length $buff;
+        last if $buffLen < 2;
+        my $code = Get16u(\$buff, 0);
+        my $pos = 2;
+        my $tag = $code >> 6;
+        my $size = $code & 0x3f;
+        $exifTool->VPrint(1, "SWF tag $tag ($size bytes):\n");
+        last unless $tag == 69 or $tag == 77 or $hasMeta;
+        # read enough to get a complete short record
+        if ($pos + $size > $buffLen) {
+            # (read 2 extra bytes if available to get next tag word)
+            unless (ReadCompressed($raf, $buff, $size + 2, $compressed)) {
+                $exifTool->Warn($compressed) if $compressed;
+                return 1;
+            }
+            $buffLen = length $buff;
+            last if $pos + $size > $buffLen;
+        }
+        # read extended record if necessary
+        if ($size == 0x3f) {
+            last if $pos + 4 > $buffLen;
+            $size = Get32u(\$buff, $pos);
+            $pos += 4;
+            last if $size > 1000000; # don't read anything huge
+            if ($pos + $size > $buffLen) {
+                unless (ReadCompressed($raf, $buff, $size + 2, $compressed)) {
+                    $exifTool->Warn($compressed) if $compressed;
+                    return 1;
+                }
+                $buffLen = length $buff;
+                last if $pos + $size > $buffLen;
+            }
+            $exifTool->VPrint(1, "  [extended size $size bytes]\n");
+        }
+        if ($tag == 69) {       # FileAttributes
+            last unless $size;
+            my $flags = Get8u(\$buff, $pos);
+            FoundFlashTag($exifTool, $tag => $flags);
+            last unless $flags & 0x10;  # only continue if we have metadata (XMP)
+            $hasMeta = 1;
+        } elsif ($tag == 77) {  # Metadata
+            my $val = substr($buff, $pos, $size);
+            FoundFlashTag($exifTool, $tag => $val);
+            last;
+        }
+        last if $pos + 2 > $buffLen;
+        $buff = substr($buff, $pos);    # remove everything before the next tag
+    }
     return 1;
 }
@@ -583,5 +722,5 @@
 =head1 AUTHOR
 
-Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
 
 This library is free software; you can redistribute it and/or modify it
@@ -598,4 +737,12 @@
 =item L<http://osflash.org/flv/>
 
+=item L<http://www.irisa.fr/texmex/people/dufouil/ffmpegdoxy/flv_8h.html>
+
+=item L<http://help.adobe.com/en_US/FlashMediaServer/3.5_Deving/WS5b3ccc516d4fbf351e63e3d11a0773d56e-7ff6.html>
+
+=item L<http://www.adobe.com/devnet/swf/pdf/swf_file_format_spec_v9.pdf>
+
+=item L<http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf>
+
 =back
 
