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 faacDecHandle 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 faacDecConfigurationPtr 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
= faacDecOpen();
106 /* set decoder config */
107 neaac_cfg
= faacDecGetCurrentConfiguration(priv
->decoder
);
108 neaac_cfg
->outputFormat
= FAAD_FMT_16BIT
; /* force 16 bit audio */
109 neaac_cfg
->downMatrix
= 1; /* 5.1 -> stereo */
110 faacDecSetConfiguration(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 * faacDecInit2() will simply use default values instead.
140 /* init decoder according to mpeg-4 audio config */
141 if (faacDecInit2(priv
->decoder
, buf
, buf_size
, &priv
->sample_rate
, &priv
->channels
) < 0) {
148 d_print("sample rate %luhz, channels %u\n", priv
->sample_rate
, priv
->channels
);
150 ip_data
->sf
= sf_rate(priv
->sample_rate
) | sf_channels(priv
->channels
) | sf_bits(16) | sf_signed(1);
151 #if defined(WORDS_BIGENDIAN)
152 ip_data
->sf
|= sf_bigendian(1);
158 if (priv
->mp4
.handle
)
159 MP4Close(priv
->mp4
.handle
);
161 faacDecClose(priv
->decoder
);
163 return -IP_ERROR_FILE_FORMAT
;
166 static int mp4_close(struct input_plugin_data
*ip_data
)
168 struct mp4_private
*priv
;
170 priv
= ip_data
->private;
172 if (priv
->mp4
.handle
)
173 MP4Close(priv
->mp4
.handle
);
176 faacDecClose(priv
->decoder
);
179 ip_data
->private = NULL
;
184 /* returns -1 on fatal errors
185 * returns -2 on non-fatal errors
187 * number of bytes put in 'buffer' on success */
188 static int decode_one_frame(struct input_plugin_data
*ip_data
, void *buffer
, int count
)
190 struct mp4_private
*priv
;
191 unsigned char *aac_data
= NULL
;
192 unsigned int aac_data_len
= 0;
193 faacDecFrameInfo frame_info
;
197 priv
= ip_data
->private;
199 BUG_ON(priv
->overflow_buf_len
);
201 if (priv
->mp4
.sample
> priv
->mp4
.num_samples
)
204 if (MP4ReadSample(priv
->mp4
.handle
, priv
->mp4
.track
, priv
->mp4
.sample
,
205 &aac_data
, &aac_data_len
, NULL
, NULL
, NULL
, NULL
) == 0) {
206 d_print("error reading mp4 sample %d\n", priv
->mp4
.sample
);
214 d_print("aac_data == NULL\n");
219 sample_buf
= faacDecDecode(priv
->decoder
, &frame_info
, aac_data
, aac_data_len
);
223 if (!sample_buf
|| frame_info
.bytesconsumed
<= 0) {
224 d_print("fatal error: %s\n", faacDecGetErrorMessage(frame_info
.error
));
229 if (frame_info
.error
!= 0) {
230 d_print("frame error: %s\n", faacDecGetErrorMessage(frame_info
.error
));
234 if (frame_info
.samples
<= 0)
237 if (frame_info
.channels
!= priv
->channels
|| frame_info
.samplerate
!= priv
->sample_rate
) {
238 d_print("invalid channel or sample_rate count\n");
243 bytes
= frame_info
.samples
* 2;
246 /* decoded too much; keep overflow. */
247 priv
->overflow_buf
= sample_buf
+ count
;
248 priv
->overflow_buf_len
= bytes
- count
;
249 memcpy(buffer
, sample_buf
, count
);
252 memcpy(buffer
, sample_buf
, bytes
);
258 static int mp4_read(struct input_plugin_data
*ip_data
, char *buffer
, int count
)
260 struct mp4_private
*priv
;
263 priv
= ip_data
->private;
265 /* use overflow from previous call (if any) */
266 if (priv
->overflow_buf_len
> 0) {
267 int len
= priv
->overflow_buf_len
;
272 memcpy(buffer
, priv
->overflow_buf
, len
);
273 priv
->overflow_buf
+= len
;
274 priv
->overflow_buf_len
-= len
;
280 rc
= decode_one_frame(ip_data
, buffer
, count
);
286 static int mp4_seek(struct input_plugin_data
*ip_data
, double offset
)
288 struct mp4_private
*priv
;
292 priv
= ip_data
->private;
294 scale
= MP4GetTrackTimeScale(priv
->mp4
.handle
, priv
->mp4
.track
);
296 return -IP_ERROR_INTERNAL
;
298 sample
= MP4GetSampleIdFromTime(priv
->mp4
.handle
, priv
->mp4
.track
,
299 (MP4Timestamp
)(offset
* (double)scale
), 0);
300 if (sample
== MP4_INVALID_SAMPLE_ID
)
301 return -IP_ERROR_INTERNAL
;
303 priv
->mp4
.sample
= sample
;
305 d_print("seeking to sample %d\n", sample
);
310 static int mp4_read_comments(struct input_plugin_data
*ip_data
,
311 struct keyval
**comments
)
313 struct mp4_private
*priv
;
314 uint16_t meta_num
, meta_total
;
321 priv
= ip_data
->private;
323 /* MP4GetMetadata* provides malloced pointers, and the data
324 * is in UTF-8 (or at least it should be). */
325 if (MP4GetMetadataArtist(priv
->mp4
.handle
, &str
))
326 comments_add(&c
, "artist", str
);
327 if (MP4GetMetadataAlbum(priv
->mp4
.handle
, &str
))
328 comments_add(&c
, "album", str
);
329 if (MP4GetMetadataName(priv
->mp4
.handle
, &str
))
330 comments_add(&c
, "title", str
);
331 if (MP4GetMetadataGenre(priv
->mp4
.handle
, &str
))
332 comments_add(&c
, "genre", str
);
333 if (MP4GetMetadataYear(priv
->mp4
.handle
, &str
))
334 comments_add(&c
, "date", str
);
336 if (MP4GetMetadataCompilation(priv
->mp4
.handle
, &val
))
337 comments_add_const(&c
, "compilation", val
? "yes" : "no");
339 if (MP4GetBytesProperty(priv
->mp4
.handle
, "moov.udta.meta.ilst.aART.data", &ustr
, &size
)) {
343 * This is the result from lack of documentation.
344 * It's supposed to return just a string, but it
345 * returns an additional 16 bytes of junk at the
346 * beginning. Could be a bug. Could be intentional.
347 * Hopefully this works around it:
349 if (ustr
[0] == 0 && size
> 16) {
353 xstr
= xmalloc(size
+ 1);
354 memcpy(xstr
, ustr
, size
);
356 comments_add(&c
, "albumartist", xstr
);
360 if (MP4GetMetadataTrack(priv
->mp4
.handle
, &meta_num
, &meta_total
)) {
362 snprintf(buf
, 6, "%u", meta_num
);
363 comments_add_const(&c
, "tracknumber", buf
);
365 if (MP4GetMetadataDisk(priv
->mp4
.handle
, &meta_num
, &meta_total
)) {
367 snprintf(buf
, 6, "%u", meta_num
);
368 comments_add_const(&c
, "discnumber", buf
);
371 comments_terminate(&c
);
372 *comments
= c
.comments
;
376 static int mp4_duration(struct input_plugin_data
*ip_data
)
378 struct mp4_private
*priv
;
382 priv
= ip_data
->private;
384 scale
= MP4GetTrackTimeScale(priv
->mp4
.handle
, priv
->mp4
.track
);
388 duration
= MP4GetTrackDuration(priv
->mp4
.handle
, priv
->mp4
.track
);
390 return duration
/ scale
;
393 const struct input_plugin_ops ip_ops
= {
398 .read_comments
= mp4_read_comments
,
399 .duration
= mp4_duration
402 const char * const ip_extensions
[] = { "mp4", "m4a", "m4b", NULL
};
403 const char * const ip_mime_types
[] = { /*"audio/mp4", "audio/mp4a-latm",*/ NULL
};