File Coverage

blib/lib/Image/ExifTool/DV.pm
Criterion Covered Total %
statement 85 90 94.4
branch 30 50 60.0
condition 11 27 40.7
subroutine 4 4 100.0
pod 0 1 0.0
total 130 172 75.5


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: DV.pm
3             #
4             # Description: Read DV meta information
5             #
6             # Revisions: 2010/12/24 - P. Harvey Created
7             #
8             # References: 1) http://www.ffmpeg.org/
9             # 2) http://dvswitch.alioth.debian.org/wiki/DV_format/
10             #------------------------------------------------------------------------------
11              
12             package Image::ExifTool::DV;
13              
14 2     2   4494 use strict;
  2         6  
  2         79  
15 2     2   10 use vars qw($VERSION);
  2         3  
  2         90  
16 2     2   12 use Image::ExifTool qw(:DataAccess :Utils);
  2         5  
  2         2850  
17              
18             $VERSION = '1.02';
19              
20             # DV profiles (ref 1)
21             my @dvProfiles = (
22             {
23             DSF => 0,
24             VideoSType => 0x0,
25             FrameSize => 120000,
26             VideoFormat => 'IEC 61834, SMPTE-314M - 525/60 (NTSC)',
27             Colorimetry => '4:1:1',
28             FrameRate => 30000/1001,
29             ImageHeight => 480,
30             ImageWidth => 720,
31             },{
32             DSF => 1,
33             VideoSType => 0x0,
34             FrameSize => 144000,
35             VideoFormat => 'IEC 61834 - 625/50 (PAL)',
36             Colorimetry => '4:2:0',
37             FrameRate => 25/1,
38             ImageHeight => 576,
39             ImageWidth => 720,
40             },{
41             DSF => 1,
42             VideoSType => 0x0,
43             FrameSize => 144000,
44             VideoFormat => 'SMPTE-314M - 625/50 (PAL)',
45             Colorimetry => '4:1:1',
46             FrameRate => 25/1,
47             ImageHeight => 576,
48             ImageWidth => 720,
49             },{
50             DSF => 0,
51             VideoSType => 0x4,
52             FrameSize => 240000,
53             VideoFormat => 'DVCPRO50: SMPTE-314M - 525/60 (NTSC) 50 Mbps',
54             Colorimetry => '4:2:2',
55             FrameRate => 30000/1001,
56             ImageHeight => 480,
57             ImageWidth => 720,
58             },{
59             DSF => 1,
60             VideoSType => 0x4,
61             FrameSize => 288000,
62             VideoFormat => 'DVCPRO50: SMPTE-314M - 625/50 (PAL) 50 Mbps',
63             Colorimetry => '4:2:2',
64             FrameRate => 25/1,
65             ImageHeight => 576,
66             ImageWidth => 720,
67             },{
68             DSF => 0,
69             VideoSType => 0x14,
70             FrameSize => 480000,
71             VideoFormat => 'DVCPRO HD: SMPTE-370M - 1080i60 100 Mbps',
72             Colorimetry => '4:2:2',
73             FrameRate => 30000/1001,
74             ImageHeight => 1080,
75             ImageWidth => 1280,
76             },{
77             DSF => 1,
78             VideoSType => 0x14,
79             FrameSize => 576000,
80             VideoFormat => 'DVCPRO HD: SMPTE-370M - 1080i50 100 Mbps',
81             Colorimetry => '4:2:2',
82             FrameRate => 25/1,
83             ImageHeight => 1080,
84             ImageWidth => 1440,
85             },{
86             DSF => 0,
87             VideoSType => 0x18,
88             FrameSize => 240000,
89             VideoFormat => 'DVCPRO HD: SMPTE-370M - 720p60 100 Mbps',
90             Colorimetry => '4:2:2',
91             FrameRate => 60000/1001,
92             ImageHeight => 720,
93             ImageWidth => 960,
94             },{
95             DSF => 1,
96             VideoSType => 0x18,
97             FrameSize => 288000,
98             VideoFormat => 'DVCPRO HD: SMPTE-370M - 720p50 100 Mbps',
99             Colorimetry => '4:2:2',
100             FrameRate => 50/1,
101             ImageHeight => 720,
102             ImageWidth => 960,
103             },{
104             DSF => 1,
105             VideoSType => 0x1,
106             FrameSize => 144000,
107             VideoFormat => 'IEC 61883-5 - 625/50 (PAL)',
108             Colorimetry => '4:2:0',
109             FrameRate => 25/1,
110             ImageHeight => 576,
111             ImageWidth => 720,
112             },
113             );
114              
115             # tags to extract, in the order we want to extract them
116             my @dvTags = (
117             'DateTimeOriginal', 'ImageWidth', 'ImageHeight', 'Duration',
118             'TotalBitrate', 'VideoFormat', 'VideoScanType', 'FrameRate',
119             'AspectRatio', 'Colorimetry', 'AudioChannels', 'AudioSampleRate',
120             'AudioBitsPerSample',
121             );
122              
123             # DV tags
124             %Image::ExifTool::DV::Main = (
125             GROUPS => { 2 => 'Video' },
126             VARS => { NO_ID => 1 },
127             NOTES => 'The following tags are extracted from DV videos.',
128             DateTimeOriginal => {
129             Description => 'Date/Time Original',
130             Groups => { 2 => 'Time' },
131             PrintConv => '$self->ConvertDateTime($val)',
132             },
133             ImageWidth => { },
134             ImageHeight => { },
135             Duration => { PrintConv => 'ConvertDuration($val)' },
136             TotalBitrate => { PrintConv => 'ConvertBitrate($val)' },
137             VideoFormat => { },
138             VideoScanType => { },
139             FrameRate => { PrintConv => 'int($val * 1000 + 0.5) / 1000' },
140             AspectRatio => { },
141             Colorimetry => { },
142             AudioChannels => { Groups => { 2 => 'Audio' } },
143             AudioSampleRate => { Groups => { 2 => 'Audio' } },
144             AudioBitsPerSample => { Groups => { 2 => 'Audio' } },
145             );
146              
147             #------------------------------------------------------------------------------
148             # Read information in a DV file (ref 1)
149             # Inputs: 0) ExifTool ref, 1) dirInfo ref
150             # Returns: 1 on success, 0 if this wasn't a valid DV file
151             sub ProcessDV($$)
152             {
153 2     2 0 7 my ($et, $dirInfo) = @_;
154 2         4 local $_;
155 2         6 my $raf = $$dirInfo{RAF};
156 2         5 my ($buff, $start, $profile, $tag, $i, $j);
157              
158 2 50       7 $raf->Read($buff, 12000) or return 0;
159 2 100       22 if ($buff =~ /\x1f\x07\0[\x3f\xbf]/sg) {
160 1         4 $start = pos($buff) - 4;
161             } else {
162 1         5 while ($buff =~ /[\0\xff]\x3f\x07\0.{76}\xff\x3f\x07\x01/sg) {
163 0 0       0 next if pos($buff) - 163 < 0;
164 0         0 $start = pos($buff) - 163;
165 0         0 last;
166             }
167 1 50       6 return 0 unless defined $start;
168             }
169 1         2 my $len = length $buff;
170             # must at least have a full DIF header
171 1 50       4 return 0 if $start + 80 * 6 > $len;
172              
173 1         8 $et->SetFileType();
174              
175 1         3 my $pos = $start;
176 1         8 my $dsf = (Get8u(\$buff, $pos + 3) & 0x80) >> 7;
177 1         6 my $stype = Get8u(\$buff, $pos + 80*5 + 48 + 3) & 0x1f;
178              
179             # 576i50 25Mbps 4:1:1 is a special case
180 1 50 33     19 if ($dsf == 1 && $stype == 0 && Get8u(\$buff, 4) & 0x07) {
      33        
181 0         0 $profile = $dvProfiles[2];
182             } else {
183 1         4 foreach (@dvProfiles) {
184 2 100 66     12 next unless $dsf == $$_{DSF} and $stype == $$_{VideoSType};
185 1         2 $profile = $_;
186 1         3 last;
187             }
188 1 50       3 $profile or $et->Warn("Unrecognized DV profile"), return 1;
189             }
190 1         5 my $tagTablePtr = GetTagTable('Image::ExifTool::DV::Main');
191              
192             # calculate total bit rate and duration
193 1         7 my $byteRate = $$profile{FrameSize} * $$profile{FrameRate};
194 1         3 my $fileSize = $$et{VALUE}{FileSize};
195 1         3 $$profile{TotalBitrate} = 8 * $byteRate;
196 1 50       6 $$profile{Duration} = $fileSize / $byteRate if defined $fileSize;
197              
198             # read DVPack metadata from the VAUX DIF's to extract video tags
199 1         2 delete $$profile{DateTimeOriginal};
200 1         2 delete $$profile{AspectRatio};
201 1         2 delete $$profile{VideoScanType};
202 1         2 my ($date, $time, $is16_9, $interlace);
203 1         6 for ($i=1; $i<6; ++$i) {
204 5         8 $pos += 80;
205 5         9 my $type = Get8u(\$buff, $pos);
206 5 100       15 next unless ($type & 0xf0) == 0x50; # look for VAUX types
207 3         7 for ($j=0; $j<15; ++$j) {
208 43         58 my $p = $pos + $j * 5 + 3;
209 43         92 $type = Get8u(\$buff, $p);
210 43 100 66     121 if ($type == 0x61) { # video control
    100          
    100          
211 1         4 my $apt = Get8u(\$buff, $start + 4) & 0x07;
212 1         3 my $t = Get8u(\$buff, $p + 2);
213 1   33     10 $is16_9 = (($t & 0x07) == 0x02 or (not $apt and ($t & 0x07) == 0x07));
214 1         3 $interlace = Get8u(\$buff, $p + 3) & 0x10; # (ref 2)
215             } elsif ($type == 0x62) { # date
216             # mask off unused bits
217 1         15 my @d = unpack('C*', substr($buff, $p + 1, 4));
218             # (ignore timezone in byte 0 until we can test this properly - see ref 2)
219 1         9 $date = sprintf('%.2x:%.2x:%.2x', $d[3], $d[2] & 0x1f, $d[1] & 0x3f);
220 1 50       6 if ($date =~ /[a-f]/) {
221 0         0 undef $date; # invalid date
222             } else {
223             # add century (this will work until 2089)
224 1 50       7 $date = ($date lt '9' ? '20' : '19') . $date;
225             }
226 1         4 undef $time;
227             } elsif ($type == 0x63 and $date) { # time
228             # (ignore frames past second in byte 0 for now - see ref 2)
229 1         7 my $val = Get32u(\$buff, $p + 1) & 0x007f7f3f;
230 1         8 my @t = unpack('C*', substr($buff, $p + 1, 4));
231 1         5 $time = sprintf('%.2x:%.2x:%.2x', $t[3] & 0x3f, $t[2] & 0x7f, $t[1] & 0x7f);
232 1         4 last;
233             } else {
234 40         70 undef $time; # must be consecutive
235             }
236             }
237             }
238 1 50 33     8 if ($date and $time) {
239 1         17 $$profile{DateTimeOriginal} = "$date $time";
240 1 50       6 if (defined $is16_9) {
241 1 50       4 $$profile{AspectRatio} = $is16_9 ? '16:9' : '4:3';
242 1 50       3 $$profile{VideoScanType} = $interlace ? 'Interlaced' : 'Progressive';
243             }
244             }
245              
246             # read audio tags if available
247 1         2 delete $$profile{AudioSampleRate};
248 1         2 delete $$profile{AudioBitsPerSample};
249 1         1 delete $$profile{AudioChannels};
250 1         3 $pos = $start + 80*6 + 80*16*3 + 3;
251 1 50 33     7 if ($pos + 4 < $len and Get8u(\$buff, $pos) == 0x50) {
252 1         3 my $smpls = Get8u(\$buff, $pos + 1);
253 1         4 my $freq = (Get8u(\$buff, $pos + 4) >> 3) & 0x07;
254 1         14 my $stype = Get8u(\$buff, $pos + 3) & 0x1f;
255 1         4 my $quant = Get8u(\$buff, $pos + 4) & 0x07;
256 1 50       4 if ($freq < 3) {
257 1         6 $$profile{AudioSampleRate} = {0=>48000, 1=>44100, 2=>32000}->{$freq};
258             }
259 1 50       4 if ($stype < 3) {
260 1 50 33     36 $stype = 2 if $stype == 0 and $quant and $freq == 2;
      33        
261 1         13 $$profile{AudioChannels} = {0=>2, 1=>0, 2=>4, 3=>8}->{$stype};
262             }
263 1 50       7 $$profile{AudioBitsPerSample} = $quant ? 12 : 16;
264             }
265              
266             # save our metadata
267 1         4 foreach $tag (@dvTags) {
268 13 50       30 next unless defined $$profile{$tag};
269 13         32 $et->HandleTag($tagTablePtr, $tag, $$profile{$tag});
270             }
271              
272 1         5 return 1;
273             }
274              
275             1; # end
276              
277             __END__