1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU 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 02111-1307 USA
19 #include "../inputPlugin.h"
26 #include "../pcm_utils.h"
27 #include "../inputStream.h"
28 #include "../outputBuffer.h"
29 #include "../decode.h"
31 #include "../mp4ff/mp4ff.h"
39 /* all code here is either based on or copied from FAAD2's frontend code */
41 static int mp4_getAACTrack(mp4ff_t
* infile
)
45 int numTracks
= mp4ff_total_tracks(infile
);
47 for (i
= 0; i
< numTracks
; i
++) {
48 unsigned char *buff
= NULL
;
49 unsigned int buff_size
= 0;
50 #ifdef HAVE_MP4AUDIOSPECIFICCONFIG
51 mp4AudioSpecificConfig mp4ASC
;
53 unsigned long dummy1_32
;
54 unsigned char dummy2_8
, dummy3_8
, dummy4_8
, dummy5_8
, dummy6_8
,
58 mp4ff_get_decoder_config(infile
, i
, &buff
, &buff_size
);
61 #ifdef HAVE_MP4AUDIOSPECIFICCONFIG
62 rc
= AudioSpecificConfig(buff
, buff_size
, &mp4ASC
);
64 rc
= AudioSpecificConfig(buff
, &dummy1_32
, &dummy2_8
,
67 &dummy7_8
, &dummy8_8
);
76 /* can't decode this */
80 static uint32_t mp4_inputStreamReadCallback(void *inStream
, void *buffer
,
83 return readFromInputStream((InputStream
*) inStream
, buffer
, 1, length
);
86 static uint32_t mp4_inputStreamSeekCallback(void *inStream
, uint64_t position
)
88 return seekInputStream((InputStream
*) inStream
, position
, SEEK_SET
);
91 static int mp4_decode(OutputBuffer
* cb
, DecoderControl
* dc
,
92 InputStream
* inStream
)
95 mp4ff_callback_t
*mp4cb
;
99 faacDecHandle decoder
;
100 faacDecFrameInfo frameInfo
;
101 faacDecConfigurationPtr config
;
102 unsigned char *mp4Buffer
;
103 unsigned int mp4BufferSize
;
104 unsigned long sampleRate
;
105 unsigned char channels
;
110 unsigned int sampleCount
;
112 size_t sampleBufferLen
;
113 unsigned int initial
= 1;
115 long seekTableEnd
= -1;
116 int seekPositionFound
= 0;
118 mpd_uint16 bitRate
= 0;
121 mp4cb
= xmalloc(sizeof(mp4ff_callback_t
));
122 mp4cb
->read
= mp4_inputStreamReadCallback
;
123 mp4cb
->seek
= mp4_inputStreamSeekCallback
;
124 mp4cb
->user_data
= inStream
;
126 mp4fh
= mp4ff_open_read(mp4cb
);
128 ERROR("Input does not appear to be a mp4 stream.\n");
130 closeInputStream(inStream
);
134 track
= mp4_getAACTrack(mp4fh
);
136 ERROR("No AAC track found in mp4 stream.\n");
138 closeInputStream(inStream
);
143 decoder
= faacDecOpen();
145 config
= faacDecGetCurrentConfiguration(decoder
);
146 config
->outputFormat
= FAAD_FMT_16BIT
;
147 #ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
148 config
->downMatrix
= 1;
150 #ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
151 config
->dontUpSampleImplicitSBR
= 0;
153 faacDecSetConfiguration(decoder
, config
);
155 dc
->audioFormat
.bits
= 16;
159 mp4ff_get_decoder_config(mp4fh
, track
, &mp4Buffer
, &mp4BufferSize
);
162 (decoder
, mp4Buffer
, mp4BufferSize
, &sampleRate
, &channels
) < 0) {
163 ERROR("Error not a AAC stream.\n");
164 faacDecClose(decoder
);
167 closeInputStream(inStream
);
171 dc
->audioFormat
.sampleRate
= sampleRate
;
172 dc
->audioFormat
.channels
= channels
;
173 time
= mp4ff_get_track_duration_use_offsets(mp4fh
, track
);
174 scale
= mp4ff_time_scale(mp4fh
, track
);
180 ERROR("Error getting audio format of mp4 AAC track.\n");
181 faacDecClose(decoder
);
183 closeInputStream(inStream
);
187 dc
->totalTime
= ((float)time
) / scale
;
189 numSamples
= mp4ff_num_samples(mp4fh
, track
);
193 seekTable
= xmalloc(sizeof(float) * numSamples
);
195 for (sampleId
= 0; sampleId
< numSamples
&& !eof
; sampleId
++) {
199 if (seeking
&& seekTableEnd
> 1 &&
200 seekTable
[seekTableEnd
] >= dc
->seekWhere
) {
202 while (seekTable
[i
] < dc
->seekWhere
)
205 time
= seekTable
[sampleId
];
208 dur
= mp4ff_get_sample_duration(mp4fh
, track
, sampleId
);
209 offset
= mp4ff_get_sample_offset(mp4fh
, track
, sampleId
);
211 if (sampleId
> seekTableEnd
) {
212 seekTable
[sampleId
] = time
;
213 seekTableEnd
= sampleId
;
222 time
+= ((float)dur
) / scale
;
224 if (seeking
&& time
> dc
->seekWhere
)
225 seekPositionFound
= 1;
227 if (seeking
&& seekPositionFound
) {
228 seekPositionFound
= 0;
229 clearOutputBuffer(cb
);
237 if (mp4ff_read_sample(mp4fh
, track
, sampleId
, &mp4Buffer
,
238 &mp4BufferSize
) == 0) {
242 #ifdef HAVE_FAAD_BUFLEN_FUNCS
243 sampleBuffer
= faacDecDecode(decoder
, &frameInfo
, mp4Buffer
,
246 sampleBuffer
= faacDecDecode(decoder
, &frameInfo
, mp4Buffer
);
251 if (frameInfo
.error
> 0) {
252 ERROR("faad2 error: %s\n",
253 faacDecGetErrorMessage(frameInfo
.error
));
258 if (dc
->state
!= DECODE_STATE_DECODE
) {
259 channels
= frameInfo
.channels
;
260 #ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
261 scale
= frameInfo
.samplerate
;
263 dc
->audioFormat
.sampleRate
= scale
;
264 dc
->audioFormat
.channels
= frameInfo
.channels
;
265 getOutputAudioFormat(&(dc
->audioFormat
),
267 dc
->state
= DECODE_STATE_DECODE
;
270 if (channels
* (dur
+ offset
) > frameInfo
.samples
) {
271 dur
= frameInfo
.samples
/ channels
;
275 sampleCount
= (unsigned long)(dur
* channels
);
277 if (sampleCount
> 0) {
279 bitRate
= frameInfo
.bytesconsumed
* 8.0 *
280 frameInfo
.channels
* scale
/
281 frameInfo
.samples
/ 1000 + 0.5;
284 sampleBufferLen
= sampleCount
* 2;
286 sampleBuffer
+= offset
* channels
* 2;
288 sendDataToOutputBuffer(cb
, NULL
, dc
, 1, sampleBuffer
,
289 sampleBufferLen
, time
, bitRate
, NULL
);
297 faacDecClose(decoder
);
299 closeInputStream(inStream
);
302 if (dc
->state
!= DECODE_STATE_DECODE
)
305 if (dc
->seek
&& seeking
) {
306 clearOutputBuffer(cb
);
309 flushOutputBuffer(cb
);
312 dc
->state
= DECODE_STATE_STOP
;
315 dc
->state
= DECODE_STATE_STOP
;
320 static MpdTag
*mp4DataDup(char *file
, int *mp4MetadataFound
)
323 InputStream inStream
;
325 mp4ff_callback_t
*cb
;
331 *mp4MetadataFound
= 0;
333 if (openInputStream(&inStream
, file
) < 0) {
334 DEBUG("mp4DataDup: Failed to open file: %s\n", file
);
338 cb
= xmalloc(sizeof(mp4ff_callback_t
));
339 cb
->read
= mp4_inputStreamReadCallback
;
340 cb
->seek
= mp4_inputStreamSeekCallback
;
341 cb
->user_data
= &inStream
;
343 mp4fh
= mp4ff_open_read(cb
);
346 closeInputStream(&inStream
);
350 track
= mp4_getAACTrack(mp4fh
);
353 closeInputStream(&inStream
);
359 time
= mp4ff_get_track_duration_use_offsets(mp4fh
, track
);
360 scale
= mp4ff_time_scale(mp4fh
, track
);
363 closeInputStream(&inStream
);
368 ret
->time
= ((float)time
) / scale
+ 0.5;
370 for (i
= 0; i
< mp4ff_meta_get_num_items(mp4fh
); i
++) {
374 mp4ff_meta_get_by_index(mp4fh
, i
, &item
, &value
);
376 if (0 == strcasecmp("artist", item
)) {
377 addItemToMpdTag(ret
, TAG_ITEM_ARTIST
, value
);
378 *mp4MetadataFound
= 1;
379 } else if (0 == strcasecmp("title", item
)) {
380 addItemToMpdTag(ret
, TAG_ITEM_TITLE
, value
);
381 *mp4MetadataFound
= 1;
382 } else if (0 == strcasecmp("album", item
)) {
383 addItemToMpdTag(ret
, TAG_ITEM_ALBUM
, value
);
384 *mp4MetadataFound
= 1;
385 } else if (0 == strcasecmp("track", item
)) {
386 addItemToMpdTag(ret
, TAG_ITEM_TRACK
, value
);
387 *mp4MetadataFound
= 1;
388 } else if (0 == strcasecmp("disc", item
)) { /* Is that the correct id? */
389 addItemToMpdTag(ret
, TAG_ITEM_DISC
, value
);
390 *mp4MetadataFound
= 1;
391 } else if (0 == strcasecmp("genre", item
)) {
392 addItemToMpdTag(ret
, TAG_ITEM_GENRE
, value
);
393 *mp4MetadataFound
= 1;
394 } else if (0 == strcasecmp("date", item
)) {
395 addItemToMpdTag(ret
, TAG_ITEM_DATE
, value
);
396 *mp4MetadataFound
= 1;
404 closeInputStream(&inStream
);
410 static MpdTag
*mp4TagDup(char *file
)
413 int mp4MetadataFound
= 0;
415 ret
= mp4DataDup(file
, &mp4MetadataFound
);
418 if (!mp4MetadataFound
) {
419 MpdTag
*temp
= id3Dup(file
);
421 temp
->time
= ret
->time
;
430 static char *mp4_suffixes
[] = { "m4a", "mp4", NULL
};
431 static char *mp4_mimeTypes
[] = { "audio/mp4", "audio/m4a", NULL
};
433 InputPlugin mp4Plugin
= {
441 INPUT_PLUGIN_STREAM_FILE
| INPUT_PLUGIN_STREAM_URL
,
448 InputPlugin mp4Plugin
;
450 #endif /* HAVE_FAAD */