CLAM-Development  1.4.0
MpegCodec.cxx
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG)
3  * UNIVERSITAT POMPEU FABRA
4  *
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #if USE_MAD != 1
23 #error with_mad was not set to 1 with scons, but you are including files that require this. Please fix your settings.cfg
24 #endif
25 
26 #if USE_ID3 != 1
27 #error with_id3 was not set to 1 with scons, but you are including files that require this. Please fix your settings.cfg
28 #endif
29 
30 
31 #include "MpegCodec.hxx"
32 #include "AudioFileFormats.hxx"
33 #include "AudioFile.hxx"
34 #include "AudioFileHeader.hxx"
35 #include "MpegBitstream.hxx"
36 #include "MpegAudioStream.hxx"
37 #include <mad.h>
38 #include <id3/tag.h>
39 #include <cstdio>
40 #include <iostream>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 
44 namespace CLAM
45 {
46 
47 namespace AudioCodecs
48 {
49 
50 
51 /* XING parsing is from the MAD winamp input plugin */
52 /* Ripped mercilessly from mpg321 */
53 
54  struct xing {
55  int flags;
56  unsigned long frames;
57  unsigned long bytes;
58  unsigned char toc[100];
59  long scale;
60  };
61 
62  enum {
63  XING_FRAMES = 0x0001,
64  XING_BYTES = 0x0002,
65  XING_TOC = 0x0004,
66  XING_SCALE = 0x0008
67  };
68 
69 # define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
70 
71  static
72  int parse_xing(struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen)
73  {
74  if (bitlen < 64) goto fail;
75  if (mad_bit_read(&ptr, 32) != XING_MAGIC) goto fail;
76 
77  xing->flags = mad_bit_read(&ptr, 32);
78  bitlen -= 64;
79 
80  if (xing->flags & XING_FRAMES) {
81  if (bitlen < 32) goto fail;
82 
83  xing->frames = mad_bit_read(&ptr, 32);
84  bitlen -= 32;
85  }
86 
87  if (xing->flags & XING_BYTES) {
88  if (bitlen < 32) goto fail;
89 
90  xing->bytes = mad_bit_read(&ptr, 32);
91  bitlen -= 32;
92  }
93 
94  if (xing->flags & XING_TOC) {
95  if (bitlen < 800) goto fail;
96 
97  // MRJ: We just need the 8 least significant bits
98  for (unsigned i = 0; i < 100; ++i)
99  xing->toc[i] = (unsigned char)mad_bit_read(&ptr, 8);
100  bitlen -= 800;
101  }
102 
103  if (xing->flags & XING_SCALE) {
104  if (bitlen < 32) goto fail;
105  xing->scale = mad_bit_read(&ptr, 32);
106  bitlen -= 32;
107  }
108  return 1;
109 
110  fail:
111  xing->flags = 0;
112  return 0;
113  }
114 
115 
116 
117  MpegCodec::MpegCodec()
118  {
119 
120  }
121 
123  {
124  }
125 
127  {
128  static MpegCodec theInstance;
129  return theInstance;
130  }
131 
132  bool MpegCodec::IsReadable( std::string uri ) const
133  {
134  // If has extension and it is not mp3 reject it
135  std::string::size_type startExt = uri.rfind( '.' );
136  if ( startExt != std::string::npos )
137  {
138  std::string ext = uri.substr(startExt+1);
139  if ( ext!="mp3" && ext!="mpg" && ext!="mpeg")
140  return false;
141  }
142 
143  // If it is not readable reject it
144  FILE* handle = fopen( uri.c_str(), "rb" );
145  if ( !handle ) // File doesn't exists / not readable
146  return false;
147 
148  // Look for an Mpeg frame
149  MpegBitstream bitstream;
150  bitstream.Init(handle);
151  bool foundSomeMpegFrame = false;
152  while( !foundSomeMpegFrame
153  && !bitstream.EOS() && !bitstream.FatalError() )
154  foundSomeMpegFrame = bitstream.NextFrame();
155  bitstream.Finish();
156  fclose( handle );
157  return foundSomeMpegFrame;
158  }
159 
160  bool MpegCodec::IsWritable( std::string uri, const AudioFileHeader& header ) const
161  {
162  // CLAM does not encode Mpeg
163  return false;
164  }
165 
167  {
168  return new MpegAudioStream(file);
169  }
170 
171  void MpegCodec::RetrieveHeaderData( std::string uri, AudioFileHeader& hdr )
172  {
173 
174  FILE* handle = fopen( uri.c_str(), "rb" );
175 
176  if ( !handle ) // File doesn't exists / not readable
177  return;
178 
179  struct stat fileStats;
180 
181  if ( stat( uri.c_str(), &fileStats ) != 0 )
182  {
183  // Error reading stats from file
184  fclose(handle);
185  return;
186  }
187 
188  unsigned long fileLength = fileStats.st_size;
189 
190  if ( fseek( handle, -128, SEEK_END ) < 0 )
191  {
192  /* File empty */
193  fclose(handle);
194  return;
195  }
196 
197  char buffer[3];
198 
199  if ( fread( buffer, 1, 3, handle ) != 3 )
200  {
201  fclose(handle);
202  return;
203  }
204 
205  if ( !strncmp( buffer, "TAG", 3 ) )
206  {
207  fileLength -=128;
208  }
209 
210  fclose( handle );
211  handle = fopen( uri.c_str(), "rb" );
212 
213  hdr.AddSampleRate();
214  hdr.AddChannels();
215  hdr.AddSamples();
216  hdr.AddFormat();
217  hdr.AddEncoding();
218  hdr.AddEndianess();
219  hdr.AddLength();
220  hdr.UpdateData();
221 
222  MpegBitstream bitstream;
223 
224  bitstream.Init(handle);
225 
226  int frameCount = 0;
227  struct xing xingHeader;
228  xingHeader.frames=0;
229  bool hasXingHeader = false;
230  bool isVBR = false;
231  unsigned int bitrate = 0;
232 
233  /* There are three ways of calculating the length of an mp3:
234  1) Constant bitrate: One frame can provide the information
235  needed: # of frames and duration. Just see how long it
236  is and do the division.
237  2) Variable bitrate: Xing tag. It provides the number of
238  frames. Each frame has the same number of samples, so
239  just use that.
240  3) All: Count up the frames and duration of each frames
241  by decoding each one. We do this if we've no other
242  choice, i.e. if it's a VBR file with no Xing tag.
243  */
244 
245  long numFrames = 0;
246 
247  while ( !bitstream.FatalError() && bitstream.NextFrame() )
248  {
249  if ( frameCount == 0 ) // first frame, for retrieving info about encoding
250  {
251  RetrieveMPEGFrameInfo( bitstream.CurrentFrame(),
252  hdr );
253  if ( parse_xing( &xingHeader,
254  bitstream.StreamState().anc_ptr,
255  bitstream.StreamState().anc_bitlen ) )
256  {
257  isVBR = true;
258 
259  if ( xingHeader.flags & XING_FRAMES )
260  {
261  /* We use the Xing tag only for frames. If it doesn't have that
262  information, it's useless to us and we have to treat it as a
263  normal VBR file */
264  hasXingHeader = true;
265  numFrames = xingHeader.frames;
266  break;
267  }
268  }
269  bitrate = bitstream.CurrentFrame().header.bitrate;
270  }
271 
272  if ( frameCount <= 20 )
273  {
274  if ( bitstream.CurrentFrame().header.bitrate != bitrate )
275  isVBR = true;
276  }
277 
278  if ( !isVBR && frameCount > 20 )
279  break;
280 
281  frameCount++;
282  }
283 
284  if ( !isVBR )
285  {
286  double time = ( fileLength * 8.0 ) / bitstream.CurrentFrame().header.bitrate;
287  double timeFrac = (double)time - ((long)(time));
288  long nsamples = 32 * MAD_NSBSAMPLES(&bitstream.CurrentFrame().header); // samples per frame
289  numFrames = ( long) ( time * bitstream.CurrentFrame().header.samplerate / nsamples );
290  mad_timer_t madFmtTime;
291  mad_timer_set( &madFmtTime, (long)time, (long)(timeFrac*100), 100 );
292  bitstream.Finish();
293 
294  //std::cerr << "Not VBR: " << (TTime)mad_timer_count( madFmtTime, MAD_UNITS_MILLISECONDS )/1000. << std::endl;
295  hdr.SetLength( (TTime)mad_timer_count( madFmtTime, MAD_UNITS_MILLISECONDS ) );
296  }
297  else if ( hasXingHeader )
298  {
299  mad_timer_multiply( &bitstream.CurrentFrame().header.duration, numFrames );
300  mad_timer_t madFmtTime = bitstream.CurrentFrame().header.duration;
301  bitstream.Finish();
302 
303  //std::cerr << "Has XING Header: "<< (TTime)mad_timer_count( madFmtTime, MAD_UNITS_MILLISECONDS )/1000. << std::endl;
304  hdr.SetLength( (TTime)mad_timer_count( madFmtTime, MAD_UNITS_MILLISECONDS ) );
305  }
306  else
307  {
308 
309  TTime decodedFramesLength = bitstream.Finish();
310  //std::cerr << "Brute force time guessing: " << decodedFramesLength/1000. << " s" << std::endl;
311  hdr.SetLength( decodedFramesLength );
312  }
313 
314  // @TODO@: Find a way to estimate reasonably well the actual
315  // number of samples.
316  hdr.SetSamples( TSize((hdr.GetLength()/1000.)*hdr.GetSampleRate()) );
317  hdr.SetEndianess( EAudioFileEndianess::eDefault );
318 
319  fclose( handle );
320  }
321 
322  void MpegCodec::RetrieveMPEGFrameInfo( const struct mad_frame& MPEGFrame,
323  AudioFileHeader& header )
324  {
325  switch( MPEGFrame.header.layer )
326  {
327  case MAD_LAYER_I:
328  header.SetFormat( EAudioFileFormat::eMpegLayer1 );
329  break;
330  case MAD_LAYER_II:
331  header.SetFormat( EAudioFileFormat::eMpegLayer2 );
332  break;
333  case MAD_LAYER_III:
334  header.SetFormat( EAudioFileFormat::eMpegLayer3 );
335  break;
336  default:
337  break;
338  }
339 
340  switch( MPEGFrame.header.emphasis )
341  {
342  case MAD_EMPHASIS_NONE:
343  header.SetEncoding( EAudioFileEncoding::eDefault );
344  break;
345  case MAD_EMPHASIS_50_15_US:
346  header.SetEncoding( EAudioFileEncoding::e5015US );
347  break;
348  case MAD_EMPHASIS_CCITT_J_17:
349  header.SetEncoding( EAudioFileEncoding::eCCITTJ17 );
350  break;
351  default:
352  break;
353  }
354 
355  header.SetSampleRate( TData(MPEGFrame.header.samplerate) );
356  header.SetChannels( MAD_NCHANNELS(&MPEGFrame.header) );
357  }
358 
359 
360  static const char * getField(ID3_Tag & tag, const ID3_FrameID & field)
361  {
362  ID3_Frame* fieldFrame = tag.Find( field );
363  if (not fieldFrame) return 0;
364  ID3_Field* fieldString = fieldFrame->GetField( ID3FN_TEXT );
365  if (not fieldString) return 0;
366  return fieldString->GetRawText();
367  }
368 
370  {
371  ID3_Tag fileTag;
372  fileTag.Link( uri.c_str() );
373 
374  const char * artist = getField(fileTag, ID3FID_LEADARTIST);
375  const char * title = getField(fileTag, ID3FID_TITLE);
376  const char * album = getField(fileTag, ID3FID_ALBUM);
377  const char * tracknum = getField(fileTag, ID3FID_TRACKNUM);
378  const char * composer = getField(fileTag, ID3FID_COMPOSER);
379  const char * performer = getField(fileTag, ID3FID_CONDUCTOR);
380 
381  if (artist) txt.AddArtist();
382  if (title) txt.AddTitle();
383  if (album) txt.AddAlbum();
384  if (tracknum) txt.AddTrackNumber();
385  if (composer) txt.AddComposer();
386  if (performer) txt.AddPerformer();
387 
388  txt.UpdateData();
389 
390  if (artist) txt.SetArtist(artist);
391  if (title) txt.SetTitle(title);
392  if (album) txt.SetAlbum(album);
393  if (tracknum) txt.SetTrackNumber(tracknum);
394  if (composer) txt.SetComposer(composer);
395  if (performer) txt.SetPerformer(performer);
396  }
397 }
398 
399 }
400