mod_musicindex  1.4.1
playlist-mp3.c
Go to the documentation of this file.
1 /*
2  * playlist-mp3.c
3  * mod_musicindex
4  *
5  * $Id: playlist-mp3.c 1010 2012-08-07 13:38:34Z varenet $
6  *
7  * Created by Regis BOUDIN on Thu Jan 22 2004.
8  * Copyright (c) 2003-2004 Regis BOUDIN
9  * Copyright (c) 2003-2009 Thibaut VARENE
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 2.1,
13  * as published by the Free Software Foundation.
14  *
15  */
16 
34 #include "playlist.h"
35 #include "playlist-mp3.h"
36 
37 #include <mad.h> /* libmad */
38 #include <id3tag.h> /* id3 tags */
39 #include <ctype.h> /* isdigit */
40 #ifdef HAVE_SYS_STAT_H
41 #include <sys/stat.h>
42 #endif
43 
44 #define INPUT_BUFFER_SIZE 16*1024
45 #define MAD_AVERAGE_FRAMES 10
46 #define DO_FAST_CALC 1
47 #define XING_MAGIC1 ( ('X' << 24) | ('i' << 16) | ('n' << 8) | 'g' )
48 #define XING_MAGIC2 ( ('I' << 24) | ('n' << 16) | ('f' << 8) | 'o' )
51 struct xing {
52  long flags; /* valid fields (see below) */
53  unsigned long frames; /* total number of frames */
54  unsigned long bytes; /* total number of bytes */
55  unsigned char toc[100]; /* 100-point seek table */
56  long scale; /* ?? */
57 };
58 
60 enum {
61  XING_FRAMES = 0x00000001L,
62  XING_BYTES = 0x00000002L,
63  XING_TOC = 0x00000004L,
64  XING_SCALE = 0x00000008L
65 };
66 
67 #undef DETECT_BY_CONTENT_NOT_BORKEN
68 #ifdef DETECT_BY_CONTENT_NOT_BORKEN
69 
76 static short mp3_content_check(FILE *const in)
77 {
78  unsigned char tmp[1024];
79  int read, i;
80 
81  rewind(in); /* make sure we're at the beginning of the file */
82 
83  /* Some files have plenty of useless 0x00 bytes at the beginning. The
84  magic number is still present, but after... */
85  read = (int)fread(tmp, (size_t)1, (size_t)1024, in);
86 
87  for (i=0; i<read; i++) {
88  if (tmp[i] != (unsigned char)0x00)
89  break;
90  }
91 
92  if (i >= read-2)
93  return 1;
94 
95  /* Try an autodetect by hand, based on magic number */
96  if ((tmp[i] == (unsigned char)0xff) && ((tmp[i+1] & (unsigned char)0xfe) == (unsigned char)0xfa)) /* MPEG Layer 2 */
97  return 0;
98  if ((tmp[i] == (unsigned char)0xff) && ((tmp[i+1] & (unsigned char)0xfe) == (unsigned char)0xfc)) /* MPEG Layer 3 */
99  return 0;
100  if ((tmp[i] == (unsigned char)'I') && (tmp[i+1] == (unsigned char)'D') && (tmp[i+2] == (unsigned char)'3')) /* ID3V2 */
101  return 0;
102 
103  return 1;
104 }
105 #endif
106 
116 static short xing_parse(struct xing *xing, struct mad_bitptr ptr, unsigned short bitlen)
117 {
118  unsigned long xing_magic;
119 
120  if (bitlen < 64)
121  goto fail;
122 
123  xing_magic = mad_bit_read(&ptr, 32);
124  if (xing_magic != XING_MAGIC1 && xing_magic != XING_MAGIC2)
125  goto fail;
126 
127  xing->flags = mad_bit_read(&ptr, 32);
128  bitlen -= 64;
129 
130  if (xing->flags & XING_FRAMES) {
131  if (bitlen < 32)
132  goto fail;
133 
134  xing->frames = mad_bit_read(&ptr, 32);
135  bitlen -= 32;
136  }
137 
138  if (xing->flags & XING_BYTES) {
139  if (bitlen < 32)
140  goto fail;
141 
142  xing->bytes = mad_bit_read(&ptr, 32);
143  bitlen -= 32;
144  }
145 
146  if (xing->flags & XING_TOC) {
147  register unsigned short i;
148 
149  if (bitlen < 800)
150  goto fail;
151 
152  for (i = 0; i < 100; ++i)
153  xing->toc[i] = mad_bit_read(&ptr, 8);
154 
155  bitlen -= 800;
156  }
157 
158  if (xing->flags & XING_SCALE) {
159  if (bitlen < 32)
160  goto fail;
161 
162  xing->scale = mad_bit_read(&ptr, 32);
163  bitlen -= 32;
164  }
165 
166  return 0;
167 
168 fail:
169  xing->flags = 0;
170  return -1;
171 }
172 
180 #ifndef DETECT_BY_CONTENT_NOT_BORKEN
181 static inline short mpg123_mp3_ext_check(const char *const filename)
182 {
183  const char *const ext = strrchr(filename, '.');
184  if (ext && (!strncasecmp(ext, ".mp3", 4) || !strncasecmp(ext, ".mp2", 4)))
185  return FALSE;
186  return TRUE;
187 }
188 #endif
189 
205 static id3_utf8_t *utf8_id3tag_findframe(struct id3_tag *tag, const char *const frameid,
206  unsigned short index)
207 {
208  const struct id3_frame *frame = NULL;
209  const union id3_field *field = NULL;
210  const id3_ucs4_t *ucs4 = NULL;
211  unsigned int nstrings, i, mp3gid;
212 
213  if ((frame = id3_tag_findframe (tag, frameid, index))) {
214  field = id3_frame_field(frame, 1);
215  nstrings = id3_field_getnstrings(field);
216  for (i = 0; i < nstrings; ++i) {
217  if ((ucs4 = id3_field_getstrings(field, i))) {
218  if (strcmp(frameid, ID3_FRAME_GENRE) == 0) {
219  /* Some encoders encaps id nb in brackets and the
220  lib doesn't handle this properly. This is ugly */
221  if ((ucs4[0] == '(') && (isdigit(ucs4[1]))) {
222  mp3gid = id3_ucs4_getnumber(ucs4+1);
223  ucs4 = id3_genre_index(mp3gid);
224  }
225  else
226  ucs4 = id3_genre_name(ucs4);
227  }
228  return(id3_ucs4_utf8duplicate(ucs4));
229  /* id3_ucs4_utf8duplicate() allocates memory.
230  Don't forget to free() after the function call. */
231  }
232  }
233  }
234  return NULL;
235 }
236 
252 mu_ent *make_mp3_entry(request_rec *r, apr_pool_t *pool, FILE *const in,
253  const char *const filename)
254 {
255  const mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
256  mu_ent *p = NULL;
257 
258  struct id3_file *id3struct = NULL;
259  struct id3_tag *tag = NULL;
260  id3_utf8_t *utf8 = NULL;
261 
262  struct stat filestat;
263 
264  struct mad_stream madstream;
265  struct mad_header madheader;
266  struct mad_frame madframe;
267  struct xing xing = { .frames = 0, .bytes = 0 };
268 
269  unsigned char madinput_buffer[INPUT_BUFFER_SIZE];
270  size_t madread_size, remaining;
271  mad_timer_t madduration = mad_timer_zero; /* total play time */
272 
273  unsigned short has_xing = 0;
274  unsigned long tagsize = 0, data_used = 0, frames = 0;
275 
276 #ifdef DETECT_BY_CONTENT_NOT_BORKEN
277  if (mp3_content_check(in))
278 #else
279  if (mpg123_mp3_ext_check(filename))
280 #endif
281  goto exit;
282 
283  fstat(fileno(in), &filestat);
284 
285  p = NEW_ENT(pool);
286  if (p == NULL)
287  return NULL;
288 
289  p->filetype = FT_MP3;
290  p->flags &= ~EF_VBR;
291 
292  p->size = filestat.st_size;
293  p->mtime = filestat.st_mtime;
294 
295  /* Check for an ID3 tag and read it if any */
296  if ((id3struct = id3_file_open(filename, ID3_FILE_MODE_READONLY))) {
297  if ((tag = id3_file_tag(id3struct)) && (tag->frames)) {
298  /* tagsize = tag->paddedsize; */
299 
300  if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_TITLE, 0))) {
301  p->title = apr_pstrdup(pool, (char *)utf8);
302  free(utf8);
303  }
304 
305  if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_ARTIST, 0))) {
306  p->artist = apr_pstrdup(pool, (char *)utf8);
307  free(utf8);
308  }
309 
310  if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_ALBUM, 0))) {
311  p->album = apr_pstrdup(pool, (char *)utf8);
312  free(utf8);
313  }
314 
315  if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_YEAR, 0))) {
316  p->date = atoi((char *)utf8);
317  free(utf8);
318  }
319 
320  if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_TRACK, 0))) {
321  p->track = atoi((char *)utf8);
322  free(utf8);
323  }
324  /* TPOS is Part Of a Set (aka disc number) */
325  if ((utf8 = utf8_id3tag_findframe(tag, "TPOS", 0))) {
326  p->posn = atoi((char *)utf8);
327  free(utf8);
328  }
329  /* Some MP3s (especially VBRs) are nice enough to tag their length */
330  if ((utf8 = utf8_id3tag_findframe(tag, "TLEN", 0))) {
331  if ((atoi((char *)utf8) / 1000) > 0)
332  p->length = atoi((char *)utf8) / 1000;
333  free(utf8);
334  }
335 
336  if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_GENRE, 0))) {
337  p->genre = apr_pstrdup(pool, (char *)utf8);
338  free(utf8);
339  }
340  }
341  id3_file_close (id3struct);
342  } /* Done with ID3 tags */
343 
344  /* Now it's time for mad operations to find out the bitrate, length & samplerate! */
345  if (conf->options & (MI_QUICKPL))
346  p->bitrate = p->length = p->freq = 0;
347  else {
348  /* initialisation of the MAD subsystem */
349  mad_stream_init(&madstream);
350  mad_header_init(&madheader);
351  mad_frame_init(&madframe);
352 
353  while (1) {
354  /* find out how much we have to read from the file */
355  remaining = madstream.bufend - madstream.next_frame;
356  memcpy(madinput_buffer, madstream.this_frame, remaining);
357  madread_size = fread(madinput_buffer + remaining, 1, INPUT_BUFFER_SIZE - remaining, in);
358 
359  if (madread_size <= 0) {
360  mi_rdebug("maderror madread_size <= 0 on %s", filename);
361  break;
362  /* TODO traitement eventuel erreur lecture */
363  }
364 
365  mad_stream_buffer(&madstream, madinput_buffer, madread_size + remaining);
366 
367  while (1) {
368  if (mad_header_decode(&madheader, &madstream) == -1) {
369  if (madstream.error == MAD_ERROR_BUFLEN)
370  break;
371  if (!MAD_RECOVERABLE(madstream.error)) {
372  /* TODO traitement eventuel erreur recoverable */
373  mi_rdebug("maderror unrecoverable header decode on %s", filename);
374  break;
375  }
376  if (madstream.error == MAD_ERROR_LOSTSYNC) {
377  /* ignore LOSTSYNC due to ID3 tags */
378  tagsize = id3_tag_query(madstream.this_frame, madstream.bufend - madstream.this_frame);
379  if (tagsize > 0) {
380  mad_stream_skip(&madstream, tagsize);
381  continue;
382  }
383  }
384  continue;
385  }
386 
387  frames++; /* count the number of frames for average length calculation */
388  mad_timer_add(&madduration, madheader.duration); /* sum frame duration */
389  data_used += madstream.next_frame - madstream.this_frame;
390 
391  if (frames == 1) {
392  /* the first frame should give us pretty much everything we need, unless... */
393  p->freq = madheader.samplerate;
394  p->bitrate = madheader.bitrate;
395 
396  /* ...we have a VBR file. See if it has XING headers first */
397  madframe.header = madheader;
398  if (mad_frame_decode(&madframe, &madstream) == -1) {
399  if (!MAD_RECOVERABLE(madstream.error)) {
400  /* TODO traitement eventuel erreur recoverable */
401  mi_rdebug("maderror unrecoverable frame decode on %s", filename);
402  break;
403  }
404  }
405 
406  if (xing_parse(&xing, madstream.anc_ptr, madstream.anc_bitlen) == 0) {
407  /* found xing header */
408  if (xing.frames) { /* some files are broken beyond repair, ignore them */
409  has_xing = 1;
410  p->flags |= EF_VBR;
411  /* Get the total number of frames and find out the average bitrate */
412  frames = xing.frames;
413  mad_timer_multiply(&madduration, frames);
414  p->bitrate = 8 * xing.bytes / mad_timer_count(madduration, MAD_UNITS_SECONDS);
415  }
416  break;
417  }
418  }
419  else {
420  /* Maybe we have a VBR file without xing header */
421  if (p->bitrate != madheader.bitrate)
422  p->flags |= EF_VBR;
423  if ((p->flags & EF_VBR))
424  p->bitrate += madheader.bitrate; /* we'll do an average, XXX overflow when no FAST_CALC? */
425  } /* XXX FIXME this loop will yield wrong result eg if the first
426  2+ frames have the same bitrate, the sum will only trigger
427  after these and the average performed later on on the total
428  number of frames will be incorrect. */
429 
430  /* We can either scan the whole file to get precise duration & bitrate,
431  or just check a few frames and guess the whole story, if DO_FAST_CALC is set */
433  float frame_size = ((double)data_used) / MAD_AVERAGE_FRAMES;
434  frames = (p->size - tagsize) / frame_size;
435 
436  madduration.seconds /= MAD_AVERAGE_FRAMES;
437  madduration.fraction /= MAD_AVERAGE_FRAMES;
438  mad_timer_multiply(&madduration, frames);
439  break;
440  }
441  }
442 
443  /* We need to break the main loop either on fatal error or, if doing
444  FAST_CALC, when we have already computed the numbers we need. Otherwise
445  things get really messy (frames not being == to the number of frames
446  actually read */
447  if ((madstream.error != MAD_ERROR_BUFLEN) || (DO_FAST_CALC && (frames > MAD_AVERAGE_FRAMES)))
448  break;
449  }
450 
451  if ((p->flags & EF_VBR) && !has_xing)
452  p->bitrate = p->bitrate / frames * 1000;
453  if (!p->length) /* remember, length can sometimes be encoded in ID3 tags */
454  p->length = mad_timer_count(madduration, MAD_UNITS_SECONDS);
455 
456  /* MAD subsystem exit */
457  mad_frame_finish(&madframe);
458  mad_header_finish(&madheader);
459  mad_stream_finish(&madstream);
460 
461  /* convert the bitrate to bytes */
462  p->bitrate <<= 10; /* * 1024 */
463  p->bitrate /= 1000; /* / 1000 - avoid FP */
464  }
465 
466  fclose(in);
467 
468 exit:
469  return p;
470 }