DV.pm 9.9 KB

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