Ogg.pm 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #------------------------------------------------------------------------------
  2. # File: Ogg.pm
  3. #
  4. # Description: Read Ogg meta information
  5. #
  6. # Revisions: 2011/07/13 - P. Harvey Created (split from Vorbis.pm)
  7. #
  8. # References: 1) http://www.xiph.org/vorbis/doc/
  9. # 2) http://flac.sourceforge.net/ogg_mapping.html
  10. # 3) http://www.theora.org/doc/Theora.pdf
  11. #------------------------------------------------------------------------------
  12. package Image::ExifTool::Ogg;
  13. use strict;
  14. use vars qw($VERSION);
  15. use Image::ExifTool qw(:DataAccess :Utils);
  16. $VERSION = '1.01';
  17. my $MAX_PACKETS = 2; # maximum packets to scan from each stream at start of file
  18. # Information types recognizedi in Ogg files
  19. %Image::ExifTool::Ogg::Main = (
  20. NOTES => q{
  21. ExifTool extracts the following types of information from Ogg files. See
  22. L<http://www.xiph.org/vorbis/doc/> for the Ogg specification.
  23. },
  24. # (these are for documentation purposes only, and aren't used by the code below)
  25. vorbis => { SubDirectory => { TagTable => 'Image::ExifTool::Vorbis::Main' } },
  26. theora => { SubDirectory => { TagTable => 'Image::ExifTool::Theora::Main' } },
  27. FLAC => { SubDirectory => { TagTable => 'Image::ExifTool::FLAC::Main' } },
  28. ID3 => { SubDirectory => { TagTable => 'Image::ExifTool::ID3::Main' } },
  29. );
  30. #------------------------------------------------------------------------------
  31. # Process Ogg packet
  32. # Inputs: 0) ExifTool object ref, 1) data ref
  33. # Returns: 1 on success
  34. sub ProcessPacket($$)
  35. {
  36. my ($et, $dataPt) = @_;
  37. my $rtnVal = 0;
  38. if ($$dataPt =~ /^(.)(vorbis|theora)/s) {
  39. my ($tag, $type) = (ord($1), ucfirst($2));
  40. # this is an OGV file if it contains Theora video
  41. $et->OverrideFileType('OGV') if $type eq 'Theora' and $$et{FILE_TYPE} eq 'OGG';
  42. my $tagTablePtr = GetTagTable("Image::ExifTool::${type}::Main");
  43. my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
  44. return 0 unless $tagInfo and $$tagInfo{SubDirectory};
  45. my $subdir = $$tagInfo{SubDirectory};
  46. my %dirInfo = (
  47. DataPt => $dataPt,
  48. DirName => $$tagInfo{Name},
  49. DirStart => 7,
  50. );
  51. my $table = GetTagTable($$subdir{TagTable});
  52. # set group1 so Theoris comments can be distinguised from Vorbis comments
  53. $$et{SET_GROUP1} = $type if $type eq 'Theora';
  54. SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder};
  55. $rtnVal = $et->ProcessDirectory(\%dirInfo, $table);
  56. SetByteOrder('II');
  57. delete $$et{SET_GROUP1};
  58. }
  59. return $rtnVal;
  60. }
  61. #------------------------------------------------------------------------------
  62. # Extract information from an Ogg file
  63. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  64. # Returns: 1 on success, 0 if this wasn't a valid Ogg file
  65. sub ProcessOGG($$)
  66. {
  67. my ($et, $dirInfo) = @_;
  68. # must first check for leading/trailing ID3 information
  69. unless ($$et{DoneID3}) {
  70. require Image::ExifTool::ID3;
  71. Image::ExifTool::ID3::ProcessID3($et, $dirInfo) and return 1;
  72. }
  73. my $raf = $$dirInfo{RAF};
  74. my $verbose = $et->Options('Verbose');
  75. my $out = $et->Options('TextOut');
  76. my ($success, $page, $packets, $streams, $stream) = (0,0,0,0,'');
  77. my ($buff, $flag, %val, $numFlac, %streamPage);
  78. for (;;) {
  79. # must read ahead to next page to see if it is a continuation
  80. # (this code would be a lot simpler if the continuation flag
  81. # was on the leading instead of the trailing page!)
  82. if ($raf and $raf->Read($buff, 28) == 28) {
  83. # validate magic number
  84. unless ($buff =~ /^OggS/) {
  85. $success and $et->Warn('Lost synchronization');
  86. last;
  87. }
  88. unless ($success) {
  89. # set file type and initialize on first page
  90. $success = 1;
  91. $et->SetFileType();
  92. SetByteOrder('II');
  93. }
  94. $flag = Get8u(\$buff, 5); # page flag
  95. $stream = Get32u(\$buff, 14); # stream serial number
  96. if ($flag & 0x02) {
  97. ++$streams; # count start-of-stream pages
  98. $streamPage{$stream} = $page = 0;
  99. } else {
  100. $page = $streamPage{$stream};
  101. }
  102. ++$packets unless $flag & 0x01; # keep track of packet count
  103. } else {
  104. # all done unless we have to process our last packet
  105. last unless %val;
  106. ($stream) = sort keys %val; # take a stream
  107. $flag = 0; # no continuation
  108. undef $raf; # flag for done reading
  109. }
  110. if (defined $numFlac) {
  111. # stop to process FLAC headers if we hit the end of file
  112. last unless $raf;
  113. --$numFlac; # one less header packet to read
  114. } else {
  115. # can finally process previous packet from this stream
  116. # unless this is a continuation page
  117. if (defined $val{$stream} and not $flag & 0x01) {
  118. ProcessPacket($et, \$val{$stream});
  119. delete $val{$stream};
  120. # only read the first $MAX_PACKETS packets from each stream
  121. if ($packets > $MAX_PACKETS * $streams or not defined $raf) {
  122. last unless %val; # all done (success!)
  123. }
  124. }
  125. # stop processing Ogg if we have scanned enough packets
  126. last if $packets > $MAX_PACKETS * $streams and not %val;
  127. }
  128. # continue processing the current page
  129. my $pageNum = Get32u(\$buff, 18); # page sequence number
  130. my $nseg = Get8u(\$buff, 26); # number of segments
  131. # calculate total data length
  132. my $dataLen = Get8u(\$buff, 27);
  133. if ($nseg) {
  134. $raf->Read($buff, $nseg-1) == $nseg-1 or last;
  135. my @segs = unpack('C*', $buff);
  136. # could check that all these (but the last) are 255...
  137. foreach (@segs) { $dataLen += $_ }
  138. }
  139. if (defined $page) {
  140. if ($page == $pageNum) {
  141. $streamPage{$stream} = ++$page;
  142. } else {
  143. $et->Warn('Missing page(s) in Ogg file');
  144. undef $page;
  145. delete $streamPage{$stream};
  146. }
  147. }
  148. # read page data
  149. $raf->Read($buff, $dataLen) == $dataLen or last;
  150. if ($verbose > 1) {
  151. printf $out "Page %d, stream 0x%x, flag 0x%x (%d bytes)\n",
  152. $pageNum, $stream, $flag, $dataLen;
  153. $et->VerboseDump(\$buff, DataPos => $raf->Tell() - $dataLen);
  154. }
  155. if (defined $val{$stream}) {
  156. $val{$stream} .= $buff; # add this continuation page
  157. } elsif (not $flag & 0x01) { # ignore remaining pages of a continued packet
  158. # ignore the first page of any packet we aren't parsing
  159. if ($buff =~ /^(.)(vorbis|theora)/s) {
  160. $val{$stream} = $buff; # save this page
  161. } elsif ($buff =~ /^\x7fFLAC..(..)/s) {
  162. $numFlac = unpack('n',$1);
  163. $val{$stream} = substr($buff, 9);
  164. }
  165. }
  166. if (defined $numFlac) {
  167. # stop to process FLAC headers if we have them all
  168. last if $numFlac <= 0;
  169. } elsif (defined $val{$stream} and $flag & 0x04) {
  170. # process Ogg packet now if end-of-stream bit is set
  171. ProcessPacket($et, \$val{$stream});
  172. delete $val{$stream};
  173. }
  174. }
  175. if (defined $numFlac and defined $val{$stream}) {
  176. # process FLAC headers as if it was a complete FLAC file
  177. require Image::ExifTool::FLAC;
  178. my %dirInfo = ( RAF => new File::RandomAccess(\$val{$stream}) );
  179. Image::ExifTool::FLAC::ProcessFLAC($et, \%dirInfo);
  180. }
  181. return $success;
  182. }
  183. 1; # end
  184. __END__
  185. =head1 NAME
  186. Image::ExifTool::Ogg - Read Ogg meta information
  187. =head1 SYNOPSIS
  188. This module is used by Image::ExifTool
  189. =head1 DESCRIPTION
  190. This module contains definitions required by Image::ExifTool to extract meta
  191. information from Ogg bitstream container files.
  192. =head1 AUTHOR
  193. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  194. This library is free software; you can redistribute it and/or modify it
  195. under the same terms as Perl itself.
  196. =head1 REFERENCES
  197. =over 4
  198. =item L<http://www.xiph.org/vorbis/doc/>
  199. =item L<http://flac.sourceforge.net/ogg_mapping.html>
  200. =item L<http://www.theora.org/doc/Theora.pdf>
  201. =back
  202. =head1 SEE ALSO
  203. L<Image::ExifTool::TagNames/Ogg Tags>,
  204. L<Image::ExifTool(3pm)|Image::ExifTool>
  205. =cut