source: gs2-extensions/parallel-building/trunk/src/perllib/cpan/Image/ExifTool/H264.pm@ 24626

Last change on this file since 24626 was 24626, checked in by jmt12, 13 years ago

An (almost) complete copy of the perllib directory from a (circa SEP2011) head checkout from Greenstone 2 trunk - in order to try and make merging in this extension a little easier later on (as there have been some major changes to buildcol.pl commited in the main trunk but not in the x64 branch)

  • Property svn:executable set to *
File size: 37.0 KB
Line 
1#------------------------------------------------------------------------------
2# File: H264.pm
3#
4# Description: Read meta information from H.264 video
5#
6# Revisions: 2010/01/31 - P. Harvey Created
7#
8# References: 1) http://www.itu.int/rec/T-REC-H.264/e (T-REC-H.264-200305-S!!PDF-E.pdf)
9# 2) http://miffteevee.co.uk/documentation/development/H264Parser_8cpp-source.html
10# 3) http://ffmpeg.org/
11# 4) US Patent 2009/0052875 A1
12# 5) European Patent (EP2 051 528A1) application no. 07792522.0 filed 08.08.2007
13# 6) Dave Nicholson private communication
14# 7) http://www.freepatentsonline.com/20050076039.pdf
15#
16# Glossary: RBSP = Raw Byte Sequence Payload
17#------------------------------------------------------------------------------
18
19package Image::ExifTool::H264;
20
21use strict;
22use vars qw($VERSION %convMake);
23use Image::ExifTool qw(:DataAccess :Utils);
24use Image::ExifTool::Exif;
25
26$VERSION = '1.06';
27
28sub ProcessSEI($$);
29
30my $parsePictureTiming; # flag to enable parsing of picture timing information (test only)
31
32# lookup for camera manufacturer name
33%convMake = (
34 0x0103 => 'Panasonic',
35 0x0108 => 'Sony',
36 0x1011 => 'Canon',
37);
38
39# information extracted from H.264 video streams
40%Image::ExifTool::H264::Main = (
41 GROUPS => { 2 => 'Video' },
42 VARS => { NO_ID => 1 },
43 NOTES => q{
44 Tags extracted from H.264 video streams. The metadata for AVCHD videos is
45 stored in this stream.
46 },
47 ImageWidth => { },
48 ImageHeight => { },
49 MDPM => { SubDirectory => { TagTable => 'Image::ExifTool::H264::MDPM' } },
50);
51
52# H.264 Supplemental Enhancement Information User Data (ref PH/4)
53%Image::ExifTool::H264::MDPM = (
54 GROUPS => { 2 => 'Camera' },
55 PROCESS_PROC => \&ProcessSEI,
56 TAG_PREFIX => 'MDPM',
57 NOTES => q{
58 The following tags are decoded from the Modified Digital Video Pack Metadata
59 (MDPM) of the unregistered user data with UUID
60 17ee8c60f84d11d98cd60800200c9a66 in the H.264 Supplemental Enhancement
61 Information (SEI). I<[Yes, this description is confusing, but nothing
62 compared to the challenge of actually decoding the data!]> This information
63 may exist at regular intervals through the entire video, but only the first
64 occurrence is extracted unless the ExtractEmbedded (-ee) option is used (in
65 which case subsequent occurrences are extracted as sub-documents).
66 },
67 # (Note: all these are explained in IEC 61834-4, but it costs money so it is useless to me)
68 # 0x00 - ControlCassetteID (ref 7)
69 # 0x01 - ControlTapeLength (ref 7)
70 # 0x02 - ControlTimerActDate (ref 7)
71 # 0x03 - ControlTimerACS_S_S (ref 7)
72 # 0x04-0x05 - ControlPR_StartPoint (ref 7)
73 # 0x06 - ControlTagIDNoGenre (ref 7)
74 # 0x07 - ControlTopicPageHeader (ref 7)
75 # 0x08 - ControlTextHeader (ref 7)
76 # 0x09 - ControlText (ref 7)
77 # 0x0a-0x0b - ControlTag (ref 7)
78 # 0x0c - ControlTeletextInfo (ref 7)
79 # 0x0d - ControlKey (ref 7)
80 # 0x0e-0x0f - ControlZoneEnd (ref 7)
81 # 0x10 - TitleTotalTime (ref 7)
82 # 0x11 - TitleRemainTime (ref 7)
83 # 0x12 - TitleChapterTotalNo (ref 7)
84 # 0x13 - TitleTimecode
85 # 0x14 - TitleBinaryGroup
86 # 0x15 - TitleCassetteNo (ref 7)
87 # 0x16-0x17 - TitleSoftID (ref 7)
88 # (0x18,0x19 listed as TitleTextHeader/TitleText by ref 7)
89 0x18 => {
90 Name => 'DateTimeOriginal',
91 Description => 'Date/Time Original',
92 Groups => { 2 => 'Time' },
93 Notes => 'combined with tag 0x19',
94 Combine => 1, # the next tag (0x19) contains the rest of the date
95 # first byte is timezone information:
96 # 0x80 - unused
97 # 0x40 - DST flag (not currently decoded)
98 # 0x20 - TimeZoneSign
99 # 0x1e - TimeZoneValue
100 # 0x01 - half-hour flag
101 ValueConv => q{
102 my ($tz, @a) = unpack('C*',$val);
103 return sprintf('%.2x%.2x:%.2x:%.2x %.2x:%.2x:%.2x%s%.2d:%s', @a,
104 $tz & 0x20 ? '-' : '+', ($tz >> 1) & 0x0f,
105 $tz & 0x01 ? '30' : '00');
106 },
107 PrintConv => '$self->ConvertDateTime($val)',
108 },
109 # 0x1a-0x1b - TitleStart (ref 7)
110 # 0x1c-0x1d - TitleReelID (ref 7)
111 # 0x1e-0x1f - TitleEnd (ref 7)
112 # 0x20 - ChapterTotalTime (ref 7)
113 # 0x42 - ProgramRecDTime (ref 7)
114 # 0x50/0x60 - (AAUX/VAUX)Source (ref 7)
115 # 0x51/0x61 - (AAUX/VAUX)SourceControl (ref 7)
116 # 0x52/0x62 - (AAUX/VAUX)RecDate (ref 7)
117 # 0x53/0x63 - (AAUX/VAUX)RecTime (ref 7)
118 # 0x54/0x64 - (AAUX/VAUX)BinaryGroup (ref 7)
119 # 0x55/0x65 - (AAUX/VAUX)ClosedCaption (ref 7)
120 # 0x56/0x66 - (AAUX/VAUX)TR (ref 7)
121 0x70 => { # ConsumerCamera1
122 Name => 'Camera1',
123 SubDirectory => { TagTable => 'Image::ExifTool::H264::Camera1' },
124 },
125 0x71 => { # ConsumerCamera2
126 Name => 'Camera2',
127 SubDirectory => { TagTable => 'Image::ExifTool::H264::Camera2' },
128 },
129 # 0x73 Lens - val: 0x75ffffd3,0x0effffd3,0x59ffffd3,0x79ffffd3,0xffffffd3...
130 # 0x74 Gain
131 # 0x75 Pedestal
132 # 0x76 Gamma
133 # 0x77 Detail
134 # 0x7b CameraPreset
135 # 0x7c Flare
136 # 0x7d Shading
137 # 0x7e Knee
138 0x7f => { # Shutter
139 Name => 'Shutter',
140 SubDirectory => {
141 TagTable => 'Image::ExifTool::H264::Shutter',
142 ByteOrder => 'LittleEndian', # weird
143 },
144 },
145 0xa0 => {
146 Name => 'ExposureTime',
147 Format => 'rational32u',
148 Groups => { 2 => 'Image' },
149 PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
150 },
151 0xa1 => {
152 Name => 'FNumber',
153 Format => 'rational32u',
154 Groups => { 2 => 'Image' },
155 },
156 0xa2 => {
157 Name => 'ExposureProgram',
158 Format => 'int32u', # (guess)
159 PrintConv => {
160 0 => 'Not Defined',
161 1 => 'Manual',
162 2 => 'Program AE',
163 3 => 'Aperture-priority AE',
164 4 => 'Shutter speed priority AE',
165 5 => 'Creative (Slow speed)',
166 6 => 'Action (High speed)',
167 7 => 'Portrait',
168 8 => 'Landscape',
169 },
170 },
171 0xa3 => {
172 Name => 'BrightnessValue',
173 Format => 'rational32s',
174 Groups => { 2 => 'Image' },
175 },
176 0xa4 => {
177 Name => 'ExposureCompensation',
178 Format => 'rational32s',
179 Groups => { 2 => 'Image' },
180 PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)',
181 },
182 0xa5 => {
183 Name => 'MaxApertureValue',
184 Format => 'rational32u',
185 ValueConv => '2 ** ($val / 2)',
186 PrintConv => 'sprintf("%.1f",$val)',
187 },
188 0xa6 => {
189 Name => 'Flash',
190 Format => 'int32u', # (guess)
191 Flags => 'PrintHex',
192 SeparateTable => 'EXIF Flash',
193 PrintConv => \%Image::ExifTool::Exif::flash,
194 },
195 0xa7 => {
196 Name => 'CustomRendered',
197 Format => 'int32u', # (guess)
198 Groups => { 2 => 'Image' },
199 PrintConv => {
200 0 => 'Normal',
201 1 => 'Custom',
202 },
203 },
204 0xa8 => {
205 Name => 'WhiteBalance',
206 Format => 'int32u', # (guess)
207 Priority => 0,
208 PrintConv => {
209 0 => 'Auto',
210 1 => 'Manual',
211 },
212 },
213 0xa9 => {
214 Name => 'FocalLengthIn35mmFormat',
215 Format => 'rational32u',
216 PrintConv => '"$val mm"',
217 },
218 0xaa => {
219 Name => 'SceneCaptureType',
220 Format => 'int32u', # (guess)
221 PrintConv => {
222 0 => 'Standard',
223 1 => 'Landscape',
224 2 => 'Portrait',
225 3 => 'Night',
226 },
227 },
228 # 0xab-0xaf ExifOption
229 0xb0 => {
230 Name => 'GPSVersionID',
231 Format => 'int8u',
232 Count => 4,
233 Groups => { 1 => 'GPS', 2 => 'Location' },
234 PrintConv => '$val =~ tr/ /./; $val',
235 },
236 0xb1 => {
237 Name => 'GPSLatitudeRef',
238 Format => 'string',
239 Groups => { 1 => 'GPS', 2 => 'Location' },
240 PrintConv => {
241 N => 'North',
242 S => 'South',
243 },
244 },
245 0xb2 => {
246 Name => 'GPSLatitude',
247 Format => 'rational32u',
248 Groups => { 1 => 'GPS', 2 => 'Location' },
249 Notes => 'combined with tags 0xb3 and 0xb4',
250 Combine => 2, # combine the next 2 tags (0xb2=deg, 0xb3=min, 0xb4=sec)
251 ValueConv => 'Image::ExifTool::GPS::ToDegrees($val)',
252 PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1)',
253 },
254 0xb5 => {
255 Name => 'GPSLongitudeRef',
256 Format => 'string',
257 Groups => { 1 => 'GPS', 2 => 'Location' },
258 PrintConv => {
259 E => 'East',
260 W => 'West',
261 },
262 },
263 0xb6 => {
264 Name => 'GPSLongitude',
265 Format => 'rational32u',
266 Groups => { 1 => 'GPS', 2 => 'Location' },
267 Combine => 2, # combine the next 2 tags (0xb6=deg, 0xb7=min, 0xb8=sec)
268 Notes => 'combined with tags 0xb7 and 0xb8',
269 ValueConv => 'Image::ExifTool::GPS::ToDegrees($val)',
270 PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1)',
271 },
272 0xb9 => {
273 Name => 'GPSAltitudeRef',
274 Format => 'int32u', # (guess)
275 Groups => { 1 => 'GPS', 2 => 'Location' },
276 ValueConv => '$val ? 1 : 0', # because I'm not sure about the Format
277 PrintConv => {
278 0 => 'Above Sea Level',
279 1 => 'Below Sea Level',
280 },
281 },
282 0xba => {
283 Name => 'GPSAltitude',
284 Format => 'rational32u',
285 Groups => { 1 => 'GPS', 2 => 'Location' },
286 },
287 0xbb => {
288 Name => 'GPSTimeStamp',
289 Format => 'rational32u',
290 Groups => { 1 => 'GPS', 2 => 'Time' },
291 Combine => 2, # the next tags (0xbc/0xbd) contain the minutes/seconds
292 Notes => 'combined with tags 0xbc and 0xbd',
293 ValueConv => 'Image::ExifTool::GPS::ConvertTimeStamp($val)',
294 },
295 0xbe => {
296 Name => 'GPSStatus',
297 Format => 'string',
298 Groups => { 1 => 'GPS', 2 => 'Location' },
299 PrintConv => {
300 A => 'Measurement Active',
301 V => 'Measurement Void',
302 },
303 },
304 0xbf => {
305 Name => 'GPSMeasureMode',
306 Format => 'string',
307 Groups => { 1 => 'GPS', 2 => 'Location' },
308 PrintConv => {
309 2 => '2-Dimensional Measurement',
310 3 => '3-Dimensional Measurement',
311 },
312 },
313 0xc0 => {
314 Name => 'GPSDOP',
315 Description => 'GPS Dilution Of Precision',
316 Format => 'rational32u',
317 Groups => { 1 => 'GPS', 2 => 'Location' },
318 },
319 0xc1 => {
320 Name => 'GPSSpeedRef',
321 Format => 'string',
322 Groups => { 1 => 'GPS', 2 => 'Location' },
323 PrintConv => {
324 K => 'km/h',
325 M => 'mph',
326 N => 'knots',
327 },
328 },
329 0xc2 => {
330 Name => 'GPSSpeed',
331 Format => 'rational32u',
332 Groups => { 1 => 'GPS', 2 => 'Location' },
333 },
334 0xc3 => {
335 Name => 'GPSTrackRef',
336 Format => 'string',
337 Groups => { 1 => 'GPS', 2 => 'Location' },
338 PrintConv => {
339 M => 'Magnetic North',
340 T => 'True North',
341 },
342 },
343 0xc4 => {
344 Name => 'GPSTrack',
345 Format => 'rational32u',
346 Groups => { 1 => 'GPS', 2 => 'Location' },
347 },
348 0xc5 => {
349 Name => 'GPSImgDirectionRef',
350 Format => 'string',
351 Groups => { 1 => 'GPS', 2 => 'Location' },
352 PrintConv => {
353 M => 'Magnetic North',
354 T => 'True North',
355 },
356 },
357 0xc6 => {
358 Name => 'GPSImgDirection',
359 Format => 'rational32u',
360 Groups => { 1 => 'GPS', 2 => 'Location' },
361 },
362 0xc7 => {
363 Name => 'GPSMapDatum',
364 Format => 'string',
365 Groups => { 1 => 'GPS', 2 => 'Location' },
366 Combine => 1, # the next tag (0xc8) contains the rest of the string
367 Notes => 'combined with tag 0xc8',
368 },
369 # 0xc9-0xcf - GPSOption
370 0xe0 => {
371 Name => 'MakeModel',
372 SubDirectory => { TagTable => 'Image::ExifTool::H264::MakeModel' },
373 },
374 # 0xe1-0xef - MakerOption
375 # 0xe1 - val: 0x01000670,0x01000678,0x06ffffff,0x01ffffff,0x01000020,0x01000400...
376 # 0xe2-0xe8 - val: 0x00000000 in many samples
377 0xe1 => { #6
378 Name => 'RecInfo',
379 Condition => '$$self{Make} eq "Canon"',
380 Notes => 'Canon only',
381 SubDirectory => { TagTable => 'Image::ExifTool::H264::RecInfo' },
382 },
383 0xe4 => { #PH
384 Name => 'Model',
385 Condition => '$$self{Make} eq "Sony"',
386 Description => 'Camera Model Name',
387 Notes => 'Sony cameras only, combined with tags 0xe5 and 0xe6',
388 Format => 'string',
389 Combine => 2, # (not sure about 0xe6, but include it just in case)
390 RawConv => '$val eq "" ? undef : $val',
391 },
392 0xee => { #6 (HFS200)
393 Name => 'FrameInfo',
394 Condition => '$$self{Make} eq "Canon"',
395 Notes => 'Canon only',
396 SubDirectory => { TagTable => 'Image::ExifTool::H264::FrameInfo' },
397 },
398);
399
400# ConsumerCamera1 information (ref PH)
401%Image::ExifTool::H264::Camera1 = (
402 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
403 GROUPS => { 2 => 'Camera' },
404 TAG_PREFIX => 'Camera1',
405 PRINT_CONV => 'sprintf("0x%.2x",$val)',
406 FIRST_ENTRY => 0,
407 0 => {
408 Name => 'ApertureSetting',
409 PrintHex => 1,
410 PrintConv => {
411 0xff => 'Auto',
412 0xfe => 'Closed',
413 OTHER => sub { sprintf('%.1f', 2 ** (($_[0] & 0x3f) / 8)) },
414 },
415 },
416 1 => {
417 Name => 'Gain',
418 Mask => 0x0f,
419 ValueConv => '($val - 1) * 3',
420 PrintConv => '"$val dB"',
421 },
422 1.1 => {
423 Name => 'ExposureProgram',
424 Mask => 0xf0,
425 ValueConv => '$val == 0xf0 ? undef : $val',
426 PrintConv => {
427 0x00 => 'Program AE',
428 0x10 => 'Gain', #?
429 0x20 => 'Shutter speed priority AE',
430 0x30 => 'Aperture-priority AE',
431 0x40 => 'Manual',
432 },
433 },
434 2.1 => {
435 Name => 'WhiteBalance',
436 Mask => 0xe0,
437 ValueConv => '$val == 0xe0 ? undef : $val',
438 PrintConv => {
439 0x00 => 'Auto',
440 0x20 => 'Hold',
441 0x40 => '1-Push',
442 0x60 => 'Daylight',
443 },
444 },
445 3 => {
446 Name => 'Focus',
447 ValueConv => '$val == 0xff ? undef : $val',
448 PrintConv => q{
449 my $foc = ($val & 0x7e) / (($val & 0x01) ? 40 : 400);
450 return ($val & 0x80 ? 'Manual' : 'Auto') . " ($foc)";
451 },
452 },
453);
454
455# ConsumerCamera2 information (ref PH)
456%Image::ExifTool::H264::Camera2 = (
457 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
458 GROUPS => { 2 => 'Camera' },
459 TAG_PREFIX => 'Camera2',
460 PRINT_CONV => 'sprintf("0x%.2x",$val)',
461 FIRST_ENTRY => 0,
462 1 => {
463 Name => 'ImageStabilization',
464 PrintConv => {
465 0 => 'Off',
466 0xff => 'n/a',
467 OTHER => sub {
468 my $val = shift;
469 sprintf("%s (0x%.2x)", $val & 0x10 ? "On" : "Off", $val);
470 },
471 },
472 },
473);
474
475# camera info 0x7f (ref PH)
476%Image::ExifTool::H264::Shutter = (
477 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
478 GROUPS => { 2 => 'Image' },
479 TAG_PREFIX => 'Shutter',
480 PRINT_CONV => 'sprintf("0x%.2x",$val)',
481 FIRST_ENTRY => 0,
482 FORMAT => 'int16u',
483 1.1 => { #6
484 Name => 'ExposureTime',
485 Mask => 0x7fff, # (what is bit 0x8000 for?)
486 RawConv => '$val == 0x7fff ? undef : $val', #7
487 ValueConv => '$val / 33640', #PH (conversion factor determined empirically)
488 PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
489 },
490);
491
492# camera info 0xe0 (ref PH)
493%Image::ExifTool::H264::MakeModel = (
494 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
495 GROUPS => { 2 => 'Camera' },
496 FORMAT => 'int16u',
497 FIRST_ENTRY => 0,
498 0 => {
499 Name => 'Make',
500 PrintHex => 1,
501 RawConv => '$$self{Make} = ($Image::ExifTool::H264::convMake{$val} || "Unknown"); $val',
502 PrintConv => \%convMake,
503 },
504 # 1 => ModelIDCode according to ref 4/5 (I think not)
505 # vals: 0x3001 - Sony HDR-CX105E/TG3E/XR500V
506 # 0x1000 - Sony HDR-UX1
507 # 0x3000 - Canon HF100 (30p)
508 # 0x2000 - Canon HF100 (60i)
509 # 0x3101 - Canon HFM300 (PH, all qualities and frame rates)
510);
511
512# camera info 0xe1 (ref 6)
513%Image::ExifTool::H264::RecInfo = (
514 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
515 GROUPS => { 2 => 'Camera' },
516 FORMAT => 'int8u',
517 NOTES => 'Recording information stored by some Canon video cameras.',
518 FIRST_ENTRY => 0,
519 0 => {
520 Name => 'RecordingMode',
521 PrintConv => {
522 0x02 => 'XP+', # High Quality 12 Mbps
523 0x04 => 'SP', # Standard Play 7 Mbps
524 0x05 => 'LP', # Long Play 5 Mbps
525 0x06 => 'FXP', # High Quality 17 Mbps
526 0x07 => 'MXP', # High Quality 24 Mbps
527 },
528 },
529);
530
531# camera info 0xee (ref 6)
532%Image::ExifTool::H264::FrameInfo = (
533 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
534 GROUPS => { 2 => 'Video' },
535 FORMAT => 'int8u',
536 NOTES => 'Frame rate information stored by some Canon video cameras.',
537 FIRST_ENTRY => 0,
538 0 => 'CaptureFrameRate',
539 1 => 'VideoFrameRate',
540 # 2 - 8=60i, 10=PF30, 74=PF24 (PH, HFM300)
541);
542
543#==============================================================================
544# Bitstream functions (used for H264 video)
545#
546# Member variables:
547# Mask = mask for next bit to read (0 when all data has been read)
548# Pos = byte offset of next word to read
549# Word = current data word
550# Len = total data length in bytes
551# DataPt = data pointer
552#..............................................................................
553
554#------------------------------------------------------------------------------
555# Read next word from bitstream
556# Inputs: 0) BitStream ref
557# Returns: true if there is more data (and updates
558# Mask, Pos and Word for first bit in next word)
559sub ReadNextWord($)
560{
561 my $bstr = shift;
562 my $pos = $$bstr{Pos};
563 if ($pos + 4 <= $$bstr{Len}) {
564 $$bstr{Word} = unpack("x$pos N", ${$$bstr{DataPt}});
565 $$bstr{Mask} = 0x80000000;
566 $$bstr{Pos} += 4;
567 } elsif ($pos < $$bstr{Len}) {
568 my @bytes = unpack("x$pos C*", ${$$bstr{DataPt}});
569 my ($word, $mask) = (shift(@bytes), 0x80);
570 while (@bytes) {
571 $word = ($word << 8) | shift(@bytes);
572 $mask <<= 8;
573 }
574 $$bstr{Word} = $word;
575 $$bstr{Mask} = $mask;
576 $$bstr{Pos} = $$bstr{Len};
577 } else {
578 return 0;
579 }
580 return 1;
581}
582
583#------------------------------------------------------------------------------
584# Create a new BitStream object
585# Inputs: 0) data ref
586# Returns: BitStream ref, or null if data is empty
587sub NewBitStream($)
588{
589 my $dataPt = shift;
590 my $bstr = {
591 DataPt => $dataPt,
592 Len => length($$dataPt),
593 Pos => 0,
594 Mask => 0,
595 };
596 ReadNextWord($bstr) or undef $bstr;
597 return $bstr;
598}
599
600#------------------------------------------------------------------------------
601# Get number of bits remaining in bit stream
602# Inputs: 0) BitStream ref
603# Returns: number of bits remaining
604sub BitsLeft($)
605{
606 my $bstr = shift;
607 my $bits = 0;
608 my $mask = $$bstr{Mask};
609 while ($mask) {
610 ++$bits;
611 $mask >>= 1;
612 }
613 return $bits + 8 * ($$bstr{Len} - $$bstr{Pos});
614}
615
616#------------------------------------------------------------------------------
617# Get integer from bitstream
618# Inputs: 0) BitStream ref, 1) number of bits
619# Returns: integer (and increments position in bitstream)
620sub GetIntN($$)
621{
622 my ($bstr, $bits) = @_;
623 my $val = 0;
624 while ($bits--) {
625 $val <<= 1;
626 ++$val if $$bstr{Mask} & $$bstr{Word};
627 $$bstr{Mask} >>= 1 and next;
628 ReadNextWord($bstr) or last;
629 }
630 return $val;
631}
632
633#------------------------------------------------------------------------------
634# Get Exp-Golomb integer from bitstream
635# Inputs: 0) BitStream ref
636# Returns: integer (and increments position in bitstream)
637sub GetGolomb($)
638{
639 my $bstr = shift;
640 # first, count the number of zero bits to get the integer bit width
641 my $count = 0;
642 until ($$bstr{Mask} & $$bstr{Word}) {
643 ++$count;
644 $$bstr{Mask} >>= 1 and next;
645 ReadNextWord($bstr) or last;
646 }
647 # then return the adjusted integer
648 return GetIntN($bstr, $count + 1) - 1;
649}
650
651#------------------------------------------------------------------------------
652# Get signed Exp-Golomb integer from bitstream
653# Inputs: 0) BitStream ref
654# Returns: integer (and increments position in bitstream)
655sub GetGolombS($)
656{
657 my $bstr = shift;
658 my $val = GetGolomb($bstr) + 1;
659 return ($val & 1) ? -($val >> 1) : ($val >> 1);
660}
661
662# end bitstream functions
663#==============================================================================
664
665#------------------------------------------------------------------------------
666# Decode H.264 scaling matrices
667# Inputs: 0) BitStream ref
668# Reference: http://ffmpeg.org/
669sub DecodeScalingMatrices($)
670{
671 my $bstr = shift;
672 if (GetIntN($bstr, 1)) {
673 my ($i, $j);
674 for ($i=0; $i<8; ++$i) {
675 my $size = $i<6 ? 16 : 64;
676 next unless GetIntN($bstr, 1);
677 my ($last, $next) = (8, 8);
678 for ($j=0; $j<$size; ++$j) {
679 $next = ($last + GetGolombS($bstr)) & 0xff if $next;
680 last unless $j or $next;
681 }
682 }
683 }
684}
685
686#------------------------------------------------------------------------------
687# Parse H.264 sequence parameter set RBSP (ref 1)
688# Inputs) 0) ExifTool ref, 1) tag table ref, 2) data ref
689sub ParseSeqParamSet($$$)
690{
691 my ($exifTool, $tagTablePtr, $dataPt) = @_;
692 # initialize our bitstream object
693 my $bstr = NewBitStream($dataPt) or return;
694 my ($t, $i, $j, $n);
695 # the messy nature of H.264 encoding makes it difficult to use
696 # data-driven structure parsing, so I code it explicitely (yuck!)
697 $t = GetIntN($bstr, 8); # profile_idc
698 GetIntN($bstr, 16); # constraints and level_idc
699 GetGolomb($bstr); # seq_parameter_set_id
700 if ($t >= 100) { # (ref b)
701 $t = GetGolomb($bstr); # chroma_format_idc
702 if ($t == 3) {
703 GetIntN($bstr, 1); # separate_colour_plane_flag
704 $n = 12;
705 } else {
706 $n = 8;
707 }
708 GetGolomb($bstr); # bit_depth_luma_minus8
709 GetGolomb($bstr); # bit_depth_chroma_minus8
710 GetIntN($bstr, 1); # qpprime_y_zero_transform_bypass_flag
711 DecodeScalingMatrices($bstr);
712 }
713 GetGolomb($bstr); # log2_max_frame_num_minus4
714 $t = GetGolomb($bstr); # pic_order_cnt_type
715 if ($t == 0) {
716 GetGolomb($bstr); # log2_max_pic_order_cnt_lsb_minus4
717 } elsif ($t == 1) {
718 GetIntN($bstr, 1); # delta_pic_order_always_zero_flag
719 GetGolomb($bstr); # offset_for_non_ref_pic
720 GetGolomb($bstr); # offset_for_top_to_bottom_field
721 $n = GetGolomb($bstr); # num_ref_frames_in_pic_order_cnt_cycle
722 for ($i=0; $i<$n; ++$i) {
723 GetGolomb($bstr); # offset_for_ref_frame[i]
724 }
725 }
726 GetGolomb($bstr); # num_ref_frames
727 GetIntN($bstr, 1); # gaps_in_frame_num_value_allowed_flag
728 my $w = GetGolomb($bstr); # pic_width_in_mbs_minus1
729 my $h = GetGolomb($bstr); # pic_height_in_map_units_minus1
730 my $f = GetIntN($bstr, 1); # frame_mbs_only_flag
731 $f or GetIntN($bstr, 1); # mb_adaptive_frame_field_flag
732 GetIntN($bstr, 1); # direct_8x8_inference_flag
733 # convert image size to pixels
734 $w = ($w + 1) * 16;
735 $h = (2 - $f) * ($h + 1) * 16;
736 # account for cropping (if any)
737 $t = GetIntN($bstr, 1); # frame_cropping_flag
738 if ($t) {
739 my $m = 4 - $f * 2;
740 $w -= 4 * GetGolomb($bstr);# frame_crop_left_offset
741 $w -= 4 * GetGolomb($bstr);# frame_crop_right_offset
742 $h -= $m * GetGolomb($bstr);# frame_crop_top_offset
743 $h -= $m * GetGolomb($bstr);# frame_crop_bottom_offset
744 }
745 # quick validity checks (just in case)
746 return unless $$bstr{Mask};
747 if ($w>=160 and $w<=4096 and $h>=120 and $h<=3072) {
748 $exifTool->HandleTag($tagTablePtr, ImageWidth => $w);
749 $exifTool->HandleTag($tagTablePtr, ImageHeight => $h);
750 # (whew! -- so much work just to get ImageSize!!)
751 }
752 # return now unless interested in picture timing information
753 return unless $parsePictureTiming;
754
755 # parse vui parameters if they exist
756 GetIntN($bstr, 1) or return; # vui_parameters_present_flag
757 $t = GetIntN($bstr, 1); # aspect_ratio_info_present_flag
758 if ($t) {
759 $t = GetIntN($bstr, 8); # aspect_ratio_idc
760 if ($t == 255) { # Extended_SAR ?
761 GetIntN($bstr, 32); # sar_width/sar_height
762 }
763 }
764 $t = GetIntN($bstr, 1); # overscan_info_present_flag
765 GetIntN($bstr, 1) if $t; # overscan_appropriate_flag
766 $t = GetIntN($bstr, 1); # video_signal_type_present_flag
767 if ($t) {
768 GetIntN($bstr, 4); # video_format/video_full_range_flag
769 $t = GetIntN($bstr, 1); # colour_description_present_flag
770 GetIntN($bstr, 24) if $t; # colour_primaries/transfer_characteristics/matrix_coefficients
771 }
772 $t = GetIntN($bstr, 1); # chroma_loc_info_present_flag
773 if ($t) {
774 GetGolomb($bstr); # chroma_sample_loc_type_top_field
775 GetGolomb($bstr); # chroma_sample_loc_type_bottom_field
776 }
777 $t = GetIntN($bstr, 1); # timing_info_present_flag
778 if ($t) {
779 return if BitsLeft($bstr) < 65;
780 $$exifTool{VUI_units} = GetIntN($bstr, 32); # num_units_in_tick
781 $$exifTool{VUI_scale} = GetIntN($bstr, 32); # time_scale
782 GetIntN($bstr, 1); # fixed_frame_rate_flag
783 }
784 my $hard;
785 for ($j=0; $j<2; ++$j) {
786 $t = GetIntN($bstr, 1); # nal_/vcl_hrd_parameters_present_flag
787 if ($t) {
788 $$exifTool{VUI_hard} = 1;
789 $hard = 1;
790 $n = GetGolomb($bstr); # cpb_cnt_minus1
791 GetIntN($bstr, 8); # bit_rate_scale/cpb_size_scale
792 for ($i=0; $i<=$n; ++$i) {
793 GetGolomb($bstr); # bit_rate_value_minus1[SchedSelIdx]
794 GetGolomb($bstr); # cpb_size_value_minus1[SchedSelIdx]
795 GetIntN($bstr, 1); # cbr_flag[SchedSelIdx]
796 }
797 GetIntN($bstr, 5); # initial_cpb_removal_delay_length_minus1
798 $$exifTool{VUI_clen} = GetIntN($bstr, 5); # cpb_removal_delay_length_minus1
799 $$exifTool{VUI_dlen} = GetIntN($bstr, 5); # dpb_output_delay_length_minus1
800 $$exifTool{VUI_toff} = GetIntN($bstr, 5); # time_offset_length
801 }
802 }
803 GetIntN($bstr, 1) if $hard; # low_delay_hrd_flag
804 $$exifTool{VUI_pic} = GetIntN($bstr, 1); # pic_struct_present_flag
805 # (don't yet decode the rest of the vui data)
806}
807
808#------------------------------------------------------------------------------
809# Parse H.264 picture timing SEI message (payload type 1) (ref 1)
810# Inputs) 0) ExifTool ref, 1) data ref
811sub ParsePictureTiming($$)
812{
813 my ($exifTool, $dataPt) = @_;
814 my $bstr = NewBitStream($dataPt) or return;
815 my ($i, $t, $n);
816 # the specification is very odd on this point: the following delays
817 # exist if the VUI hardware parameters are present, or if
818 # "determined by the application, by some means not specified" -- WTF??
819 if ($$exifTool{VUI_hard}) {
820 GetIntN($bstr, $$exifTool{VUI_clen} + 1); # cpb_removal_delay
821 GetIntN($bstr, $$exifTool{VUI_dlen} + 1); # dpb_output_delay
822 }
823 if ($$exifTool{VUI_pic}) {
824 $t = GetIntN($bstr, 4); # pic_struct
825 # determine NumClockTS ($n)
826 $n = { 0=>1, 1=>1, 2=>1, 3=>2, 4=>2, 5=>3, 6=>3, 7=>2, 8=>3 }->{$t};
827 $n or return;
828 for ($i=0; $i<$n; ++$i) {
829 $t = GetIntN($bstr, 1); # clock_timestamp_flag[i]
830 next unless $t;
831 my ($nu, $s, $m, $h, $o);
832 GetIntN($bstr, 2); # ct_type
833 $nu = GetIntN($bstr, 1);# nuit_field_based_flag
834 GetIntN($bstr, 5); # counting_type
835 $t = GetIntN($bstr, 1); # full_timestamp_flag
836 GetIntN($bstr, 1); # discontinuity_flag
837 GetIntN($bstr, 1); # cnt_dropped_flag
838 GetIntN($bstr, 8); # n_frames
839 if ($t) {
840 $s = GetIntN($bstr, 6); # seconds_value
841 $m = GetIntN($bstr, 6); # minutes_value
842 $h = GetIntN($bstr, 5); # hours_value
843 } else {
844 $t = GetIntN($bstr, 1); # seconds_flag
845 if ($t) {
846 $s = GetIntN($bstr, 6); # seconds_value
847 $t = GetIntN($bstr, 1); # minutes_flag
848 if ($t) {
849 $m = GetIntN($bstr, 6); # minutes_value
850 $t = GetIntN($bstr, 1); # hours_flag
851 $h = GetIntN($bstr, 5) if $t; # hours_value
852 }
853 }
854 }
855 if ($$exifTool{VUI_toff}) {
856 $o = GetIntN($bstr, $$exifTool{VUI_toff}); # time_offset
857 }
858 last; # only parse the first clock timestamp found
859 }
860 }
861}
862
863#------------------------------------------------------------------------------
864# Process H.264 Supplementary Enhancement Information (ref 1/PH)
865# Inputs: 0) Exiftool ref, 1) dirInfo ref, 2) tag table ref
866# Returns: 1 if we processed payload type 5
867sub ProcessSEI($$)
868{
869 my ($exifTool, $dirInfo) = @_;
870 my $dataPt = $$dirInfo{DataPt};
871 my $end = length($$dataPt);
872 my $pos = 0;
873 my ($type, $size, $index, $t);
874
875 # scan through SEI payload for type 5 (the unregistered user data)
876 for (;;) {
877 $type = 0;
878 for (;;) {
879 return 0 if $pos >= $end;
880 $t = Get8u($dataPt, $pos++); # payload type
881 $type += $t;
882 last unless $t == 255;
883 }
884 return 0 if $type == 0x80; # terminator (ref PH - maybe byte alignment bits?)
885 $size = 0;
886 for (;;) {
887 return 0 if $pos >= $end;
888 $t = Get8u($dataPt, $pos++); # payload data length
889 $size += $t;
890 last unless $t == 255;
891 }
892 return 0 if $pos + $size > $end;
893 if ($type == 1) { # picture timing information
894 if ($parsePictureTiming) {
895 my $buff = substr($$dataPt, $pos, $size);
896 ParsePictureTiming($exifTool, $dataPt);
897 }
898 } elsif ($type == 5) { # unregistered user data
899 last; # exit loop to process user data now
900 }
901 $pos += $size;
902 }
903
904 # look for our 16-byte UUID
905 # - plus "MDPM" for "ModifiedDVPackMeta"
906 # - plus "GA94" for closed-caption data (not currently decoded)
907 return 0 unless $size > 20 and substr($$dataPt, $pos, 20) eq
908 "\x17\xee\x8c\x60\xf8\x4d\x11\xd9\x8c\xd6\x08\0\x20\x0c\x9a\x66MDPM";
909
910 # load the GPS module because it contains conversion routines and
911 # Composite tags needed for a number of tags we may be extracting
912 require Image::ExifTool::GPS;
913#
914# parse the MDPM records in the UUID 17ee8c60f84d11d98cd60800200c9a66
915# unregistered user data payload (ref PH)
916#
917 my $tagTablePtr = GetTagTable('Image::ExifTool::H264::MDPM');
918 my $oldIndent = $$exifTool{INDENT};
919 $$exifTool{INDENT} .= '| ';
920 $end = $pos + $size; # end of payload
921 $pos += 20; # skip UUID + "MDPM"
922 my $num = Get8u($dataPt, $pos++); # get entry count
923 my $lastTag = 0;
924 $exifTool->VerboseDir('MDPM', $num) if $exifTool->Options('Verbose');
925 # walk through entries in the MDPM payload
926 for ($index=0; $index<$num and $pos<$end; ++$index) {
927 my $tag = Get8u($dataPt, $pos);
928 if ($tag <= $lastTag) { # should be in numerical order (PH)
929 $exifTool->Warn('Entries in MDPM directory are out of sequence');
930 last;
931 }
932 $lastTag = $tag;
933 my $buff = substr($$dataPt, $pos + 1, 4);
934 my $from;
935 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
936 if ($tagInfo) {
937 # use our own print conversion for Unknown tags
938 if ($$tagInfo{Unknown} and not $$tagInfo{SetPrintConv}) {
939 $$tagInfo{PrintConv} = 'sprintf("0x%.8x", unpack("N", $val))';
940 $$tagInfo{SetPrintConv} = 1;
941 }
942 # combine with next value(s) if necessary
943 my $combine = $$tagTablePtr{$tag}{Combine};
944 while ($combine) {
945 last if $pos + 5 >= $end;
946 my $t = Get8u($dataPt, $pos + 5);
947 last if $t != $lastTag + 1; # must be consecutive tag ID's
948 $pos += 5;
949 $buff .= substr($$dataPt, $pos + 1, 4);
950 $from = $index unless defined $from;
951 ++$index;
952 ++$lastTag;
953 --$combine;
954 }
955 $exifTool->HandleTag($tagTablePtr, $tag, undef,
956 TagInfo => $tagInfo,
957 DataPt => \$buff,
958 Size => length($buff),
959 Index => defined $from ? "$from-$index" : $index,
960 );
961 }
962 $pos += 5;
963 }
964 $$exifTool{INDENT} = $oldIndent;
965 return 1;
966}
967
968#------------------------------------------------------------------------------
969# Extract information from H.264 video stream
970# Inputs: 0) ExifTool ref, 1) data ref
971# Returns: 0 = done parsing, 1 = we want to parse more of these
972sub ParseH264Video($$)
973{
974 my ($exifTool, $dataPt) = @_;
975 my $verbose = $exifTool->Options('Verbose');
976 my $out = $exifTool->Options('TextOut');
977 my $tagTablePtr = GetTagTable('Image::ExifTool::H264::Main');
978 my %parseNalUnit = ( 0x06 => 1, 0x07 => 1 ); # NAL unit types to parse
979 my $foundUserData;
980 my $len = length $$dataPt;
981 my $pos = 0;
982 while ($pos < $len) {
983 my ($nextPos, $end);
984 # find start of next NAL unit
985 if ($$dataPt =~ /(\0{2,3}\x01)/g) {
986 $nextPos = pos $$dataPt;
987 $end = $nextPos - length $1;
988 $pos or $pos = $nextPos, next;
989 } else {
990 last unless $pos;
991 $nextPos = $end = $len;
992 }
993 last if $pos >= $len;
994 # parse NAL unit from $pos to $end
995 my $nal_unit_type = Get8u($dataPt, $pos);
996 ++$pos;
997 # check forbidden_zero_bit
998 $nal_unit_type & 0x80 and $exifTool->Warn('H264 forbidden bit error'), last;
999 $nal_unit_type &= 0x1f;
1000 # ignore this NAL unit unless we will parse it
1001 $parseNalUnit{$nal_unit_type} or $verbose or $pos = $nextPos, next;
1002 # read NAL unit (and convert all 0x000003's to 0x0000 as per spec.)
1003 my $buff = '';
1004 pos($$dataPt) = $pos + 1;
1005 while ($$dataPt =~ /\0\0\x03/g) {
1006 last if pos $$dataPt > $end;
1007 $buff .= substr($$dataPt, $pos, pos($$dataPt)-1-$pos);
1008 $pos = pos $$dataPt;
1009 }
1010 $buff .= substr($$dataPt, $pos, $end - $pos);
1011 if ($verbose > 1) {
1012 printf $out " NAL Unit Type: 0x%x (%d bytes)\n",$nal_unit_type, length $buff;
1013 my %parms = ( Out => $out );
1014 $parms{MaxLen} = 96 if $verbose < 4;
1015 Image::ExifTool::HexDump(\$buff, undef, %parms) if $verbose > 2;
1016 }
1017 pos($$dataPt) = $pos = $nextPos;
1018
1019 if ($nal_unit_type == 0x06) { # sei_rbsp (supplemental enhancement info)
1020
1021 if ($$exifTool{GotNAL06}) {
1022 # process only the first SEI unless ExtractEmbedded is set
1023 next unless $exifTool->Options('ExtractEmbedded');
1024 $$exifTool{DOC_NUM} = $$exifTool{GotNAL06};
1025 }
1026 $foundUserData = ProcessSEI($exifTool, { DataPt => \$buff } );
1027 delete $$exifTool{DOC_NUM};
1028 # keep parsing SEI's until we find the user data
1029 next unless $foundUserData;
1030 $$exifTool{GotNAL06} = ($$exifTool{GotNAL06} || 0) + 1;
1031
1032 } elsif ($nal_unit_type == 0x07) { # sequence_parameter_set_rbsp
1033
1034 # process this NAL unit type only once
1035 next if $$exifTool{GotNAL07};
1036 $$exifTool{GotNAL07} = 1;
1037 ParseSeqParamSet($exifTool, $tagTablePtr, \$buff);
1038 }
1039 # we were successful, so don't parse this NAL unit type again
1040 delete $parseNalUnit{$nal_unit_type};
1041 }
1042 # parse one extra H264 frame if we didn't find the user data in this one
1043 # (Panasonic cameras don't put the SEI in the first frame)
1044 return 0 if $foundUserData or $$exifTool{ParsedH264};
1045 $$exifTool{ParsedH264} = 1;
1046 return 1;
1047}
1048
10491; # end
1050
1051__END__
1052
1053=head1 NAME
1054
1055Image::ExifTool::H264 - Read meta information from H.264 video
1056
1057=head1 SYNOPSIS
1058
1059This module is used by Image::ExifTool
1060
1061=head1 DESCRIPTION
1062
1063This module contains routines required by Image::ExifTool to extract
1064information from H.264 video streams.
1065
1066=head1 AUTHOR
1067
1068Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
1069
1070This library is free software; you can redistribute it and/or modify it
1071under the same terms as Perl itself.
1072
1073=head1 REFERENCES
1074
1075=over 4
1076
1077=item L<http://www.itu.int/rec/T-REC-H.264/e>
1078
1079=item L<http://miffteevee.co.uk/documentation/development/H264Parser_8cpp-source.html>
1080
1081=item L<http://ffmpeg.org/>
1082
1083=back
1084
1085=head1 SEE ALSO
1086
1087L<Image::ExifTool::TagNames/H264 Tags>,
1088L<Image::ExifTool(3pm)|Image::ExifTool>
1089
1090=cut
1091
Note: See TracBrowser for help on using the repository browser.