File Coverage

blib/lib/Image/ExifTool/H264.pm
Criterion Covered Total %
statement 186 308 60.3
branch 61 156 39.1
condition 11 29 37.9
subroutine 13 16 81.2
pod 0 11 0.0
total 271 520 52.1


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