mod_musicindex  1.4.1
playlist-mp4.c
Go to the documentation of this file.
1 /*
2  * playlist-mp4.c
3  * mod_musicindex
4  *
5  * $Id: playlist-mp4.c 1010 2012-08-07 13:38:34Z varenet $
6  *
7  * Created by Thibaut VARENE on Tue Sep 2 2004.
8  * Copyright (c) 2004-2005,2007,2010-2011 Thibaut VARENE
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License version 2.1,
12  * as published by the Free Software Foundation.
13  *
14  */
15 
32 #include "playlist.h"
33 #include "playlist-mp4.h"
34 
35 #include <mp4v2/mp4v2.h> /* libmp4v2 */
36 #ifdef HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
39 
48 static inline short mp4_ext_check(const char *const filename)
49 {
50  const char *const ext = strrchr(filename, '.');
51  if(ext && (!strncasecmp(ext, ".m4a", 4) || /* Apple mp4 file */
52  !strncasecmp(ext, ".mp4", 4) || /* official extention */
53  !strncasecmp(ext, ".aac", 4)) ) /* old MPEG2/4 extention */
54  return TRUE;
55  return FALSE;
56 }
57 
74 mu_ent *make_mp4_entry(request_rec *r, apr_pool_t *pool, FILE *const in,
75  const char *const filename)
76 {
77  const mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
78  mu_ent *p = NULL;
79 
80  struct stat filestat;
81  MP4FileHandle mp4file;
82  MP4TrackId trackID;
83  const char *trackType;
84 
85  /* XXX We could probably rely on the trackType test only though */
86  if (!mp4_ext_check(filename))
87  goto exit;
88 
89 #ifdef MP4_DETAILS_ALL /* braindead API */
90  mp4file = MP4Read(filename, 0);
91 #else
92  mp4file = MP4Read(filename);
93 #endif
94  if (mp4file == MP4_INVALID_FILE_HANDLE)
95  goto exit;
96 
97  /* until proven wrong, audio files have either only 1 sound track,
98  * or one sound track and a couple other ones. The sound track always
99  * comes first. */
100  trackID = MP4FindTrackId(mp4file, 0, NULL, 0);
101  trackType = MP4GetTrackType(mp4file, trackID);
102 
103  if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) { /* We have an Audio Track */
104  /* Allocate convenient tag structure and populate it */
105  const MP4Tags *mp4tags = MP4TagsAlloc();
106  if (!mp4tags)
107  goto fail;
108  MP4TagsFetch(mp4tags, mp4file);
109 
110  p = NEW_ENT(pool);
111  if (p == NULL) {
112  MP4TagsFree(mp4tags);
113  goto fail;
114  }
115 
116  p->filetype = FT_MP4;
117  p->flags |= EF_VBR;
118 
119  fstat(fileno(in), &filestat);
120  p->size = filestat.st_size;
121  p->mtime = filestat.st_mtime;
122 
123  fclose(in); /* we won't use the file stream as provided anymore */
124 
125  if (conf->options & (MI_QUICKPL))
126  p->bitrate = p->length = p->freq = 0;
127  else {
128  unsigned short timeScale = MP4GetTrackTimeScale(mp4file, trackID); /* Hz */
129  MP4Duration trackDuration = MP4GetTrackDuration(mp4file, trackID);
130  unsigned long msDuration = MP4ConvertFromTrackDuration(mp4file, trackID,
131  trackDuration, MP4_MSECS_TIME_SCALE) / 1000; /* s */
132  unsigned long avgBitRate = ( MP4GetTrackBitRate(mp4file, trackID) + 500 ) / 1000; /* kbps */
133 
134  p->length = msDuration;
135  p->bitrate = avgBitRate << 10;
136  p->freq = timeScale;
137  }
138 
139  if (mp4tags->artist)
140  p->artist = apr_pstrdup(pool, mp4tags->artist);
141 
142  if (mp4tags->name)
143  p->title = apr_pstrdup(pool, mp4tags->name);
144 
145  if (mp4tags->album)
146  p->album = apr_pstrdup(pool, mp4tags->album);
147 
148  if (mp4tags->releaseDate)
149  p->date = atoi(mp4tags->releaseDate);
150 
151  if (mp4tags->genre)
152  p->genre = apr_pstrdup(pool, mp4tags->genre);
153 
154  if (mp4tags->track)
155  p->track = mp4tags->track->index;
156 
157  if (mp4tags->disk)
158  p->posn = mp4tags->disk->index;
159 
160  MP4TagsFree(mp4tags);
161  }
162 
163 fail:
164 #ifdef MP4_CLOSE_DO_NOT_COMPUTE_BITRATE /* still braindamaged */
165  MP4Close(mp4file, 0);
166 #else
167  MP4Close(mp4file);
168 #endif
169 
170 exit:
171  return p;
172 }