1 | #------------------------------------------------------------------------------
|
---|
2 | # File: M2TS.pm
|
---|
3 | #
|
---|
4 | # Description: Read M2TS (AVCHD) meta information
|
---|
5 | #
|
---|
6 | # Revisions: 2009/07/03 - P. Harvey Created
|
---|
7 | #
|
---|
8 | # References: 1) http://neuron2.net/library/mpeg2/iso13818-1.pdf
|
---|
9 | # 2) http://www.blu-raydisc.com/Assets/Downloadablefile/BD-RE_Part3_V2.1_WhitePaper_080406-15271.pdf
|
---|
10 | # 3) http://www.videohelp.com/forum/archive/reading-avchd-playlist-files-bdmv-playlist-mpl-t358888.html
|
---|
11 | # 4) http://en.wikipedia.org/wiki/MPEG_transport_stream
|
---|
12 | # 5) http://www.dunod.com/documents/9782100493463/49346_DVB.pdf
|
---|
13 | # 6) http://trac.handbrake.fr/browser/trunk/libhb/stream.c
|
---|
14 | # 7) http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=04560141
|
---|
15 | # 8) http://www.w6rz.net/xport.zip
|
---|
16 | #
|
---|
17 | # Notes: Variable names containing underlines are the same as in ref 1.
|
---|
18 | #
|
---|
19 | # Glossary: PES = Packetized Elementary Stream
|
---|
20 | # PAT = Program Association Table
|
---|
21 | # PMT = Program Map Table
|
---|
22 | # PCR = Program Clock Reference
|
---|
23 | # PID = Packet Identifier
|
---|
24 | #
|
---|
25 | # To Do: - parse PCR to obtain average bitrates?
|
---|
26 | #------------------------------------------------------------------------------
|
---|
27 |
|
---|
28 | package Image::ExifTool::M2TS;
|
---|
29 |
|
---|
30 | use strict;
|
---|
31 | use vars qw($VERSION);
|
---|
32 | use Image::ExifTool qw(:DataAccess :Utils);
|
---|
33 |
|
---|
34 | $VERSION = '1.08';
|
---|
35 |
|
---|
36 | # program map table "stream_type" lookup (ref 6/1)
|
---|
37 | my %streamType = (
|
---|
38 | 0x00 => 'Reserved',
|
---|
39 | 0x01 => 'MPEG-1 Video',
|
---|
40 | 0x02 => 'MPEG-2 Video',
|
---|
41 | 0x03 => 'MPEG-1 Audio',
|
---|
42 | 0x04 => 'MPEG-2 Audio',
|
---|
43 | 0x05 => 'ISO 13818-1 private sections',
|
---|
44 | 0x06 => 'ISO 13818-1 PES private data',
|
---|
45 | 0x07 => 'ISO 13522 MHEG',
|
---|
46 | 0x08 => 'ISO 13818-1 DSM-CC',
|
---|
47 | 0x09 => 'ISO 13818-1 auxiliary',
|
---|
48 | 0x0A => 'ISO 13818-6 multi-protocol encap',
|
---|
49 | 0x0B => 'ISO 13818-6 DSM-CC U-N msgs',
|
---|
50 | 0x0C => 'ISO 13818-6 stream descriptors',
|
---|
51 | 0x0D => 'ISO 13818-6 sections',
|
---|
52 | 0x0E => 'ISO 13818-1 auxiliary',
|
---|
53 | 0x0F => 'MPEG-2 AAC Audio',
|
---|
54 | 0x10 => 'MPEG-4 Video',
|
---|
55 | 0x11 => 'MPEG-4 LATM AAC Audio',
|
---|
56 | 0x12 => 'MPEG-4 generic',
|
---|
57 | 0x13 => 'ISO 14496-1 SL-packetized',
|
---|
58 | 0x14 => 'ISO 13818-6 Synchronized Download Protocol',
|
---|
59 | # 0x15-0x7F => 'ISO 13818-1 Reserved',
|
---|
60 | 0x1b => 'H.264 Video',
|
---|
61 | 0x80 => 'DigiCipher II Video',
|
---|
62 | 0x81 => 'A52/AC-3 Audio',
|
---|
63 | 0x82 => 'HDMV DTS Audio',
|
---|
64 | 0x83 => 'LPCM Audio',
|
---|
65 | 0x84 => 'SDDS Audio',
|
---|
66 | 0x85 => 'ATSC Program ID',
|
---|
67 | 0x86 => 'DTS-HD Audio',
|
---|
68 | 0x87 => 'E-AC-3 Audio',
|
---|
69 | 0x8a => 'DTS Audio',
|
---|
70 | 0x91 => 'A52b/AC-3 Audio',
|
---|
71 | 0x92 => 'DVD_SPU vls Subtitle',
|
---|
72 | 0x94 => 'SDDS Audio',
|
---|
73 | 0xa0 => 'MSCODEC Video',
|
---|
74 | 0xea => 'Private ES (VC-1)',
|
---|
75 | # 0x80-0xFF => 'User Private',
|
---|
76 | );
|
---|
77 |
|
---|
78 | # "table_id" values (ref 5)
|
---|
79 | my %tableID = (
|
---|
80 | 0x00 => 'Program Association',
|
---|
81 | 0x01 => 'Conditional Access',
|
---|
82 | 0x02 => 'Program Map',
|
---|
83 | 0x03 => 'Transport Stream Description',
|
---|
84 | 0x40 => 'Actual Network Information',
|
---|
85 | 0x41 => 'Other Network Information',
|
---|
86 | 0x42 => 'Actual Service Description',
|
---|
87 | 0x46 => 'Other Service Description',
|
---|
88 | 0x4a => 'Bouquet Association',
|
---|
89 | 0x4e => 'Actual Event Information - Present/Following',
|
---|
90 | 0x4f => 'Other Event Information - Present/Following',
|
---|
91 | 0x50 => 'Actual Event Information - Schedule', #(also 0x51-0x5f)
|
---|
92 | 0x60 => 'Other Event Information - Schedule', # (also 0x61-0x6f)
|
---|
93 | 0x70 => 'Time/Date',
|
---|
94 | 0x71 => 'Running Status',
|
---|
95 | 0x72 => 'Stuffing',
|
---|
96 | 0x73 => 'Time Offset',
|
---|
97 | 0x7e => 'Discontinuity Information',
|
---|
98 | 0x7f => 'Selection Information',
|
---|
99 | # 0x80-0xfe => 'User Defined',
|
---|
100 | );
|
---|
101 |
|
---|
102 | # PES stream ID's for which a syntax field does not exist
|
---|
103 | my %noSyntax = (
|
---|
104 | 0xbc => 1, # program_stream_map
|
---|
105 | 0xbe => 1, # padding_stream
|
---|
106 | 0xbf => 1, # private_stream_2
|
---|
107 | 0xf0 => 1, # ECM_stream
|
---|
108 | 0xf1 => 1, # EMM_stream
|
---|
109 | 0xf2 => 1, # DSMCC_stream
|
---|
110 | 0xf8 => 1, # ITU-T Rec. H.222.1 type E stream
|
---|
111 | 0xff => 1, # program_stream_directory
|
---|
112 | );
|
---|
113 |
|
---|
114 | # information extracted from the MPEG-2 transport stream
|
---|
115 | %Image::ExifTool::M2TS::Main = (
|
---|
116 | GROUPS => { 2 => 'Video' },
|
---|
117 | VARS => { NO_ID => 1 },
|
---|
118 | NOTES => q{
|
---|
119 | The MPEG-2 transport stream is used as a container for many different
|
---|
120 | audio/video formats (including AVCHD). This table lists information
|
---|
121 | extracted from M2TS files.
|
---|
122 | },
|
---|
123 | VideoStreamType => {
|
---|
124 | PrintHex => 1,
|
---|
125 | PrintConv => \%streamType,
|
---|
126 | SeparateTable => 'StreamType',
|
---|
127 | },
|
---|
128 | AudioStreamType => {
|
---|
129 | PrintHex => 1,
|
---|
130 | PrintConv => \%streamType,
|
---|
131 | SeparateTable => 'StreamType',
|
---|
132 | },
|
---|
133 | Duration => {
|
---|
134 | Notes => q{
|
---|
135 | the -fast option may be used to avoid scanning to the end of file to
|
---|
136 | calculate the Duration
|
---|
137 | },
|
---|
138 | ValueConv => '$val / 27000000', # (clock is 27MHz)
|
---|
139 | PrintConv => 'ConvertDuration($val)',
|
---|
140 | },
|
---|
141 | # the following tags are for documentation purposes only
|
---|
142 | _AC3 => { SubDirectory => { TagTable => 'Image::ExifTool::M2TS::AC3' } },
|
---|
143 | _H264 => { SubDirectory => { TagTable => 'Image::ExifTool::H264::Main' } },
|
---|
144 | );
|
---|
145 |
|
---|
146 | # information extracted from AC-3 audio streams
|
---|
147 | %Image::ExifTool::M2TS::AC3 = (
|
---|
148 | GROUPS => { 1 => 'AC3', 2 => 'Audio' },
|
---|
149 | VARS => { NO_ID => 1 },
|
---|
150 | NOTES => 'Tags extracted from AC-3 audio streams.',
|
---|
151 | AudioSampleRate => {
|
---|
152 | PrintConv => {
|
---|
153 | 0 => '48000',
|
---|
154 | 1 => '44100',
|
---|
155 | 2 => '32000',
|
---|
156 | },
|
---|
157 | },
|
---|
158 | AudioBitrate => {
|
---|
159 | PrintConvColumns => 2,
|
---|
160 | ValueConv => {
|
---|
161 | 0 => 32000,
|
---|
162 | 1 => 40000,
|
---|
163 | 2 => 48000,
|
---|
164 | 3 => 56000,
|
---|
165 | 4 => 64000,
|
---|
166 | 5 => 80000,
|
---|
167 | 6 => 96000,
|
---|
168 | 7 => 112000,
|
---|
169 | 8 => 128000,
|
---|
170 | 9 => 160000,
|
---|
171 | 10 => 192000,
|
---|
172 | 11 => 224000,
|
---|
173 | 12 => 256000,
|
---|
174 | 13 => 320000,
|
---|
175 | 14 => 384000,
|
---|
176 | 15 => 448000,
|
---|
177 | 16 => 512000,
|
---|
178 | 17 => 576000,
|
---|
179 | 18 => 640000,
|
---|
180 | 32 => '32000 max',
|
---|
181 | 33 => '40000 max',
|
---|
182 | 34 => '48000 max',
|
---|
183 | 35 => '56000 max',
|
---|
184 | 36 => '64000 max',
|
---|
185 | 37 => '80000 max',
|
---|
186 | 38 => '96000 max',
|
---|
187 | 39 => '112000 max',
|
---|
188 | 40 => '128000 max',
|
---|
189 | 41 => '160000 max',
|
---|
190 | 42 => '192000 max',
|
---|
191 | 43 => '224000 max',
|
---|
192 | 44 => '256000 max',
|
---|
193 | 45 => '320000 max',
|
---|
194 | 46 => '384000 max',
|
---|
195 | 47 => '448000 max',
|
---|
196 | 48 => '512000 max',
|
---|
197 | 49 => '576000 max',
|
---|
198 | 50 => '640000 max',
|
---|
199 | },
|
---|
200 | PrintConv => 'ConvertBitrate($val)',
|
---|
201 | },
|
---|
202 | SurroundMode => {
|
---|
203 | PrintConv => {
|
---|
204 | 0 => 'Not indicated',
|
---|
205 | 1 => 'Not Dolby surround',
|
---|
206 | 2 => 'Dolby surround',
|
---|
207 | },
|
---|
208 | },
|
---|
209 | AudioChannels => {
|
---|
210 | PrintConvColumns => 2,
|
---|
211 | PrintConv => {
|
---|
212 | 0 => '1 + 1',
|
---|
213 | 1 => 1,
|
---|
214 | 2 => 2,
|
---|
215 | 3 => 3,
|
---|
216 | 4 => '2/1',
|
---|
217 | 5 => '3/1',
|
---|
218 | 6 => '2/2',
|
---|
219 | 7 => '3/2',
|
---|
220 | 8 => 1,
|
---|
221 | 9 => '2 max',
|
---|
222 | 10 => '3 max',
|
---|
223 | 11 => '4 max',
|
---|
224 | 12 => '5 max',
|
---|
225 | 13 => '6 max',
|
---|
226 | },
|
---|
227 | },
|
---|
228 | );
|
---|
229 |
|
---|
230 | #------------------------------------------------------------------------------
|
---|
231 | # Extract information from AC-3 audio stream
|
---|
232 | # Inputs: 0) ExifTool ref, 1) data ref
|
---|
233 | # Reference: http://www.atsc.org/standards/a_52b.pdf
|
---|
234 | sub ParseAC3Audio($$)
|
---|
235 | {
|
---|
236 | my ($exifTool, $dataPt) = @_;
|
---|
237 | if ($$dataPt =~ /\x0b\x77..(.)/sg) {
|
---|
238 | my $sampleRate = ord($1) >> 6;
|
---|
239 | my $tagTablePtr = GetTagTable('Image::ExifTool::M2TS::AC3');
|
---|
240 | $exifTool->HandleTag($tagTablePtr, AudioSampleRate => $sampleRate);
|
---|
241 | }
|
---|
242 | }
|
---|
243 |
|
---|
244 | #------------------------------------------------------------------------------
|
---|
245 | # Extract information from AC-3 stream descriptor
|
---|
246 | # Inputs: 0) ExifTool ref, 1) data ref
|
---|
247 | # Reference: http://www.atsc.org/standards/a_52b.pdf
|
---|
248 | # Note: This information is duplicated in the Audio stream, but it
|
---|
249 | # is somewhat easier to extract it from the descriptor instead
|
---|
250 | sub ParseAC3Descriptor($$)
|
---|
251 | {
|
---|
252 | my ($exifTool, $dataPt) = @_;
|
---|
253 | return if length $$dataPt < 3;
|
---|
254 | my @v = unpack('C3', $$dataPt);
|
---|
255 | my $tagTablePtr = GetTagTable('Image::ExifTool::M2TS::AC3');
|
---|
256 | # $exifTool->HandleTag($tagTablePtr, 'AudioSampleRate', $v[0] >> 5);
|
---|
257 | $exifTool->HandleTag($tagTablePtr, 'AudioBitrate', $v[1] >> 2);
|
---|
258 | $exifTool->HandleTag($tagTablePtr, 'SurroundMode', $v[1] & 0x03);
|
---|
259 | $exifTool->HandleTag($tagTablePtr, 'AudioChannels', ($v[2] >> 1) & 0x0f);
|
---|
260 | # don't (yet) decode any more (language codes, etc)
|
---|
261 | }
|
---|
262 |
|
---|
263 | #------------------------------------------------------------------------------
|
---|
264 | # Parse PID stream data
|
---|
265 | # Inputs: 0) Exiftool ref, 1) PID number, 2) PID type, 3) PID name, 4) data ref
|
---|
266 | # Returns: 0=stream parsed OK,
|
---|
267 | # 1=stream parsed but we want to parse more of these,
|
---|
268 | # -1=can't parse yet because we don't know the type
|
---|
269 | sub ParsePID($$$$$)
|
---|
270 | {
|
---|
271 | my ($exifTool, $pid, $type, $pidName, $dataPt) = @_;
|
---|
272 | # can't parse until we know the type (Program Map Table may be later in the stream)
|
---|
273 | return -1 unless defined $type;
|
---|
274 | my $verbose = $exifTool->Options('Verbose');
|
---|
275 | if ($verbose > 1) {
|
---|
276 | my $out = $exifTool->Options('TextOut');
|
---|
277 | printf $out "Parsing stream 0x%.4x (%s)\n", $pid, $pidName;
|
---|
278 | my %parms = ( Out => $out );
|
---|
279 | $parms{MaxLen} = 96 if $verbose < 4;
|
---|
280 | Image::ExifTool::HexDump($dataPt, undef, %parms) if $verbose > 2;
|
---|
281 | }
|
---|
282 | my $more = 0;
|
---|
283 | if ($type == 0x01 or $type == 0x02) {
|
---|
284 | # MPEG-1/MPEG-2 Video
|
---|
285 | require Image::ExifTool::MPEG;
|
---|
286 | Image::ExifTool::MPEG::ParseMPEGAudioVideo($exifTool, $dataPt);
|
---|
287 | } elsif ($type == 0x03 or $type == 0x04) {
|
---|
288 | # MPEG-1/MPEG-2 Audio
|
---|
289 | require Image::ExifTool::MPEG;
|
---|
290 | Image::ExifTool::MPEG::ParseMPEGAudio($exifTool, $dataPt);
|
---|
291 | } elsif ($type == 0x1b) {
|
---|
292 | # H.264 Video
|
---|
293 | require Image::ExifTool::H264;
|
---|
294 | $more = Image::ExifTool::H264::ParseH264Video($exifTool, $dataPt);
|
---|
295 | # force parsing additional H264 frames with ExtractEmbedded option
|
---|
296 | $more = 1 if $exifTool->Options('ExtractEmbedded');
|
---|
297 | } elsif ($type == 0x81 or $type == 0x87 or $type == 0x91) {
|
---|
298 | # AC-3 audio
|
---|
299 | ParseAC3Audio($exifTool, $dataPt);
|
---|
300 | }
|
---|
301 | return $more;
|
---|
302 | }
|
---|
303 |
|
---|
304 | #------------------------------------------------------------------------------
|
---|
305 | # Extract information from a M2TS file
|
---|
306 | # Inputs: 0) ExifTool object reference, 1) DirInfo reference
|
---|
307 | # Returns: 1 on success, 0 if this wasn't a valid M2TS file
|
---|
308 | sub ProcessM2TS($$)
|
---|
309 | {
|
---|
310 | my ($exifTool, $dirInfo) = @_;
|
---|
311 | my $raf = $$dirInfo{RAF};
|
---|
312 | my ($buff, $plen, $i, $j, $fileType, $eof);
|
---|
313 | my (%pmt, %pidType, %didPID, %data, %sectLen);
|
---|
314 | my ($startTime, $endTime, $backScan, $maxBack);
|
---|
315 | my $verbose = $exifTool->Options('Verbose');
|
---|
316 | my $out = $exifTool->Options('TextOut');
|
---|
317 |
|
---|
318 | # read first packet
|
---|
319 | return 0 unless $raf->Read($buff, 8) == 8;
|
---|
320 | # test for magic number (sync byte is the only thing we can safely check)
|
---|
321 | return 0 unless $buff =~ /^(....)?\x47/s;
|
---|
322 | unless ($1) {
|
---|
323 | $plen = 188; # no timecode
|
---|
324 | $fileType = 'M2T'; # (just as a way to tell there is no timecode)
|
---|
325 | } else {
|
---|
326 | $plen = 192; # 188-byte transport packet + leading 4-byte timecode (ref 4)
|
---|
327 | }
|
---|
328 | $exifTool->SetFileType($fileType);
|
---|
329 | SetByteOrder('MM');
|
---|
330 | $raf->Seek(0,0); # rewind to start
|
---|
331 | my $tagTablePtr = GetTagTable('Image::ExifTool::M2TS::Main');
|
---|
332 |
|
---|
333 | # PID lookup strings (will add to this with entries from program map table)
|
---|
334 | my %pidName = (
|
---|
335 | 0 => 'Program Association Table',
|
---|
336 | 1 => 'Conditional Access Table',
|
---|
337 | 2 => 'Transport Stream Description Table',
|
---|
338 | 0x1fff => 'Null Packet',
|
---|
339 | );
|
---|
340 | my %needPID = ( 0x00 => 1 ); # lookup for stream PID's that we still need to parse
|
---|
341 |
|
---|
342 | # parse packets from MPEG-2 Transport Stream
|
---|
343 | for ($i=0; ; ++$i) {
|
---|
344 |
|
---|
345 | unless (%needPID) {
|
---|
346 | last unless defined $startTime;
|
---|
347 | # seek backwards to find last PCR
|
---|
348 | if (defined $backScan) {
|
---|
349 | last if defined $endTime;
|
---|
350 | $backScan -= $plen;
|
---|
351 | last if $backScan < $maxBack;
|
---|
352 | } else {
|
---|
353 | undef $endTime;
|
---|
354 | last if $exifTool->Options('FastScan');
|
---|
355 | $verbose and print "[Starting backscan for last PCR]\n";
|
---|
356 | # calculate position of last complete packet
|
---|
357 | my $fwdPos = $raf->Tell();
|
---|
358 | $raf->Seek(0, 2) or last;
|
---|
359 | my $fsize = $raf->Tell();
|
---|
360 | my $nPack = int($fsize / $plen);
|
---|
361 | $backScan = ($nPack - 1) * $plen - $fsize;
|
---|
362 | # set limit on how far back we will go
|
---|
363 | $maxBack = $fwdPos - $fsize;
|
---|
364 | $maxBack = -256000 if $maxBack < -256000;
|
---|
365 | }
|
---|
366 | $raf->Seek($backScan, 2) or last;
|
---|
367 | }
|
---|
368 | # read the next packet
|
---|
369 | $raf->Read($buff, $plen) == $plen or $eof = 1, last;
|
---|
370 | # decode the packet prefix
|
---|
371 | my $pos = length($buff) - 188;
|
---|
372 | my $prefix = Get32u(\$buff, $pos);
|
---|
373 | $pos += 4;
|
---|
374 | # validate sync byte
|
---|
375 | unless (($prefix & 0xff000000) >> 24 == 0x47) {
|
---|
376 | $exifTool->Warn('Synchronization error') unless defined $backScan;
|
---|
377 | last;
|
---|
378 | }
|
---|
379 | # my $transport_error_indicator = $prefix & 0x00800000;
|
---|
380 | my $payload_unit_start_indicator = $prefix & 0x00400000;
|
---|
381 | # my $transport_priority = $prefix & 0x00200000;
|
---|
382 | my $pid =($prefix & 0x001fff00) >> 8; # packet ID
|
---|
383 | # my $transport_scrambling_control = $prefix & 0x000000c0;
|
---|
384 | my $adaptation_field_exists = $prefix & 0x00000020;
|
---|
385 | my $payload_data_exists = $prefix & 0x00000010;
|
---|
386 | # my $continuity_counter = $prefix & 0x0000000f;
|
---|
387 |
|
---|
388 | if ($verbose > 1) {
|
---|
389 | print $out "Transport packet $i:\n";
|
---|
390 | Image::ExifTool::HexDump(\$buff, undef, Addr => $i * $plen, Out => $out) if $verbose > 2;
|
---|
391 | my $str = $pidName{$pid} ? " ($pidName{$pid})" : '';
|
---|
392 | printf $out " Timecode: 0x%.4x\n", Get32u(\$buff, 0) if $plen == 192;
|
---|
393 | printf $out " Packet ID: 0x%.4x$str\n", $pid;
|
---|
394 | printf $out " Start Flag: %s\n", $payload_unit_start_indicator ? 'Yes' : 'No';
|
---|
395 | }
|
---|
396 |
|
---|
397 | # handle adaptation field
|
---|
398 | if ($adaptation_field_exists) {
|
---|
399 | my $len = Get8u(\$buff, $pos++);
|
---|
400 | $pos + $len > $plen and $exifTool->Warn('Invalid adaptation field length'), last;
|
---|
401 | # read PCR value for calculation of Duration
|
---|
402 | if ($len > 6) {
|
---|
403 | my $flags = Get8u(\$buff, $pos);
|
---|
404 | if ($flags & 0x10) { # PCR_flag
|
---|
405 | # combine 33-bit program_clock_reference_base and 9-bit extension
|
---|
406 | my $pcrBase = Get32u(\$buff, $pos + 1);
|
---|
407 | my $pcrExt = Get16u(\$buff, $pos + 5);
|
---|
408 | # ignore separate programs (PID's) and store just the
|
---|
409 | # first and last timestamps found in the file (is this OK?)
|
---|
410 | $endTime = 300 * (2 * $pcrBase + ($pcrExt >> 15)) + ($pcrExt & 0x01ff);
|
---|
411 | $startTime = $endTime unless defined $startTime;
|
---|
412 | }
|
---|
413 | }
|
---|
414 | $pos += $len;
|
---|
415 | }
|
---|
416 |
|
---|
417 | # all done with this packet unless it carries a payload
|
---|
418 | # or if we are just looking for the last timestamp
|
---|
419 | next unless $payload_data_exists and not defined $backScan;
|
---|
420 |
|
---|
421 | # decode payload data
|
---|
422 | if ($pid == 0 or # program association table
|
---|
423 | defined $pmt{$pid}) # program map table(s)
|
---|
424 | {
|
---|
425 | # must interpret pointer field if payload_unit_start_indicator is set
|
---|
426 | if ($payload_unit_start_indicator) {
|
---|
427 | # skip to start of section
|
---|
428 | my $pointer_field = Get8u(\$buff, $pos);
|
---|
429 | $pos += 1 + $pointer_field;
|
---|
430 | $pos >= $plen and $exifTool->Warn('Bad pointer field'), last;
|
---|
431 | } else {
|
---|
432 | # not the start of a section
|
---|
433 | next unless $sectLen{$pid};
|
---|
434 | my $more = $sectLen{$pid} - length($data{$pid});
|
---|
435 | my $size = length($buff) - $pos;
|
---|
436 | $size = $more if $size > $more;
|
---|
437 | $data{$pid} .= substr($buff, $pos, $size);
|
---|
438 | next unless $size == $more;
|
---|
439 | # we have the complete section now, so put back into $buff for parsing
|
---|
440 | $buff = $data{$pid};
|
---|
441 | $pos = 0;
|
---|
442 | delete $data{$pid};
|
---|
443 | delete $sectLen{$pid};
|
---|
444 | }
|
---|
445 | my $slen = length($buff); # section length
|
---|
446 | $pos + 8 > $slen and $exifTool->Warn("Truncated payload"), last;
|
---|
447 | # validate table ID
|
---|
448 | my $table_id = Get8u(\$buff, $pos);
|
---|
449 | my $name = ($tableID{$table_id} || sprintf('Unknown (0x%x)',$table_id)) . ' Table';
|
---|
450 | my $expectedID = $pid ? 0x02 : 0x00;
|
---|
451 | unless ($table_id == $expectedID) {
|
---|
452 | $verbose > 1 and printf $out " (skipping $name)\n";
|
---|
453 | delete $needPID{$pid};
|
---|
454 | $didPID{$pid} = 1;
|
---|
455 | next;
|
---|
456 | }
|
---|
457 | # validate section syntax indicator for parsed tables (PAT, PMT)
|
---|
458 | my $section_syntax_indicator = Get8u(\$buff, $pos + 1) & 0xc0;
|
---|
459 | $section_syntax_indicator == 0x80 or $exifTool->Warn("Bad $name"), last;
|
---|
460 | my $section_length = Get16u(\$buff, $pos + 1) & 0x0fff;
|
---|
461 | $section_length > 1021 and $exifTool->Warn("Invalid $name length"), last;
|
---|
462 | if ($slen < $section_length + 3) { # (3 bytes for table_id + section_length)
|
---|
463 | # must wait until we have the full section
|
---|
464 | $data{$pid} = substr($buff, $pos);
|
---|
465 | $sectLen{$pid} = $section_length + 3;
|
---|
466 | next;
|
---|
467 | }
|
---|
468 | my $program_number = Get16u(\$buff, $pos + 3);
|
---|
469 | my $section_number = Get8u(\$buff, $pos + 6);
|
---|
470 | my $last_section_number = Get8u(\$buff, $pos + 7);
|
---|
471 | if ($verbose > 1) {
|
---|
472 | print $out " $name length: $section_length\n";
|
---|
473 | print $out " Program No: $program_number\n" if $pid;
|
---|
474 | printf $out " Stream ID: 0x%x\n", $program_number if not $pid;
|
---|
475 | print $out " Section No: $section_number\n";
|
---|
476 | print $out " Last Sect.: $last_section_number\n";
|
---|
477 | }
|
---|
478 | my $end = $pos + $section_length + 3 - 4; # (don't read 4-byte CRC)
|
---|
479 | $pos += 8;
|
---|
480 | if ($pid == 0) {
|
---|
481 | # decode PAT (Program Association Table)
|
---|
482 | while ($pos <= $end - 4) {
|
---|
483 | my $program_number = Get16u(\$buff, $pos);
|
---|
484 | my $program_map_PID = Get16u(\$buff, $pos + 2) & 0x1fff;
|
---|
485 | $pmt{$program_map_PID} = $program_number; # save our PMT PID's
|
---|
486 | if (not $pidName{$program_map_PID} or $verbose > 1) {
|
---|
487 | my $str = "Program $program_number Map";
|
---|
488 | $pidName{$program_map_PID} = $str;
|
---|
489 | $needPID{$program_map_PID} = 1 unless $didPID{$program_map_PID};
|
---|
490 | $verbose and printf $out " PID(0x%.4x) --> $str\n", $program_map_PID;
|
---|
491 | }
|
---|
492 | $pos += 4;
|
---|
493 | }
|
---|
494 | } else {
|
---|
495 | # decode PMT (Program Map Table)
|
---|
496 | $pos + 4 > $slen and $exifTool->Warn('Truncated PMT'), last;
|
---|
497 | my $pcr_pid = Get16u(\$buff, $pos) & 0x1fff;
|
---|
498 | my $program_info_length = Get16u(\$buff, $pos + 2) & 0x0fff;
|
---|
499 | if (not $pidName{$pcr_pid} or $verbose > 1) {
|
---|
500 | my $str = "Program $program_number Clock Reference";
|
---|
501 | $pidName{$pcr_pid} = $str;
|
---|
502 | $verbose and printf $out " PID(0x%.4x) --> $str\n", $pcr_pid;
|
---|
503 | }
|
---|
504 | $pos += 4;
|
---|
505 | $pos + $program_info_length > $slen and $exifTool->Warn('Truncated program info'), last;
|
---|
506 | # dump program information descriptors if verbose
|
---|
507 | if ($verbose > 1) { for ($j=0; $j<$program_info_length-2; ) {
|
---|
508 | my $descriptor_tag = Get8u(\$buff, $pos + $j);
|
---|
509 | my $descriptor_length = Get8u(\$buff, $pos + $j + 1);
|
---|
510 | $j += 2;
|
---|
511 | last if $j + $descriptor_length > $program_info_length;
|
---|
512 | my $desc = substr($buff, $pos+$j, $descriptor_length);
|
---|
513 | $j += $descriptor_length;
|
---|
514 | $desc =~ s/([\x00-\x1f\x80-\xff])/sprintf("\\x%.2x",ord $1)/eg;
|
---|
515 | printf $out " Program Descriptor: Type=0x%.2x \"$desc\"\n", $descriptor_tag;
|
---|
516 | }}
|
---|
517 | $pos += $program_info_length; # skip descriptors (for now)
|
---|
518 | while ($pos <= $end - 5) {
|
---|
519 | my $stream_type = Get8u(\$buff, $pos);
|
---|
520 | my $elementary_pid = Get16u(\$buff, $pos + 1) & 0x1fff;
|
---|
521 | my $es_info_length = Get16u(\$buff, $pos + 3) & 0x0fff;
|
---|
522 | if (not $pidName{$elementary_pid} or $verbose > 1) {
|
---|
523 | my $str = $streamType{$stream_type};
|
---|
524 | $str or $str = ($stream_type < 0x7f ? 'Reserved' : 'Private');
|
---|
525 | $str = sprintf('%s (0x%.2x)', $str, $stream_type);
|
---|
526 | $str = "Program $program_number $str";
|
---|
527 | # save PID type and name string
|
---|
528 | $pidName{$elementary_pid} = $str;
|
---|
529 | $pidType{$elementary_pid} = $stream_type;
|
---|
530 | $verbose and printf $out " PID(0x%.4x) --> $str\n", $elementary_pid;
|
---|
531 | if ($str =~ /(Audio|Video)/) {
|
---|
532 | $exifTool->HandleTag($tagTablePtr, $1 . 'StreamType', $stream_type);
|
---|
533 | # we want to parse all Audio and Video streams
|
---|
534 | $needPID{$elementary_pid} = 1 unless $didPID{$elementary_pid};
|
---|
535 | }
|
---|
536 | }
|
---|
537 | $pos += 5;
|
---|
538 | $pos + $es_info_length > $slen and $exifTool->Warn('Trunacted ES info'), $pos = $end, last;
|
---|
539 | # parse elementary stream descriptors
|
---|
540 | for ($j=0; $j<$es_info_length-2; ) {
|
---|
541 | my $descriptor_tag = Get8u(\$buff, $pos + $j);
|
---|
542 | my $descriptor_length = Get8u(\$buff, $pos + $j + 1);
|
---|
543 | $j += 2;
|
---|
544 | last if $j + $descriptor_length > $es_info_length;
|
---|
545 | my $desc = substr($buff, $pos+$j, $descriptor_length);
|
---|
546 | $j += $descriptor_length;
|
---|
547 | if ($verbose > 1) {
|
---|
548 | my $dstr = $desc;
|
---|
549 | $dstr =~ s/([\x00-\x1f\x80-\xff])/sprintf("\\x%.2x",ord $1)/eg;
|
---|
550 | printf $out " ES Descriptor: Type=0x%.2x \"$dstr\"\n", $descriptor_tag;
|
---|
551 | }
|
---|
552 | # parse type-specific descriptor information (once)
|
---|
553 | unless ($didPID{$pid}) {
|
---|
554 | if ($descriptor_tag == 0x81) { # AC-3
|
---|
555 | ParseAC3Descriptor($exifTool, \$desc);
|
---|
556 | }
|
---|
557 | }
|
---|
558 | }
|
---|
559 | $pos += $es_info_length;
|
---|
560 | }
|
---|
561 | }
|
---|
562 | $pos = $end + 4; # skip CRC
|
---|
563 | } elsif ($pid == 1) { # conditional access table
|
---|
564 | } elsif ($pid == 2) { # transport stream description table
|
---|
565 | } elsif ($pid == 0x1fff) { # null packet
|
---|
566 | } elsif (not $didPID{$pid}) {
|
---|
567 | # save data from the start of each elementary stream
|
---|
568 | if ($payload_unit_start_indicator) {
|
---|
569 | if (defined $data{$pid}) {
|
---|
570 | # we must have a whole section, so parse now
|
---|
571 | my $more = ParsePID($exifTool, $pid, $pidType{$pid}, $pidName{$pid}, \$data{$pid});
|
---|
572 | # start fresh even if we couldn't process this PID yet
|
---|
573 | delete $data{$pid};
|
---|
574 | unless ($more) {
|
---|
575 | delete $needPID{$pid};
|
---|
576 | $didPID{$pid} = 1;
|
---|
577 | next;
|
---|
578 | }
|
---|
579 | # set flag indicating we found this PID but we still want more
|
---|
580 | $needPID{$pid} = -1;
|
---|
581 | }
|
---|
582 | # check for a PES header
|
---|
583 | next if $pos + 6 > $plen;
|
---|
584 | my $start_code = Get32u(\$buff, $pos);
|
---|
585 | next unless ($start_code & 0xffffff00) == 0x00000100;
|
---|
586 | my $stream_id = $start_code & 0xff;
|
---|
587 | if ($verbose > 1) {
|
---|
588 | my $pes_packet_length = Get16u(\$buff, $pos + 4);
|
---|
589 | printf $out " Stream ID: 0x%.2x\n", $stream_id;
|
---|
590 | print $out " Packet Len: $pes_packet_length\n";
|
---|
591 | }
|
---|
592 | $pos += 6;
|
---|
593 | unless ($noSyntax{$stream_id}) {
|
---|
594 | next if $pos + 3 > $plen;
|
---|
595 | # validate PES syntax
|
---|
596 | my $syntax = Get8u(\$buff, $pos) & 0xc0;
|
---|
597 | $syntax == 0x80 or $exifTool->Warn('Bad PES syntax'), next;
|
---|
598 | # skip PES header
|
---|
599 | my $pes_header_data_length = Get8u(\$buff, $pos + 2);
|
---|
600 | $pos += 3 + $pes_header_data_length;
|
---|
601 | next if $pos >= $plen;
|
---|
602 | }
|
---|
603 | $data{$pid} = substr($buff, $pos);
|
---|
604 | } else {
|
---|
605 | next unless defined $data{$pid};
|
---|
606 | # accumulate data for each elementary stream
|
---|
607 | $data{$pid} .= substr($buff, $pos);
|
---|
608 | }
|
---|
609 | # save only the first 256 bytes of most streams, except for
|
---|
610 | # unknown or H.264 streams where we save 1 kB
|
---|
611 | my $saveLen = (not $pidType{$pid} or $pidType{$pid} == 0x1b) ? 1024 : 256;
|
---|
612 | if (length($data{$pid}) >= $saveLen) {
|
---|
613 | my $more = ParsePID($exifTool, $pid, $pidType{$pid}, $pidName{$pid}, \$data{$pid});
|
---|
614 | next if $more < 0; # wait for program map table (hopefully not too long)
|
---|
615 | delete $data{$pid};
|
---|
616 | $more and $needPID{$pid} = -1, next; # parse more of these
|
---|
617 | delete $needPID{$pid};
|
---|
618 | $didPID{$pid} = 1;
|
---|
619 | }
|
---|
620 | next;
|
---|
621 | }
|
---|
622 | if ($needPID{$pid}) {
|
---|
623 | # we found and parsed a section with this PID, so
|
---|
624 | # delete from the lookup of PID's we still need to parse
|
---|
625 | delete $needPID{$pid};
|
---|
626 | $didPID{$pid} = 1;
|
---|
627 | }
|
---|
628 | }
|
---|
629 |
|
---|
630 | # calculate Duration if available
|
---|
631 | if (defined $startTime and defined $endTime and $startTime != $endTime) {
|
---|
632 | $endTime += 0x80000000 * 1200 if $startTime > $endTime; # handle 33-bit wrap
|
---|
633 | $exifTool->HandleTag($tagTablePtr, 'Duration', $endTime - $startTime);
|
---|
634 | }
|
---|
635 |
|
---|
636 | if ($verbose) {
|
---|
637 | my @need;
|
---|
638 | foreach (keys %needPID) {
|
---|
639 | push @need, sprintf('0x%.2x',$_) if $needPID{$_} > 0;
|
---|
640 | }
|
---|
641 | if (@need) {
|
---|
642 | @need = sort @need;
|
---|
643 | print $out "End of file. Missing PID(s): @need\n";
|
---|
644 | } else {
|
---|
645 | my $what = $eof ? 'of file' : 'scan';
|
---|
646 | print $out "End $what. All PID's parsed.\n";
|
---|
647 | }
|
---|
648 | }
|
---|
649 |
|
---|
650 | # parse any remaining partial PID streams
|
---|
651 | my $pid;
|
---|
652 | foreach $pid (sort keys %data) {
|
---|
653 | ParsePID($exifTool, $pid, $pidType{$pid}, $pidName{$pid}, \$data{$pid});
|
---|
654 | delete $data{$pid};
|
---|
655 | }
|
---|
656 | return 1;
|
---|
657 | }
|
---|
658 |
|
---|
659 | 1; # end
|
---|
660 |
|
---|
661 | __END__
|
---|
662 |
|
---|
663 | =head1 NAME
|
---|
664 |
|
---|
665 | Image::ExifTool::M2TS - Read M2TS (AVCHD) meta information
|
---|
666 |
|
---|
667 | =head1 SYNOPSIS
|
---|
668 |
|
---|
669 | This module is used by Image::ExifTool
|
---|
670 |
|
---|
671 | =head1 DESCRIPTION
|
---|
672 |
|
---|
673 | This module contains routines required by Image::ExifTool to extract
|
---|
674 | information from MPEG-2 transport streams, such as those used by AVCHD
|
---|
675 | video.
|
---|
676 |
|
---|
677 | =head1 AUTHOR
|
---|
678 |
|
---|
679 | Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
680 |
|
---|
681 | This library is free software; you can redistribute it and/or modify it
|
---|
682 | under the same terms as Perl itself.
|
---|
683 |
|
---|
684 | =head1 REFERENCES
|
---|
685 |
|
---|
686 | =over 4
|
---|
687 |
|
---|
688 | =item L<http://neuron2.net/library/mpeg2/iso13818-1.pdf>
|
---|
689 |
|
---|
690 | =item L<http://www.blu-raydisc.com/Assets/Downloadablefile/BD-RE_Part3_V2.1_WhitePaper_080406-15271.pdf>
|
---|
691 |
|
---|
692 | =item L<http://www.videohelp.com/forum/archive/reading-avchd-playlist-files-bdmv-playlist-mpl-t358888.html>
|
---|
693 |
|
---|
694 | =item L<http://en.wikipedia.org/wiki/MPEG_transport_stream>
|
---|
695 |
|
---|
696 | =item L<http://www.dunod.com/documents/9782100493463/49346_DVB.pdf>
|
---|
697 |
|
---|
698 | =item L<http://trac.handbrake.fr/browser/trunk/libhb/stream.c>
|
---|
699 |
|
---|
700 | =item L<http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=04560141>
|
---|
701 |
|
---|
702 | =item L<http://www.w6rz.net/xport.zip>
|
---|
703 |
|
---|
704 | =back
|
---|
705 |
|
---|
706 | =head1 SEE ALSO
|
---|
707 |
|
---|
708 | L<Image::ExifTool::TagNames/M2TS Tags>,
|
---|
709 | L<Image::ExifTool(3pm)|Image::ExifTool>
|
---|
710 |
|
---|
711 | =cut
|
---|
712 |
|
---|