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
30 #include <sys/types.h>
37 unsigned char channels
;
38 unsigned long sample_rate
;
40 NeAACDecHandle decoder
; /* typedef void * */
43 MP4FileHandle handle
; /* typedef void * */
47 MP4SampleId num_samples
;
52 static MP4TrackId
mp4_get_track(MP4FileHandle
*handle
)
54 MP4TrackId num_tracks
;
55 const char *track_type
;
59 num_tracks
= MP4GetNumberOfTracks(handle
, NULL
, 0);
61 for (i
= 1; i
<= num_tracks
; i
++) {
62 track_type
= MP4GetTrackType(handle
, i
);
66 if (!MP4_IS_AUDIO_TRACK_TYPE(track_type
))
69 /* MP4GetTrackAudioType */
70 obj_type
= MP4GetTrackEsdsObjectTypeId(handle
, i
);
71 if (obj_type
== MP4_INVALID_AUDIO_TYPE
)
74 if (obj_type
== MP4_MPEG4_AUDIO_TYPE
) {
75 obj_type
= MP4GetTrackAudioMpeg4Type(handle
, i
);
77 if (MP4_IS_MPEG4_AAC_AUDIO_TYPE(obj_type
))
80 if (MP4_IS_AAC_AUDIO_TYPE(obj_type
))
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
;
93 unsigned int buf_size
;
96 /* http://sourceforge.net/forum/message.php?msg_id=3578887 */
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");
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");
126 priv
->mp4
.num_samples
= MP4GetTrackNumberOfSamples(priv
->mp4
.handle
, priv
->mp4
.track
);
128 priv
->mp4
.sample
= 1;
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.
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) {
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);
159 if (priv
->mp4
.handle
)
160 MP4Close(priv
->mp4
.handle
);
162 NeAACDecClose(priv
->decoder
);
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
);
177 NeAACDecClose(priv
->decoder
);
180 ip_data
->private = NULL
;
185 /* returns -1 on fatal errors
186 * returns -2 on non-fatal errors
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
;
198 priv
= ip_data
->private;
200 BUG_ON(priv
->overflow_buf_len
);
202 if (priv
->mp4
.sample
> priv
->mp4
.num_samples
)
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
);
215 d_print("aac_data == NULL\n");
220 sample_buf
= NeAACDecDecode(priv
->decoder
, &frame_info
, aac_data
, aac_data_len
);
224 if (!sample_buf
|| frame_info
.bytesconsumed
<= 0) {
225 d_print("fatal error: %s\n", NeAACDecGetErrorMessage(frame_info
.error
));
230 if (frame_info
.error
!= 0) {
231 d_print("frame error: %s\n", NeAACDecGetErrorMessage(frame_info
.error
));
235 if (frame_info
.samples
<= 0)
238 if (frame_info
.channels
!= priv
->channels
|| frame_info
.samplerate
!= priv
->sample_rate
) {
239 d_print("invalid channel or sample_rate count\n");
244 bytes
= frame_info
.samples
* 2;
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
);
254 memcpy(buffer
, sample_buf
, bytes
);
260 static int mp4_read(struct input_plugin_data
*ip_data
, char *buffer
, int count
)
262 struct mp4_private
*priv
;
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
;
274 memcpy(buffer
, priv
->overflow_buf
, len
);
275 priv
->overflow_buf
+= len
;
276 priv
->overflow_buf_len
-= len
;
282 rc
= decode_one_frame(ip_data
, buffer
, count
);
288 static int mp4_seek(struct input_plugin_data
*ip_data
, double offset
)
290 struct mp4_private
*priv
;
294 priv
= ip_data
->private;
296 scale
= MP4GetTrackTimeScale(priv
->mp4
.handle
, priv
->mp4
.track
);
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
);
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
;
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
;
334 priv
= ip_data
->private;
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"));
354 if (MP4GetBytesProperty(priv
->mp4
.handle
, "moov.udta.meta.ilst.aART.data", &ustr
, &size
)) {
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) {
368 xstr
= xmalloc(size
+ 1);
369 memcpy(xstr
, ustr
, size
);
371 push_comment(comments
, &n
, "albumartist", xstr
);
374 if (MP4GetMetadataTrack(priv
->mp4
.handle
, &meta_num
, &meta_total
)) {
376 snprintf(str
, 6, "%u", meta_num
);
377 push_comment(comments
, &n
, "tracknumber", str
);
379 if (MP4GetMetadataDisk(priv
->mp4
.handle
, &meta_num
, &meta_total
)) {
381 snprintf(str
, 6, "%u", meta_num
);
382 push_comment(comments
, &n
, "discnumber", str
);
385 if (*comments
== NULL
)
386 *comments
= xnew0(struct keyval
, 1);
391 static int mp4_duration(struct input_plugin_data
*ip_data
)
393 struct mp4_private
*priv
;
397 priv
= ip_data
->private;
399 scale
= MP4GetTrackTimeScale(priv
->mp4
.handle
, priv
->mp4
.track
);
403 duration
= MP4GetTrackDuration(priv
->mp4
.handle
, priv
->mp4
.track
);
405 return duration
/ scale
;
408 const struct input_plugin_ops ip_ops
= {
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
};