Add simple authentication support for cmus-remote
[cmus.git] / mp4.c
blobeb82ca7867825fca96127557438964489d5aa66b
1 /*
2 * Copyright 2006 dnk <dnk@bjum.net>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include "ip.h"
21 #include "xmalloc.h"
22 #include "debug.h"
23 #include "file.h"
25 #include <mp4.h>
26 #include <neaacdec.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <unistd.h>
33 struct mp4_private {
34 char *overflow_buf;
35 int overflow_buf_len;
37 unsigned char channels;
38 unsigned long sample_rate;
40 NeAACDecHandle decoder; /* typedef void * */
42 struct {
43 MP4FileHandle handle; /* typedef void * */
45 MP4TrackId track;
46 MP4SampleId sample;
47 MP4SampleId num_samples;
48 } mp4;
52 static MP4TrackId mp4_get_track(MP4FileHandle *handle)
54 MP4TrackId num_tracks;
55 const char *track_type;
56 uint8_t obj_type;
57 MP4TrackId i;
59 num_tracks = MP4GetNumberOfTracks(handle, NULL, 0);
61 for (i = 1; i <= num_tracks; i++) {
62 track_type = MP4GetTrackType(handle, i);
63 if (!track_type)
64 continue;
66 if (!MP4_IS_AUDIO_TRACK_TYPE(track_type))
67 continue;
69 /* MP4GetTrackAudioType */
70 obj_type = MP4GetTrackEsdsObjectTypeId(handle, i);
71 if (obj_type == MP4_INVALID_AUDIO_TYPE)
72 continue;
74 if (obj_type == MP4_MPEG4_AUDIO_TYPE) {
75 obj_type = MP4GetTrackAudioMpeg4Type(handle, i);
77 if (MP4_IS_MPEG4_AAC_AUDIO_TYPE(obj_type))
78 return i;
79 } else {
80 if (MP4_IS_AAC_AUDIO_TYPE(obj_type))
81 return i;
85 return MP4_INVALID_TRACK_ID;
88 static int mp4_open(struct input_plugin_data *ip_data)
90 struct mp4_private *priv;
91 NeAACDecConfigurationPtr neaac_cfg;
92 unsigned char *buf;
93 unsigned int buf_size;
96 /* http://sourceforge.net/forum/message.php?msg_id=3578887 */
97 if (ip_data->remote)
98 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
100 /* init private struct */
101 priv = xnew0(struct mp4_private, 1);
102 ip_data->private = priv;
104 priv->decoder = NeAACDecOpen();
106 /* set decoder config */
107 neaac_cfg = NeAACDecGetCurrentConfiguration(priv->decoder);
108 neaac_cfg->outputFormat = FAAD_FMT_16BIT; /* force 16 bit audio */
109 neaac_cfg->downMatrix = 1; /* 5.1 -> stereo */
110 NeAACDecSetConfiguration(priv->decoder, neaac_cfg);
112 /* open mpeg-4 file */
113 priv->mp4.handle = MP4Read(ip_data->filename, 0);
114 if (!priv->mp4.handle) {
115 d_print("MP4Read failed\n");
116 goto out;
119 /* find aac audio track */
120 priv->mp4.track = mp4_get_track(priv->mp4.handle);
121 if (priv->mp4.track == MP4_INVALID_TRACK_ID) {
122 d_print("MP4FindTrackId failed\n");
123 goto out;
126 priv->mp4.num_samples = MP4GetTrackNumberOfSamples(priv->mp4.handle, priv->mp4.track);
128 priv->mp4.sample = 1;
130 buf = NULL;
131 buf_size = 0;
132 if (!MP4GetTrackESConfiguration(priv->mp4.handle, priv->mp4.track, &buf, &buf_size)) {
133 /* failed to get mpeg-4 audio config... this is ok.
134 * NeAACDecInit2() will simply use default values instead.
136 buf = NULL;
137 buf_size = 0;
140 /* init decoder according to mpeg-4 audio config
141 * cast to signed char because char is unsigned by default on ppc */
142 if ((signed char)NeAACDecInit2(priv->decoder, buf, buf_size, &priv->sample_rate, &priv->channels) < 0) {
143 free(buf);
144 goto out;
147 free(buf);
149 d_print("sample rate %luhz, channels %u\n", priv->sample_rate, priv->channels);
151 ip_data->sf = sf_rate(priv->sample_rate) | sf_channels(priv->channels) | sf_bits(16) | sf_signed(1);
152 #if defined(WORDS_BIGENDIAN)
153 ip_data->sf |= sf_bigendian(1);
154 #endif
156 return 0;
158 out:
159 if (priv->mp4.handle)
160 MP4Close(priv->mp4.handle);
161 if (priv->decoder)
162 NeAACDecClose(priv->decoder);
163 free(priv);
164 return -IP_ERROR_FILE_FORMAT;
167 static int mp4_close(struct input_plugin_data *ip_data)
169 struct mp4_private *priv;
171 priv = ip_data->private;
173 if (priv->mp4.handle)
174 MP4Close(priv->mp4.handle);
176 if (priv->decoder)
177 NeAACDecClose(priv->decoder);
179 free(priv);
180 ip_data->private = NULL;
182 return 0;
185 /* returns -1 on fatal errors
186 * returns -2 on non-fatal errors
187 * 0 on eof
188 * number of bytes put in 'buffer' on success */
189 static int decode_one_frame(struct input_plugin_data *ip_data, void *buffer, int count)
191 struct mp4_private *priv;
192 unsigned char *aac_data = NULL;
193 unsigned int aac_data_len = 0;
194 NeAACDecFrameInfo frame_info;
195 char *sample_buf;
196 int bytes;
198 priv = ip_data->private;
200 BUG_ON(priv->overflow_buf_len);
202 if (priv->mp4.sample > priv->mp4.num_samples)
203 return 0; /* EOF */
205 if (MP4ReadSample(priv->mp4.handle, priv->mp4.track, priv->mp4.sample,
206 &aac_data, &aac_data_len, NULL, NULL, NULL, NULL) == 0) {
207 d_print("error reading mp4 sample %d\n", priv->mp4.sample);
208 errno = EINVAL;
209 return -1;
212 priv->mp4.sample++;
214 if (!aac_data) {
215 d_print("aac_data == NULL\n");
216 errno = EINVAL;
217 return -1;
220 sample_buf = NeAACDecDecode(priv->decoder, &frame_info, aac_data, aac_data_len);
222 free(aac_data);
224 if (!sample_buf || frame_info.bytesconsumed <= 0) {
225 d_print("fatal error: %s\n", NeAACDecGetErrorMessage(frame_info.error));
226 errno = EINVAL;
227 return -1;
230 if (frame_info.error != 0) {
231 d_print("frame error: %s\n", NeAACDecGetErrorMessage(frame_info.error));
232 return -2;
235 if (frame_info.samples <= 0)
236 return -2;
238 if (frame_info.channels != priv->channels || frame_info.samplerate != priv->sample_rate) {
239 d_print("invalid channel or sample_rate count\n");
240 return -2;
243 /* 16-bit samples */
244 bytes = frame_info.samples * 2;
246 if (bytes > count) {
247 /* decoded too much; keep overflow. this only happens
248 * when we've called NeAACDecDecode_bugfree() */
249 priv->overflow_buf = sample_buf + count;
250 priv->overflow_buf_len = bytes - count;
251 memcpy(buffer, sample_buf, count);
252 return count;
253 } else {
254 memcpy(buffer, sample_buf, bytes);
257 return bytes;
260 static int mp4_read(struct input_plugin_data *ip_data, char *buffer, int count)
262 struct mp4_private *priv;
263 int rc;
265 priv = ip_data->private;
267 /* use overflow from previous call (if any) */
268 if (priv->overflow_buf_len > 0) {
269 int len = priv->overflow_buf_len;
271 if (len > count)
272 len = count;
274 memcpy(buffer, priv->overflow_buf, len);
275 priv->overflow_buf += len;
276 priv->overflow_buf_len -= len;
278 return len;
281 do {
282 rc = decode_one_frame(ip_data, buffer, count);
283 } while (rc == -2);
285 return rc;
288 static int mp4_seek(struct input_plugin_data *ip_data, double offset)
290 struct mp4_private *priv;
291 MP4SampleId sample;
292 uint32_t scale;
294 priv = ip_data->private;
296 scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track);
297 if (scale == 0)
298 return -IP_ERROR_INTERNAL;
300 sample = MP4GetSampleIdFromTime(priv->mp4.handle, priv->mp4.track,
301 (MP4Timestamp)(offset * (double)scale), 0);
302 if (sample == MP4_INVALID_SAMPLE_ID)
303 return -IP_ERROR_INTERNAL;
305 priv->mp4.sample = sample;
307 d_print("seeking to sample %d\n", sample);
309 return 0;
312 /* used by mp4_read_comments only. 'val' should be malloced */
313 static void push_comment(struct keyval **comments, int *count, const char *key, char *val)
315 *comments = xrenew(struct keyval, *comments, *count + 2);
316 (*comments)[*count+0].key = xstrdup(key);
317 (*comments)[*count+0].val = val;
318 (*comments)[*count+1].key = NULL;
319 (*comments)[*count+1].val = NULL;
320 (*count)++;
323 static int mp4_read_comments(struct input_plugin_data *ip_data,
324 struct keyval **comments)
326 struct mp4_private *priv;
327 uint16_t meta_num, meta_total;
328 uint8_t val;
329 /*uint8_t *ustr;
330 uint32_t size;*/
331 char *str;
332 int n = 0;
334 priv = ip_data->private;
336 *comments = NULL;
338 /* MP4GetMetadata* provides malloced pointers, and the data
339 * is in UTF-8 (or at least it should be). */
340 if (MP4GetMetadataArtist(priv->mp4.handle, &str))
341 push_comment(comments, &n, "artist", str);
342 if (MP4GetMetadataAlbum(priv->mp4.handle, &str))
343 push_comment(comments, &n, "album", str);
344 if (MP4GetMetadataName(priv->mp4.handle, &str))
345 push_comment(comments, &n, "title", str);
346 if (MP4GetMetadataGenre(priv->mp4.handle, &str))
347 push_comment(comments, &n, "genre", str);
348 if (MP4GetMetadataYear(priv->mp4.handle, &str))
349 push_comment(comments, &n, "date", str);
351 if (MP4GetMetadataCompilation(priv->mp4.handle, &val))
352 push_comment(comments, &n, "compilation", xstrdup(val ? "yes" : "no"));
353 #if 0
354 if (MP4GetBytesProperty(priv->mp4.handle, "moov.udta.meta.ilst.aART.data", &ustr, &size)) {
355 char *xstr;
357 /* What's this?
358 * This is the result from lack of documentation.
359 * It's supposed to return just a string, but it
360 * returns an additional 16 bytes of junk at the
361 * beginning. Could be a bug. Could be intentional.
362 * Hopefully this works around it:
364 if (ustr[0] == 0 && size > 16) {
365 ustr += 16;
366 size -= 16;
368 xstr = xmalloc(size + 1);
369 memcpy(xstr, ustr, size);
370 xstr[size] = 0;
371 push_comment(comments, &n, "albumartist", xstr);
373 #endif
374 if (MP4GetMetadataTrack(priv->mp4.handle, &meta_num, &meta_total)) {
375 str = xmalloc(6);
376 snprintf(str, 6, "%u", meta_num);
377 push_comment(comments, &n, "tracknumber", str);
379 if (MP4GetMetadataDisk(priv->mp4.handle, &meta_num, &meta_total)) {
380 str = xmalloc(6);
381 snprintf(str, 6, "%u", meta_num);
382 push_comment(comments, &n, "discnumber", str);
385 if (*comments == NULL)
386 *comments = xnew0(struct keyval, 1);
388 return 0;
391 static int mp4_duration(struct input_plugin_data *ip_data)
393 struct mp4_private *priv;
394 uint32_t scale;
395 uint64_t duration;
397 priv = ip_data->private;
399 scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track);
400 if (scale == 0)
401 return 0;
403 duration = MP4GetTrackDuration(priv->mp4.handle, priv->mp4.track);
405 return duration / scale;
408 const struct input_plugin_ops ip_ops = {
409 .open = mp4_open,
410 .close = mp4_close,
411 .read = mp4_read,
412 .seek = mp4_seek,
413 .read_comments = mp4_read_comments,
414 .duration = mp4_duration
417 const char * const ip_extensions[] = { "mp4", "m4a", "m4b", NULL };
418 const char * const ip_mime_types[] = { /*"audio/mp4", "audio/mp4a-latm",*/ NULL };