Merge lp:~neale/mixxx/m4a into lp:~mixxxdevelopers/mixxx/trunk
- m4a
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~neale/mixxx/m4a |
Merge into: | lp:~mixxxdevelopers/mixxx/trunk |
Diff against target: |
1832 lines (+338/-1341) 9 files modified
mixxx/build/features.py (+5/-10) mixxx/plugins/soundsourcem4a/SConscript (+1/-8) mixxx/plugins/soundsourcem4a/m4a/comment.h (+0/-30) mixxx/plugins/soundsourcem4a/m4a/ip.h (+0/-99) mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp (+0/-587) mixxx/plugins/soundsourcem4a/m4a/mp4.c (+0/-407) mixxx/plugins/soundsourcem4a/m4a/sf.h (+0/-61) mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp (+316/-129) mixxx/plugins/soundsourcem4a/soundsourcem4a.h (+16/-10) |
To merge this branch: | bzr merge lp:~neale/mixxx/m4a |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stefan Nürnberger (community) | Needs Information | ||
RJ Skerry-Ryan | Pending | ||
Review via email: mp+121310@code.launchpad.net |
Commit message
Description of the change
Rewritten M4A plugin, uses libmp4ff instead of libmp4v2. Works with all my M4A files, and I will maintain the code if people can send me examples of problem files.
Stefan Nürnberger (kabelfrickler) wrote : | # |
Neale Pickett (neale) wrote : | # |
> I therefore propose to keep the old implementation using mp4v2. What is/was
> the reason for using mp4ff instead, anyway?
Yeah, it should use mp4v2.
The reason I moved to mp4ff is because I couldn't find example code for mp4v2. The original motivation for this branch was that I have a whole lot of m4a files with frames larger than the current implementation can handle.
I'll port this branch to mp4v2 and submit another merge request when that work is done. The main thing this patch does is make mixxx work with all m4a files I can find, the container parsing library isn't significant.
Unmerged revisions
- 3293. By Neale Pickett
-
Rewritten M4A plugin
Preview Diff
1 | === modified file 'mixxx/build/features.py' |
2 | --- mixxx/build/features.py 2012-07-08 07:30:32 +0000 |
3 | +++ mixxx/build/features.py 2012-08-25 14:26:19 +0000 |
4 | @@ -465,21 +465,16 @@ |
5 | if not self.enabled(build): |
6 | return |
7 | |
8 | - have_mp4v2_h = conf.CheckHeader('mp4v2/mp4v2.h') |
9 | - have_mp4v2 = conf.CheckLib(['mp4v2','libmp4v2'], autoadd=False) |
10 | - have_mp4 = conf.CheckLib('mp4', autoadd=False) |
11 | - |
12 | - # Either mp4 or mp4v2 works |
13 | - have_mp4 = (have_mp4v2_h and have_mp4v2) or have_mp4 |
14 | - |
15 | - if not have_mp4: |
16 | - raise Exception('Could not find libmp4, libmp4v2 or the libmp4v2 development headers.') |
17 | - |
18 | have_faad = conf.CheckLib(['faad','libfaad'], autoadd=False) |
19 | |
20 | if not have_faad: |
21 | raise Exception('Could not find libfaad or the libfaad development headers.') |
22 | |
23 | + have_mp4ff = conf.CheckLib(['mp4ff', 'libmp4ff'], autoadd=False) |
24 | + |
25 | + if not have_mp4ff: |
26 | + raise Exception('Could not find libmp4ff or the libmp4ff development headers.') |
27 | + |
28 | |
29 | class WavPack(Feature): |
30 | def description(self): |
31 | |
32 | === modified file 'mixxx/plugins/soundsourcem4a/SConscript' |
33 | --- mixxx/plugins/soundsourcem4a/SConscript 2011-03-07 22:25:20 +0000 |
34 | +++ mixxx/plugins/soundsourcem4a/SConscript 2012-08-25 14:26:19 +0000 |
35 | @@ -23,9 +23,7 @@ |
36 | |
37 | conf = Configure(env) |
38 | |
39 | - have_mp4v2_h = conf.CheckHeader('mp4v2/mp4v2.h') |
40 | - have_mp4 = (have_mp4v2_h and conf.CheckLib(['mp4v2', 'libmp4v2'])) or \ |
41 | - conf.CheckLib('mp4') |
42 | + have_mp4ff = conf.CheckLib(['mp4ff', 'libmp4ff']) |
43 | |
44 | have_faad = conf.CheckLib(['faad', 'libfaad']) |
45 | have_faad_26 = False |
46 | @@ -47,8 +45,6 @@ |
47 | if have_faad_26: |
48 | env.Append(CPPDEFINES = '__M4AHACK__') |
49 | print "libfaad 2.6 compatibility mode... enabled" |
50 | - if have_mp4v2_h: |
51 | - env.Append(CPPDEFINES = '__MP4V2__') |
52 | |
53 | env = conf.Finish() |
54 | SHLIBPREFIX='lib' #Makes the filename "libsoundsourcem4a" consistently across platforms to make our lives easier. |
55 | @@ -63,6 +59,3 @@ |
56 | Return("ssm4a_bin") |
57 | else: |
58 | Return("") |
59 | - |
60 | - |
61 | - |
62 | |
63 | === removed directory 'mixxx/plugins/soundsourcem4a/m4a' |
64 | === removed file 'mixxx/plugins/soundsourcem4a/m4a/comment.h' |
65 | --- mixxx/plugins/soundsourcem4a/m4a/comment.h 2008-12-14 09:12:07 +0000 |
66 | +++ mixxx/plugins/soundsourcem4a/m4a/comment.h 1970-01-01 00:00:00 +0000 |
67 | @@ -1,30 +0,0 @@ |
68 | -#ifndef _COMMENT_H |
69 | -#define _COMMENT_H |
70 | - |
71 | -struct keyval { |
72 | - char *key; |
73 | - char *val; |
74 | -}; |
75 | - |
76 | -struct growing_keyvals { |
77 | - struct keyval *comments; |
78 | - int alloc; |
79 | - int count; |
80 | -}; |
81 | - |
82 | -#define GROWING_KEYVALS(name) struct growing_keyvals name = { NULL, 0, 0 } |
83 | - |
84 | -struct keyval *comments_dup(const struct keyval *comments); |
85 | -void comments_free(struct keyval *comments); |
86 | - |
87 | -/* case insensitive key */ |
88 | -const char *comments_get_val(const struct keyval *comments, const char *key); |
89 | -const char *comments_get_albumartist(const struct keyval *comments); |
90 | -int comments_get_int(const struct keyval *comments, const char *key); |
91 | -int comments_get_date(const struct keyval *comments, const char *key); |
92 | - |
93 | -int comments_add(struct growing_keyvals *c, const char *key, char *val); |
94 | -int comments_add_const(struct growing_keyvals *c, const char *key, const char *val); |
95 | -void comments_terminate(struct growing_keyvals *c); |
96 | - |
97 | -#endif |
98 | |
99 | === removed file 'mixxx/plugins/soundsourcem4a/m4a/ip.h' |
100 | --- mixxx/plugins/soundsourcem4a/m4a/ip.h 2008-12-14 09:12:07 +0000 |
101 | +++ mixxx/plugins/soundsourcem4a/m4a/ip.h 1970-01-01 00:00:00 +0000 |
102 | @@ -1,99 +0,0 @@ |
103 | -/* |
104 | - * Copyright 2004-2005 Timo Hirvonen |
105 | - * |
106 | - * This program is free software; you can redistribute it and/or |
107 | - * modify it under the terms of the GNU General Public License as |
108 | - * published by the Free Software Foundation; either version 2 of the |
109 | - * License, or (at your option) any later version. |
110 | - * |
111 | - * This program is distributed in the hope that it will be useful, but |
112 | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
113 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
114 | - * General Public License for more details. |
115 | - * |
116 | - * You should have received a copy of the GNU General Public License |
117 | - * along with this program; if not, write to the Free Software |
118 | - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
119 | - * 02111-1307, USA. |
120 | - */ |
121 | - |
122 | -#ifndef _IP_H |
123 | -#define _IP_H |
124 | - |
125 | -#include "comment.h" |
126 | -#include "sf.h" |
127 | - |
128 | -enum { |
129 | - /* no error */ |
130 | - IP_ERROR_SUCCESS, |
131 | - /* system error (error code in errno) */ |
132 | - IP_ERROR_ERRNO, |
133 | - /* file type not supported */ |
134 | - IP_ERROR_UNRECOGNIZED_FILE_TYPE, |
135 | - /* function not supported (usually seek) */ |
136 | - IP_ERROR_FUNCTION_NOT_SUPPORTED, |
137 | - /* input plugin detected corrupted file */ |
138 | - IP_ERROR_FILE_FORMAT, |
139 | - /* malformed uri */ |
140 | - IP_ERROR_INVALID_URI, |
141 | - /* sample format not supported */ |
142 | - IP_ERROR_SAMPLE_FORMAT, |
143 | - /* error parsing response line / headers */ |
144 | - IP_ERROR_HTTP_RESPONSE, |
145 | - /* usually 404 */ |
146 | - IP_ERROR_HTTP_STATUS, |
147 | - /* */ |
148 | - IP_ERROR_INTERNAL |
149 | -}; |
150 | - |
151 | -/* |
152 | -struct input_plugin_data { |
153 | - // filled by ip-layer |
154 | - char *filename; |
155 | - int fd; |
156 | - |
157 | - unsigned int remote : 1; |
158 | - unsigned int metadata_changed : 1; |
159 | - |
160 | - // shoutcast |
161 | - int counter; |
162 | - int metaint; |
163 | - char *metadata; |
164 | - |
165 | - // filled by plugin |
166 | - sample_format_t sf; |
167 | - void * private_ipd; |
168 | -}; |
169 | -*/ |
170 | - |
171 | -struct input_plugin_data { |
172 | - // filled by ip-layer |
173 | -// QString filename; |
174 | - char *filename; |
175 | - int fd; |
176 | - |
177 | - unsigned int remote : 1; |
178 | - unsigned int metadata_changed : 1; |
179 | - |
180 | - // shoutcast |
181 | - int counter; |
182 | - int metaint; |
183 | - char *metadata; |
184 | - |
185 | - // filled by plugin |
186 | - sample_format_t sf; |
187 | - void * private_ipd; |
188 | -}; |
189 | - |
190 | - |
191 | -struct input_plugin_ops { |
192 | - int (*open)(struct input_plugin_data *ip_data); |
193 | - int (*close)(struct input_plugin_data *ip_data); |
194 | - int (*read)(struct input_plugin_data *ip_data, char *buffer, int count); |
195 | - int (*seek)(struct input_plugin_data *ip_data, double offset); |
196 | - int (*read_comments)(struct input_plugin_data *ip_data, |
197 | - struct keyval **comments); |
198 | - int (*duration)(struct input_plugin_data *ip_data); |
199 | -}; |
200 | - |
201 | -#endif |
202 | |
203 | === removed file 'mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp' |
204 | --- mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp 2012-05-04 03:44:44 +0000 |
205 | +++ mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp 1970-01-01 00:00:00 +0000 |
206 | @@ -1,587 +0,0 @@ |
207 | -// mp4-mixxx.cpp |
208 | -// This file is a hopefully shortlived fork + convertsion to C++ of the M4A audio playback plugin from the C* Music Player (cmus) project. |
209 | -// The original file mp4.c is also in this directory. |
210 | -// |
211 | -// This forked and converted by Garth and Albert in Summer 2008 to support M4A playback in Mixxx |
212 | -// |
213 | -// g++ $(pkg-config --cflags QtCore) $(pkg-config --libs-only-l QtCore) -lmp4v2 -lfaad -o mp4-mixxx mp4-mixxx.cpp |
214 | -// |
215 | -#include <QtCore> |
216 | -#include <stdlib.h> |
217 | - |
218 | -#include "mathstuff.h" |
219 | - |
220 | -/* |
221 | - * Copyright 2006 dnk <dnk@bjum.net> |
222 | - * |
223 | - * This program is free software; you can redistribute it and/or |
224 | - * modify it under the terms of the GNU General Public License as |
225 | - * published by the Free Software Foundation; either version 2 of the |
226 | - * License, or (at your option) any later version. |
227 | - * |
228 | - * This program is distributed in the hope that it will be useful, but |
229 | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
230 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
231 | - * General Public License for more details. |
232 | - * |
233 | - * You should have received a copy of the GNU General Public License |
234 | - * along with this program; if not, write to the Free Software |
235 | - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
236 | - * 02111-1307, USA. |
237 | - */ |
238 | - |
239 | -#include "ip.h" |
240 | -// #include "xmalloc.h" |
241 | -// #include "debug.h" |
242 | -// #include "file.h" |
243 | - |
244 | -#ifdef __MP4V2__ |
245 | - #include <mp4v2/mp4v2.h> |
246 | -#else |
247 | - #include <mp4.h> |
248 | -#endif |
249 | - |
250 | -#include <neaacdec.h> |
251 | - |
252 | -#include <errno.h> |
253 | -#include <string.h> |
254 | -#include <sys/types.h> |
255 | -#ifndef _MSC_VER |
256 | - #include <unistd.h> |
257 | -#endif |
258 | - |
259 | -#ifdef _MSC_VER |
260 | - #define S_ISDIR(mode) (mode & _S_IFDIR) |
261 | - #define strcasecmp stricmp |
262 | - #define strncasecmp strnicmp |
263 | -#endif |
264 | - |
265 | -#ifdef __M4AHACK__ |
266 | - typedef uint32_t SAMPLERATE_TYPE; |
267 | -#else |
268 | - typedef unsigned long SAMPLERATE_TYPE; |
269 | -#endif |
270 | - |
271 | -struct mp4_private { |
272 | - char *overflow_buf; |
273 | - int overflow_buf_len; |
274 | - |
275 | - unsigned char *aac_data; |
276 | - unsigned int aac_data_len; |
277 | - |
278 | - char *sample_buf; |
279 | - unsigned int sample_buf_frame; |
280 | - unsigned int sample_buf_len; |
281 | - |
282 | - unsigned char channels; |
283 | - unsigned long sample_rate; |
284 | - |
285 | - faacDecHandle decoder; /* typedef void * */ |
286 | - |
287 | - struct { |
288 | - MP4FileHandle handle; /* typedef void * */ |
289 | - |
290 | - MP4TrackId track; |
291 | - MP4SampleId sample; |
292 | - MP4SampleId num_samples; |
293 | - } mp4; |
294 | -}; |
295 | - |
296 | - |
297 | -static MP4TrackId mp4_get_track(MP4FileHandle *handle) |
298 | -{ |
299 | - MP4TrackId num_tracks; |
300 | - const char *track_type; |
301 | - uint8_t obj_type; |
302 | - MP4TrackId i; |
303 | - |
304 | - num_tracks = MP4GetNumberOfTracks(handle, NULL, 0); |
305 | - |
306 | - for (i = 1; i <= num_tracks; i++) { |
307 | - track_type = MP4GetTrackType(handle, i); |
308 | - if (!track_type) |
309 | - continue; |
310 | - |
311 | - if (!MP4_IS_AUDIO_TRACK_TYPE(track_type)) |
312 | - continue; |
313 | - |
314 | - /* MP4GetTrackAudioType */ |
315 | - obj_type = MP4GetTrackEsdsObjectTypeId(handle, i); |
316 | - if (obj_type == MP4_INVALID_AUDIO_TYPE) |
317 | - continue; |
318 | - |
319 | - if (obj_type == MP4_MPEG4_AUDIO_TYPE) { |
320 | - obj_type = MP4GetTrackAudioMpeg4Type(handle, i); |
321 | - |
322 | - if (MP4_IS_MPEG4_AAC_AUDIO_TYPE(obj_type)) |
323 | - return i; |
324 | - } else { |
325 | - if (MP4_IS_AAC_AUDIO_TYPE(obj_type)) |
326 | - return i; |
327 | - } |
328 | - } |
329 | - |
330 | - return MP4_INVALID_TRACK_ID; |
331 | -} |
332 | - |
333 | -static int mp4_open(struct input_plugin_data *ip_data) |
334 | -{ |
335 | - struct mp4_private *priv; |
336 | - faacDecConfigurationPtr neaac_cfg; |
337 | - unsigned char *buf; |
338 | - unsigned int buf_size; |
339 | - |
340 | - /* http://sourceforge.net/forum/message.php?msg_id=3578887 */ |
341 | - if (ip_data->remote) |
342 | - return -IP_ERROR_FUNCTION_NOT_SUPPORTED; |
343 | - |
344 | - /* init private_ipd struct */ |
345 | - // priv = xnew0(struct mp4_private, 1); |
346 | - priv = new mp4_private(); |
347 | - //priv = (mp4_private*) calloc(1, sizeof(mp4_private)); |
348 | - // FIXME: there was some alloc error checking in the orgininal ver |
349 | - memset(priv, 0, sizeof(*priv)); |
350 | - |
351 | - priv->overflow_buf_len = 0; |
352 | - priv->overflow_buf = NULL; |
353 | - |
354 | - priv->sample_buf_len = 4096; |
355 | - priv->sample_buf = new char[priv->sample_buf_len]; |
356 | - priv->sample_buf_frame = -1; |
357 | - |
358 | - ip_data->private_ipd = priv; |
359 | - |
360 | - priv->decoder = faacDecOpen(); |
361 | - /* set decoder config */ |
362 | - neaac_cfg = faacDecGetCurrentConfiguration(priv->decoder); |
363 | - neaac_cfg->outputFormat = FAAD_FMT_16BIT; /* force 16 bit audio */ |
364 | - neaac_cfg->downMatrix = 1; /* 5.1 -> stereo */ |
365 | - neaac_cfg->defObjectType = LC; |
366 | - //qDebug() << "Decoder Config" << neaac_cfg->defObjectType |
367 | - // << neaac_cfg->defSampleRate |
368 | - // << neaac_cfg->useOldADTSFormat |
369 | - // << neaac_cfg->dontUpSampleImplicitSBR; |
370 | - faacDecSetConfiguration(priv->decoder, neaac_cfg); |
371 | - |
372 | - /* open mpeg-4 file, check for >= ver 1.9.1 */ |
373 | -#if MP4V2_PROJECT_version_hex <= 0x00010901 |
374 | - priv->mp4.handle = MP4Read(ip_data->filename, 0); |
375 | -#else |
376 | - priv->mp4.handle = MP4Read(ip_data->filename); |
377 | -#endif |
378 | - if (!priv->mp4.handle) { |
379 | - qDebug() << "MP4Read failed"; |
380 | - goto out; |
381 | - } |
382 | - |
383 | - /* find aac audio track */ |
384 | - priv->mp4.track = mp4_get_track((MP4FileHandle*)priv->mp4.handle); |
385 | - if (priv->mp4.track == MP4_INVALID_TRACK_ID) { |
386 | - qDebug() << "MP4FindTrackId failed"; |
387 | - goto out; |
388 | - } |
389 | - |
390 | - // Allocate AAC read buffer |
391 | - priv->aac_data_len = MP4GetTrackMaxSampleSize(priv->mp4.handle, priv->mp4.track); |
392 | - priv->aac_data = new unsigned char[priv->aac_data_len]; |
393 | - |
394 | - priv->mp4.num_samples = MP4GetTrackNumberOfSamples(priv->mp4.handle, priv->mp4.track); |
395 | - // MP4 frames are 1-indexed |
396 | - priv->mp4.sample = 1; |
397 | - |
398 | - buf = NULL; |
399 | - buf_size = 0; |
400 | - if (!MP4GetTrackESConfiguration(priv->mp4.handle, priv->mp4.track, &buf, &buf_size)) { |
401 | - /* failed to get mpeg-4 audio config... this is ok. |
402 | - * faacDecInit2() will simply use default values instead. |
403 | - */ |
404 | - qDebug() << "Didn't get MP4 Audio Config (not a bad thing)"; |
405 | - buf = NULL; |
406 | - buf_size = 0; |
407 | - } |
408 | - |
409 | - /* init decoder according to mpeg-4 audio config */ |
410 | - if (faacDecInit2(priv->decoder, buf, buf_size, |
411 | - (SAMPLERATE_TYPE*)&priv->sample_rate, &priv->channels) < 0) { |
412 | - free(buf); |
413 | - goto out; |
414 | - } |
415 | - free(buf); |
416 | - |
417 | - // qDebug() << "sample rate "<< priv->sample_rate <<"hz, channels" << priv->channels; |
418 | - |
419 | - ip_data->sf = sf_rate(priv->sample_rate) | sf_channels(priv->channels) | sf_bits(16) | sf_signed(1); |
420 | -#if defined(WORDS_BIGENDIAN) |
421 | - ip_data->sf |= sf_bigendian(1); |
422 | -#endif |
423 | - |
424 | - return 0; |
425 | -out: |
426 | - if (priv->mp4.handle) |
427 | - MP4Close(priv->mp4.handle); |
428 | - if (priv->decoder) |
429 | - faacDecClose(priv->decoder); |
430 | - delete [] priv->sample_buf; |
431 | - delete [] priv->aac_data; |
432 | - delete priv; |
433 | - return -IP_ERROR_FILE_FORMAT; |
434 | -} |
435 | - |
436 | -static int mp4_close(struct input_plugin_data *ip_data) |
437 | -{ |
438 | - struct mp4_private *priv; |
439 | - |
440 | - priv = (mp4_private*) ip_data->private_ipd; |
441 | - |
442 | - if (priv->mp4.handle) |
443 | - MP4Close(priv->mp4.handle); |
444 | - |
445 | - if (priv->decoder) |
446 | - faacDecClose(priv->decoder); |
447 | - |
448 | - if (priv->sample_buf) { |
449 | - delete [] priv->sample_buf; |
450 | - } |
451 | - |
452 | - if (priv->aac_data) { |
453 | - delete [] priv->aac_data; |
454 | - } |
455 | - |
456 | - delete priv; |
457 | - ip_data->private_ipd = NULL; |
458 | - |
459 | - return 0; |
460 | -} |
461 | - |
462 | -/* returns -1 on fatal errors |
463 | - * returns -2 on non-fatal errors |
464 | - * 0 on eof |
465 | - * number of bytes put in 'buffer' on success */ |
466 | -static int decode_one_frame(struct input_plugin_data *ip_data, void *buffer, int count) |
467 | -{ |
468 | - struct mp4_private *priv = (mp4_private*) ip_data->private_ipd; |
469 | - faacDecFrameInfo frame_info; |
470 | - int bytes; |
471 | - |
472 | - //BUG_ON(priv->overflow_buf_len); |
473 | - |
474 | - if (priv->mp4.sample > priv->mp4.num_samples) |
475 | - return 0; /* EOF */ |
476 | - |
477 | - unsigned char *aac_data = priv->aac_data; |
478 | - unsigned int aac_data_len = priv->aac_data_len; |
479 | - |
480 | - // If you do this, then MP4ReadSample allocates the buffer for you. We don't |
481 | - // want this because it's slow. |
482 | - // unsigned char *aac_data = NULL; |
483 | - // unsigned int aac_data_len = 0; |
484 | - |
485 | - int this_frame = priv->mp4.sample; |
486 | - if (MP4ReadSample(priv->mp4.handle, priv->mp4.track, this_frame, |
487 | - &aac_data, &aac_data_len, |
488 | - NULL, NULL, NULL, NULL) == 0) { |
489 | - qWarning() << "m4a: error reading mp4 sample" << priv->mp4.sample; |
490 | - errno = EINVAL; |
491 | - return -1; |
492 | - } |
493 | - |
494 | - if (!aac_data) { |
495 | - qWarning() << "m4a: aac_data == NULL"; |
496 | - errno = EINVAL; |
497 | - return -1; |
498 | - } |
499 | - |
500 | - char* sample_buf = priv->sample_buf; |
501 | - int sample_buf_len = priv->sample_buf_len; |
502 | - |
503 | - NeAACDecDecode2(priv->decoder, |
504 | - &frame_info, |
505 | - aac_data, aac_data_len, |
506 | - (void**)&sample_buf, sample_buf_len); |
507 | - |
508 | - // qDebug() << "Sample frame" << priv->mp4.sample |
509 | - // << "has" << frame_info.samples << "samples" |
510 | - // << frame_info.bytesconsumed << "bytes" |
511 | - // << frame_info.channels << "channels" |
512 | - // << frame_info.error << "error" |
513 | - // << frame_info.samplerate << "samplerate"; |
514 | - |
515 | - if (!sample_buf || frame_info.bytesconsumed <= 0) { |
516 | - qWarning() << "m4a fatal error:" << faacDecGetErrorMessage(frame_info.error); |
517 | - errno = EINVAL; |
518 | - return -1; |
519 | - } |
520 | - |
521 | - if (frame_info.error != 0) { |
522 | - qDebug() << "frame error:" << faacDecGetErrorMessage(frame_info.error); |
523 | - return -2; |
524 | - } |
525 | - |
526 | - if (frame_info.samples <= 0) { |
527 | - return -2; |
528 | - } |
529 | - |
530 | - if (frame_info.channels != priv->channels || |
531 | - frame_info.samplerate != priv->sample_rate) { |
532 | - qDebug() << "invalid channel or sample_rate count\n"; |
533 | - return -2; |
534 | - } |
535 | - |
536 | - // The frame read was successful |
537 | - priv->sample_buf_frame = this_frame; |
538 | - priv->mp4.sample++; |
539 | - |
540 | - /* 16-bit samples */ |
541 | - bytes = frame_info.samples * 2; |
542 | - |
543 | - if (bytes > count) { |
544 | - /* decoded too much; keep overflow. */ |
545 | - //memcpy(priv->overflow_buf_base, sample_buf + count, bytes - count); |
546 | - //priv->overflow_buf = priv->overflow_buf_base; |
547 | - |
548 | - priv->overflow_buf = sample_buf + count; |
549 | - priv->overflow_buf_len = bytes - count; |
550 | - memcpy(buffer, sample_buf, count); |
551 | - return count; |
552 | - } |
553 | - |
554 | - memcpy(buffer, sample_buf, bytes); |
555 | - return bytes; |
556 | -} |
557 | - |
558 | -static int mp4_read(struct input_plugin_data *ip_data, char *buffer, int count) |
559 | -{ |
560 | - struct mp4_private *priv = (mp4_private*) ip_data->private_ipd; |
561 | - int rc; |
562 | - |
563 | - /* use overflow from previous call (if any) */ |
564 | - if (priv->overflow_buf_len > 0) { |
565 | - int len = priv->overflow_buf_len; |
566 | - |
567 | - if (len > count) |
568 | - len = count; |
569 | - |
570 | - memcpy(buffer, priv->overflow_buf, len); |
571 | - priv->overflow_buf += len; |
572 | - priv->overflow_buf_len -= len; |
573 | - |
574 | - //qDebug() << "Reading" << len << "from overflow." |
575 | - // << priv->overflow_buf_len << "overflow remains"; |
576 | - |
577 | - return len; |
578 | - } |
579 | - |
580 | - do { |
581 | - rc = decode_one_frame(ip_data, buffer, count); |
582 | - } while (rc == -2); |
583 | - |
584 | - return rc; |
585 | -} |
586 | - |
587 | -static int mp4_total_samples(struct input_plugin_data *ip_data) { |
588 | - struct mp4_private *priv = (struct mp4_private*)ip_data->private_ipd; |
589 | - return priv->channels * priv->mp4.num_samples * 1024; |
590 | -} |
591 | - |
592 | -static int mp4_current_sample(struct input_plugin_data *ip_data) { |
593 | - struct mp4_private *priv = (struct mp4_private*)ip_data->private_ipd; |
594 | - int frame_length = priv->channels * 1024; |
595 | - if (priv->overflow_buf_len == 0) { |
596 | - return priv->mp4.sample * frame_length - priv->overflow_buf_len; |
597 | - } |
598 | - // rryan 9/2009 This is equivalent to the current sample. The full expression |
599 | - // is (priv->mp4.sample - 1) * frame_length + (frame_length - |
600 | - // priv->overflow_buf_len); but the frame_length lone terms drop out. |
601 | - |
602 | - // -1 because if overflow buf is filled then mp4.sample is incremented, and |
603 | - // the samples in the overflow buf are for sample - 1 |
604 | - return (priv->mp4.sample - 1) * frame_length - priv->overflow_buf_len; |
605 | -} |
606 | - |
607 | -static int mp4_seek_sample(struct input_plugin_data *ip_data, int sample) |
608 | -{ |
609 | - struct mp4_private *priv; |
610 | - priv = (mp4_private*) ip_data->private_ipd; |
611 | - |
612 | - Q_ASSERT(sample >= 0); |
613 | - // The first frame is samples 0 through 2047. The first sample of the second |
614 | - // frame is 2048. 2048 / 2048 = 1, so frame_for_sample will be 2 on the |
615 | - // 2048'th sample. The frame_offset_samples is how many samples into the frame |
616 | - // the sample'th sample is. For x in (0,2047), the frame offset is x. For x in |
617 | - // (2048,4095) the offset is x-2048 and so on. sample % 2048 is therefore |
618 | - // suitable for calculating the offset. |
619 | - unsigned int frame_for_sample = 1 + (sample / (2 * 1024)); |
620 | - unsigned int frame_offset_samples = sample % (2 * 1024); |
621 | - unsigned int frame_offset_bytes = frame_offset_samples * 2; |
622 | - |
623 | - //qDebug() << "Seeking to" << frame_for_sample << ":" << frame_offset; |
624 | - |
625 | - // Invalid sample requested -- return the current position. |
626 | - if (frame_for_sample < 1 || frame_for_sample > priv->mp4.num_samples) |
627 | - return mp4_current_sample(ip_data); |
628 | - |
629 | - // We don't have the current frame decoded -- decode it. |
630 | - if (priv->sample_buf_frame != frame_for_sample) { |
631 | - |
632 | - // We might have to 'prime the pump' if this isn't the first frame. The |
633 | - // decoder has internal state that it builds as it plays, and just seeking |
634 | - // to the frame we want will result in poor audio quality (clicks and |
635 | - // pops). This is akin to seeking in a video and seeing MPEG |
636 | - // artifacts. Figure out how many frames we need to go backward -- 1 seems |
637 | - // to work. |
638 | - const int how_many_backwards = 1; |
639 | - int start_frame = math_max(frame_for_sample - how_many_backwards, 1); |
640 | - priv->mp4.sample = start_frame; |
641 | - |
642 | - // rryan 9/2009 -- the documentation is sketchy on this, but I think that |
643 | - // it tells the decoder that you are seeking so it should flush its state |
644 | - faacDecPostSeekReset(priv->decoder, priv->mp4.sample); |
645 | - |
646 | - // Loop until the current frame is past the frame we intended to read |
647 | - // (i.e. we have decoded how_many_backwards + 1 frames). The desidered |
648 | - // decoded frame will be stored in the overflow buffer, since we're asking |
649 | - // to read 0 bytes. |
650 | - int result; |
651 | - do { |
652 | - result = decode_one_frame(ip_data, 0, 0); |
653 | - if (result < 0) qDebug() << "SEEK_ERROR"; |
654 | - } while (result == -2 || priv->mp4.sample <= frame_for_sample); |
655 | - |
656 | - if (result == -1 || result == 0) { |
657 | - return mp4_current_sample(ip_data); |
658 | - } |
659 | - } else { |
660 | - qDebug() << "Seek within frame"; |
661 | - } |
662 | - |
663 | - // Now the overflow buffer contains the sample we want to seek to. Fix the |
664 | - // overflow buffer so that the next call to read() will read starting with the |
665 | - // requested sample. |
666 | - priv->overflow_buf = priv->sample_buf; |
667 | - priv->overflow_buf += frame_offset_bytes; |
668 | - priv->overflow_buf_len -= frame_offset_bytes; |
669 | - |
670 | - return mp4_current_sample(ip_data); |
671 | -} |
672 | - |
673 | -static int mp4_seek(struct input_plugin_data *ip_data, double offset) |
674 | -{ |
675 | - struct mp4_private *priv; |
676 | - MP4SampleId sample; |
677 | - uint32_t scale; |
678 | - |
679 | - priv = (mp4_private*) ip_data->private_ipd; |
680 | - |
681 | - scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track); |
682 | - if (scale == 0) |
683 | - return -IP_ERROR_INTERNAL; |
684 | - |
685 | - sample = MP4GetSampleIdFromTime(priv->mp4.handle, priv->mp4.track, |
686 | - (MP4Timestamp)(offset * (double)scale), 0); |
687 | - if (sample == MP4_INVALID_SAMPLE_ID) |
688 | - return -IP_ERROR_INTERNAL; |
689 | - |
690 | - qDebug() << "seeking from sample" << priv->mp4.sample << "to sample" << sample; |
691 | - priv->mp4.sample = sample; |
692 | - priv->overflow_buf_len = 0; |
693 | - |
694 | - return priv->mp4.sample; |
695 | -} |
696 | - |
697 | -/* commented because we use TagLib now ??? -- bkgood |
698 | -static int mp4_read_comments(struct input_plugin_data *ip_data, |
699 | - struct keyval **comments) |
700 | -{ |
701 | - struct mp4_private *priv; |
702 | - uint16_t meta_num, meta_total; |
703 | - uint8_t val; |
704 | - uint8_t *ustr; |
705 | - uint32_t size; |
706 | - char *str; |
707 | - GROWING_KEYVALS(c); |
708 | - |
709 | - priv = ip_data->private; |
710 | - |
711 | - MP4GetMetadata* provides malloced pointers, and the data |
712 | - * is in UTF-8 (or at least it should be). |
713 | - if (MP4GetMetadataArtist(priv->mp4.handle, &str)) |
714 | - comments_add(&c, "artist", str); |
715 | - if (MP4GetMetadataAlbum(priv->mp4.handle, &str)) |
716 | - comments_add(&c, "album", str); |
717 | - if (MP4GetMetadataName(priv->mp4.handle, &str)) |
718 | - comments_add(&c, "title", str); |
719 | - if (MP4GetMetadataGenre(priv->mp4.handle, &str)) |
720 | - comments_add(&c, "genre", str); |
721 | - if (MP4GetMetadataYear(priv->mp4.handle, &str)) |
722 | - comments_add(&c, "date", str); |
723 | - |
724 | - if (MP4GetMetadataCompilation(priv->mp4.handle, &val)) |
725 | - comments_add_const(&c, "compilation", val ? "yes" : "no"); |
726 | -#if 0 |
727 | - if (MP4GetBytesProperty(priv->mp4.handle, "moov.udta.meta.ilst.aART.data", &ustr, &size)) { |
728 | - char *xstr; |
729 | - |
730 | - What's this? |
731 | - * This is the result from lack of documentation. |
732 | - * It's supposed to return just a string, but it |
733 | - * returns an additional 16 bytes of junk at the |
734 | - * beginning. Could be a bug. Could be intentional. |
735 | - * Hopefully this works around it: |
736 | - |
737 | - if (ustr[0] == 0 && size > 16) { |
738 | - ustr += 16; |
739 | - size -= 16; |
740 | - } |
741 | - xstr = xmalloc(size + 1); |
742 | - memcpy(xstr, ustr, size); |
743 | - xstr[size] = 0; |
744 | - comments_add(&c, "albumartist", xstr); |
745 | - free(xstr); |
746 | - } |
747 | -#endif |
748 | - if (MP4GetMetadataTrack(priv->mp4.handle, &meta_num, &meta_total)) { |
749 | - char buf[6]; |
750 | - snprintf(buf, 6, "%u", meta_num); |
751 | - comments_add_const(&c, "tracknumber", buf); |
752 | - } |
753 | - if (MP4GetMetadataDisk(priv->mp4.handle, &meta_num, &meta_total)) { |
754 | - char buf[6]; |
755 | - snprintf(buf, 6, "%u", meta_num); |
756 | - comments_add_const(&c, "discnumber", buf); |
757 | - } |
758 | - |
759 | - comments_terminate(&c); |
760 | - *comments = c.comments; |
761 | - return 0; |
762 | -} |
763 | -*/ |
764 | - |
765 | -static int mp4_duration(struct input_plugin_data *ip_data) |
766 | -{ |
767 | - struct mp4_private *priv; |
768 | - uint32_t scale; |
769 | - uint64_t duration; |
770 | - |
771 | - priv = (mp4_private*) ip_data->private_ipd; |
772 | - |
773 | - scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track); |
774 | - if (scale == 0) |
775 | - return 0; |
776 | - |
777 | - duration = MP4GetTrackDuration(priv->mp4.handle, priv->mp4.track); |
778 | - |
779 | - return duration / scale; |
780 | -} |
781 | -/* |
782 | -const struct input_plugin_ops ip_ops = { |
783 | - .open = mp4_open, |
784 | - .close = mp4_close, |
785 | - .read = mp4_read, |
786 | - .seek = mp4_seek, |
787 | - .read_comments = mp4_read_comments, |
788 | - .duration = mp4_duration |
789 | -}; |
790 | - |
791 | -const char * const ip_extensions[] = { "mp4", "m4a", "m4b", NULL }; |
792 | -const char * const ip_mime_types[] = { "audio/mp4", "audio/mp4a-latm", NULL }; |
793 | -*/ |
794 | |
795 | === removed file 'mixxx/plugins/soundsourcem4a/m4a/mp4.c' |
796 | --- mixxx/plugins/soundsourcem4a/m4a/mp4.c 2010-01-19 21:49:19 +0000 |
797 | +++ mixxx/plugins/soundsourcem4a/m4a/mp4.c 1970-01-01 00:00:00 +0000 |
798 | @@ -1,407 +0,0 @@ |
799 | -/* |
800 | - * Copyright 2006 dnk <dnk@bjum.net> |
801 | - * |
802 | - * This program is free software; you can redistribute it and/or |
803 | - * modify it under the terms of the GNU General Public License as |
804 | - * published by the Free Software Foundation; either version 2 of the |
805 | - * License, or (at your option) any later version. |
806 | - * |
807 | - * This program is distributed in the hope that it will be useful, but |
808 | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
809 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
810 | - * General Public License for more details. |
811 | - * |
812 | - * You should have received a copy of the GNU General Public License |
813 | - * along with this program; if not, write to the Free Software |
814 | - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
815 | - * 02111-1307, USA. |
816 | - */ |
817 | - |
818 | -#include "ip.h" |
819 | -#include "xmalloc.h" |
820 | -#include "debug.h" |
821 | -#include "file.h" |
822 | - |
823 | -#ifdef __MP4V2__ |
824 | - #include <mp4v2/mp4v2.h> |
825 | -#else |
826 | - #include <mp4.h> |
827 | -#endif |
828 | -#include <faad.h> |
829 | - |
830 | -#include <errno.h> |
831 | -#include <string.h> |
832 | -#include <sys/types.h> |
833 | -#include <unistd.h> |
834 | - |
835 | -struct mp4_private { |
836 | - char *overflow_buf; |
837 | - int overflow_buf_len; |
838 | - |
839 | - unsigned char channels; |
840 | - unsigned long sample_rate; |
841 | - |
842 | - faacDecHandle decoder; /* typedef void * */ |
843 | - |
844 | - struct { |
845 | - MP4FileHandle handle; /* typedef void * */ |
846 | - |
847 | - MP4TrackId track; |
848 | - MP4SampleId sample; |
849 | - MP4SampleId num_samples; |
850 | - } mp4; |
851 | -}; |
852 | - |
853 | - |
854 | -static MP4TrackId mp4_get_track(MP4FileHandle *handle) |
855 | -{ |
856 | - MP4TrackId num_tracks; |
857 | - const char *track_type; |
858 | - uint8_t obj_type; |
859 | - MP4TrackId i; |
860 | - |
861 | - num_tracks = MP4GetNumberOfTracks(handle, NULL, 0); |
862 | - |
863 | - for (i = 1; i <= num_tracks; i++) { |
864 | - track_type = MP4GetTrackType(handle, i); |
865 | - if (!track_type) |
866 | - continue; |
867 | - |
868 | - if (!MP4_IS_AUDIO_TRACK_TYPE(track_type)) |
869 | - continue; |
870 | - |
871 | - /* MP4GetTrackAudioType */ |
872 | - obj_type = MP4GetTrackEsdsObjectTypeId(handle, i); |
873 | - if (obj_type == MP4_INVALID_AUDIO_TYPE) |
874 | - continue; |
875 | - |
876 | - if (obj_type == MP4_MPEG4_AUDIO_TYPE) { |
877 | - obj_type = MP4GetTrackAudioMpeg4Type(handle, i); |
878 | - |
879 | - if (MP4_IS_MPEG4_AAC_AUDIO_TYPE(obj_type)) |
880 | - return i; |
881 | - } else { |
882 | - if (MP4_IS_AAC_AUDIO_TYPE(obj_type)) |
883 | - return i; |
884 | - } |
885 | - } |
886 | - |
887 | - return MP4_INVALID_TRACK_ID; |
888 | -} |
889 | - |
890 | -static int mp4_open(struct input_plugin_data *ip_data) |
891 | -{ |
892 | - struct mp4_private *priv; |
893 | - faacDecConfigurationPtr neaac_cfg; |
894 | - unsigned char *buf; |
895 | - unsigned int buf_size; |
896 | - |
897 | - |
898 | - /* http://sourceforge.net/forum/message.php?msg_id=3578887 */ |
899 | - if (ip_data->remote) |
900 | - return -IP_ERROR_FUNCTION_NOT_SUPPORTED; |
901 | - |
902 | - /* init private struct */ |
903 | - priv = xnew0(struct mp4_private, 1); |
904 | - ip_data->private = priv; |
905 | - |
906 | - priv->decoder = faacDecOpen(); |
907 | - |
908 | - /* set decoder config */ |
909 | - neaac_cfg = faacDecGetCurrentConfiguration(priv->decoder); |
910 | - neaac_cfg->outputFormat = FAAD_FMT_16BIT; /* force 16 bit audio */ |
911 | - neaac_cfg->downMatrix = 1; /* 5.1 -> stereo */ |
912 | - faacDecSetConfiguration(priv->decoder, neaac_cfg); |
913 | - |
914 | - /* open mpeg-4 file */ |
915 | - priv->mp4.handle = MP4Read(ip_data->filename, 0); |
916 | - if (!priv->mp4.handle) { |
917 | - d_print("MP4Read failed\n"); |
918 | - goto out; |
919 | - } |
920 | - |
921 | - /* find aac audio track */ |
922 | - priv->mp4.track = mp4_get_track(priv->mp4.handle); |
923 | - if (priv->mp4.track == MP4_INVALID_TRACK_ID) { |
924 | - d_print("MP4FindTrackId failed\n"); |
925 | - goto out; |
926 | - } |
927 | - |
928 | - priv->mp4.num_samples = MP4GetTrackNumberOfSamples(priv->mp4.handle, priv->mp4.track); |
929 | - |
930 | - priv->mp4.sample = 1; |
931 | - |
932 | - buf = NULL; |
933 | - buf_size = 0; |
934 | - if (!MP4GetTrackESConfiguration(priv->mp4.handle, priv->mp4.track, &buf, &buf_size)) { |
935 | - /* failed to get mpeg-4 audio config... this is ok. |
936 | - * faacDecInit2() will simply use default values instead. |
937 | - */ |
938 | - buf = NULL; |
939 | - buf_size = 0; |
940 | - } |
941 | - |
942 | - /* init decoder according to mpeg-4 audio config */ |
943 | - if (faacDecInit2(priv->decoder, buf, buf_size, &priv->sample_rate, &priv->channels) < 0) { |
944 | - free(buf); |
945 | - goto out; |
946 | - } |
947 | - |
948 | - free(buf); |
949 | - |
950 | - d_print("sample rate %luhz, channels %u\n", priv->sample_rate, priv->channels); |
951 | - |
952 | - ip_data->sf = sf_rate(priv->sample_rate) | sf_channels(priv->channels) | sf_bits(16) | sf_signed(1); |
953 | -#if defined(WORDS_BIGENDIAN) |
954 | - ip_data->sf |= sf_bigendian(1); |
955 | -#endif |
956 | - |
957 | - return 0; |
958 | - |
959 | -out: |
960 | - if (priv->mp4.handle) |
961 | - MP4Close(priv->mp4.handle); |
962 | - if (priv->decoder) |
963 | - faacDecClose(priv->decoder); |
964 | - free(priv); |
965 | - return -IP_ERROR_FILE_FORMAT; |
966 | -} |
967 | - |
968 | -static int mp4_close(struct input_plugin_data *ip_data) |
969 | -{ |
970 | - struct mp4_private *priv; |
971 | - |
972 | - priv = ip_data->private; |
973 | - |
974 | - if (priv->mp4.handle) |
975 | - MP4Close(priv->mp4.handle); |
976 | - |
977 | - if (priv->decoder) |
978 | - faacDecClose(priv->decoder); |
979 | - |
980 | - free(priv); |
981 | - ip_data->private = NULL; |
982 | - |
983 | - return 0; |
984 | -} |
985 | - |
986 | -/* returns -1 on fatal errors |
987 | - * returns -2 on non-fatal errors |
988 | - * 0 on eof |
989 | - * number of bytes put in 'buffer' on success */ |
990 | -static int decode_one_frame(struct input_plugin_data *ip_data, void *buffer, int count) |
991 | -{ |
992 | - struct mp4_private *priv; |
993 | - unsigned char *aac_data = NULL; |
994 | - unsigned int aac_data_len = 0; |
995 | - faacDecFrameInfo frame_info; |
996 | - char *sample_buf; |
997 | - int bytes; |
998 | - |
999 | - priv = ip_data->private; |
1000 | - |
1001 | - BUG_ON(priv->overflow_buf_len); |
1002 | - |
1003 | - if (priv->mp4.sample > priv->mp4.num_samples) |
1004 | - return 0; /* EOF */ |
1005 | - |
1006 | - if (MP4ReadSample(priv->mp4.handle, priv->mp4.track, priv->mp4.sample, |
1007 | - &aac_data, &aac_data_len, NULL, NULL, NULL, NULL) == 0) { |
1008 | - d_print("error reading mp4 sample %d\n", priv->mp4.sample); |
1009 | - errno = EINVAL; |
1010 | - return -1; |
1011 | - } |
1012 | - |
1013 | - priv->mp4.sample++; |
1014 | - |
1015 | - if (!aac_data) { |
1016 | - d_print("aac_data == NULL\n"); |
1017 | - errno = EINVAL; |
1018 | - return -1; |
1019 | - } |
1020 | - |
1021 | - sample_buf = faacDecDecode(priv->decoder, &frame_info, aac_data, aac_data_len); |
1022 | - |
1023 | - free(aac_data); |
1024 | - |
1025 | - if (!sample_buf || frame_info.bytesconsumed <= 0) { |
1026 | - d_print("fatal error: %s\n", faacDecGetErrorMessage(frame_info.error)); |
1027 | - errno = EINVAL; |
1028 | - return -1; |
1029 | - } |
1030 | - |
1031 | - if (frame_info.error != 0) { |
1032 | - d_print("frame error: %s\n", faacDecGetErrorMessage(frame_info.error)); |
1033 | - return -2; |
1034 | - } |
1035 | - |
1036 | - if (frame_info.samples <= 0) |
1037 | - return -2; |
1038 | - |
1039 | - if (frame_info.channels != priv->channels || frame_info.samplerate != priv->sample_rate) { |
1040 | - d_print("invalid channel or sample_rate count\n"); |
1041 | - return -2; |
1042 | - } |
1043 | - |
1044 | - /* 16-bit samples */ |
1045 | - bytes = frame_info.samples * 2; |
1046 | - |
1047 | - if (bytes > count) { |
1048 | - /* decoded too much; keep overflow. */ |
1049 | - priv->overflow_buf = sample_buf + count; |
1050 | - priv->overflow_buf_len = bytes - count; |
1051 | - memcpy(buffer, sample_buf, count); |
1052 | - return count; |
1053 | - } else { |
1054 | - memcpy(buffer, sample_buf, bytes); |
1055 | - } |
1056 | - |
1057 | - return bytes; |
1058 | -} |
1059 | - |
1060 | -static int mp4_read(struct input_plugin_data *ip_data, char *buffer, int count) |
1061 | -{ |
1062 | - struct mp4_private *priv; |
1063 | - int rc; |
1064 | - |
1065 | - priv = ip_data->private; |
1066 | - |
1067 | - /* use overflow from previous call (if any) */ |
1068 | - if (priv->overflow_buf_len > 0) { |
1069 | - int len = priv->overflow_buf_len; |
1070 | - |
1071 | - if (len > count) |
1072 | - len = count; |
1073 | - |
1074 | - memcpy(buffer, priv->overflow_buf, len); |
1075 | - priv->overflow_buf += len; |
1076 | - priv->overflow_buf_len -= len; |
1077 | - |
1078 | - return len; |
1079 | - } |
1080 | - |
1081 | - do { |
1082 | - rc = decode_one_frame(ip_data, buffer, count); |
1083 | - } while (rc == -2); |
1084 | - |
1085 | - return rc; |
1086 | -} |
1087 | - |
1088 | -static int mp4_seek(struct input_plugin_data *ip_data, double offset) |
1089 | -{ |
1090 | - struct mp4_private *priv; |
1091 | - MP4SampleId sample; |
1092 | - uint32_t scale; |
1093 | - |
1094 | - priv = ip_data->private; |
1095 | - |
1096 | - scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track); |
1097 | - if (scale == 0) |
1098 | - return -IP_ERROR_INTERNAL; |
1099 | - |
1100 | - sample = MP4GetSampleIdFromTime(priv->mp4.handle, priv->mp4.track, |
1101 | - (MP4Timestamp)(offset * (double)scale), 0); |
1102 | - if (sample == MP4_INVALID_SAMPLE_ID) |
1103 | - return -IP_ERROR_INTERNAL; |
1104 | - |
1105 | - priv->mp4.sample = sample; |
1106 | - |
1107 | - d_print("seeking to sample %d\n", sample); |
1108 | - |
1109 | - return 0; |
1110 | -} |
1111 | - |
1112 | -static int mp4_read_comments(struct input_plugin_data *ip_data, |
1113 | - struct keyval **comments) |
1114 | -{ |
1115 | - struct mp4_private *priv; |
1116 | - uint16_t meta_num, meta_total; |
1117 | - uint8_t val; |
1118 | - /*uint8_t *ustr; |
1119 | - uint32_t size;*/ |
1120 | - char *str; |
1121 | - GROWING_KEYVALS(c); |
1122 | - |
1123 | - priv = ip_data->private; |
1124 | - |
1125 | - /* MP4GetMetadata* provides malloced pointers, and the data |
1126 | - * is in UTF-8 (or at least it should be). */ |
1127 | - if (MP4GetMetadataArtist(priv->mp4.handle, &str)) |
1128 | - comments_add(&c, "artist", str); |
1129 | - if (MP4GetMetadataAlbum(priv->mp4.handle, &str)) |
1130 | - comments_add(&c, "album", str); |
1131 | - if (MP4GetMetadataName(priv->mp4.handle, &str)) |
1132 | - comments_add(&c, "title", str); |
1133 | - if (MP4GetMetadataGenre(priv->mp4.handle, &str)) |
1134 | - comments_add(&c, "genre", str); |
1135 | - if (MP4GetMetadataYear(priv->mp4.handle, &str)) |
1136 | - comments_add(&c, "date", str); |
1137 | - |
1138 | - if (MP4GetMetadataCompilation(priv->mp4.handle, &val)) |
1139 | - comments_add_const(&c, "compilation", val ? "yes" : "no"); |
1140 | -#if 0 |
1141 | - if (MP4GetBytesProperty(priv->mp4.handle, "moov.udta.meta.ilst.aART.data", &ustr, &size)) { |
1142 | - char *xstr; |
1143 | - |
1144 | - /* What's this? |
1145 | - * This is the result from lack of documentation. |
1146 | - * It's supposed to return just a string, but it |
1147 | - * returns an additional 16 bytes of junk at the |
1148 | - * beginning. Could be a bug. Could be intentional. |
1149 | - * Hopefully this works around it: |
1150 | - */ |
1151 | - if (ustr[0] == 0 && size > 16) { |
1152 | - ustr += 16; |
1153 | - size -= 16; |
1154 | - } |
1155 | - xstr = xmalloc(size + 1); |
1156 | - memcpy(xstr, ustr, size); |
1157 | - xstr[size] = 0; |
1158 | - comments_add(&c, "albumartist", xstr); |
1159 | - free(xstr); |
1160 | - } |
1161 | -#endif |
1162 | - if (MP4GetMetadataTrack(priv->mp4.handle, &meta_num, &meta_total)) { |
1163 | - char buf[6]; |
1164 | - snprintf(buf, 6, "%u", meta_num); |
1165 | - comments_add_const(&c, "tracknumber", buf); |
1166 | - } |
1167 | - if (MP4GetMetadataDisk(priv->mp4.handle, &meta_num, &meta_total)) { |
1168 | - char buf[6]; |
1169 | - snprintf(buf, 6, "%u", meta_num); |
1170 | - comments_add_const(&c, "discnumber", buf); |
1171 | - } |
1172 | - |
1173 | - comments_terminate(&c); |
1174 | - *comments = c.comments; |
1175 | - return 0; |
1176 | -} |
1177 | - |
1178 | -static int mp4_duration(struct input_plugin_data *ip_data) |
1179 | -{ |
1180 | - struct mp4_private *priv; |
1181 | - uint32_t scale; |
1182 | - uint64_t duration; |
1183 | - |
1184 | - priv = ip_data->private; |
1185 | - |
1186 | - scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track); |
1187 | - if (scale == 0) |
1188 | - return 0; |
1189 | - |
1190 | - duration = MP4GetTrackDuration(priv->mp4.handle, priv->mp4.track); |
1191 | - |
1192 | - return duration / scale; |
1193 | -} |
1194 | - |
1195 | -const struct input_plugin_ops ip_ops = { |
1196 | - .open = mp4_open, |
1197 | - .close = mp4_close, |
1198 | - .read = mp4_read, |
1199 | - .seek = mp4_seek, |
1200 | - .read_comments = mp4_read_comments, |
1201 | - .duration = mp4_duration |
1202 | -}; |
1203 | - |
1204 | -const char * const ip_extensions[] = { "mp4", "m4a", "m4b", NULL }; |
1205 | -const char * const ip_mime_types[] = { /*"audio/mp4", "audio/mp4a-latm",*/ NULL }; |
1206 | |
1207 | === removed file 'mixxx/plugins/soundsourcem4a/m4a/sf.h' |
1208 | --- mixxx/plugins/soundsourcem4a/m4a/sf.h 2008-12-14 09:12:07 +0000 |
1209 | +++ mixxx/plugins/soundsourcem4a/m4a/sf.h 1970-01-01 00:00:00 +0000 |
1210 | @@ -1,61 +0,0 @@ |
1211 | -/* |
1212 | - * Copyright 2004 Timo Hirvonen |
1213 | - * |
1214 | - * This program is free software; you can redistribute it and/or |
1215 | - * modify it under the terms of the GNU General Public License as |
1216 | - * published by the Free Software Foundation; either version 2 of the |
1217 | - * License, or (at your option) any later version. |
1218 | - * |
1219 | - * This program is distributed in the hope that it will be useful, but |
1220 | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
1221 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1222 | - * General Public License for more details. |
1223 | - * |
1224 | - * You should have received a copy of the GNU General Public License |
1225 | - * along with this program; if not, write to the Free Software |
1226 | - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
1227 | - * 02111-1307, USA. |
1228 | - */ |
1229 | - |
1230 | -#ifndef _SF_H |
1231 | -#define _SF_H |
1232 | - |
1233 | -/* |
1234 | - * 0 1 big_endian 0-1 |
1235 | - * 1 1 is_signed 0-1 |
1236 | - * 2 1 unused 0 |
1237 | - * 3-5 3 bits >> 3 0-7 (* 8 = 0-56) |
1238 | - * 6-23 18 rate 0-262143 |
1239 | - * 24-31 8 channels 0-255 |
1240 | - */ |
1241 | -typedef unsigned int sample_format_t; |
1242 | - |
1243 | -#define SF_BIGENDIAN_MASK 0x00000001 |
1244 | -#define SF_SIGNED_MASK 0x00000002 |
1245 | -#define SF_BITS_MASK 0x00000038 |
1246 | -#define SF_RATE_MASK 0x00ffffc0 |
1247 | -#define SF_CHANNELS_MASK 0xff000000 |
1248 | - |
1249 | -#define SF_BIGENDIAN_SHIFT 0 |
1250 | -#define SF_SIGNED_SHIFT 1 |
1251 | -#define SF_BITS_SHIFT 0 |
1252 | -#define SF_RATE_SHIFT 6 |
1253 | -#define SF_CHANNELS_SHIFT 24 |
1254 | - |
1255 | -#define sf_get_bigendian(sf) (((sf) & SF_BIGENDIAN_MASK) >> SF_BIGENDIAN_SHIFT) |
1256 | -#define sf_get_signed(sf) (((sf) & SF_SIGNED_MASK ) >> SF_SIGNED_SHIFT) |
1257 | -#define sf_get_bits(sf) (((sf) & SF_BITS_MASK ) >> SF_BITS_SHIFT) |
1258 | -#define sf_get_rate(sf) (((sf) & SF_RATE_MASK ) >> SF_RATE_SHIFT) |
1259 | -#define sf_get_channels(sf) (((sf) & SF_CHANNELS_MASK ) >> SF_CHANNELS_SHIFT) |
1260 | - |
1261 | -#define sf_bigendian(val) (((val) << SF_BIGENDIAN_SHIFT) & SF_BIGENDIAN_MASK) |
1262 | -#define sf_signed(val) (((val) << SF_SIGNED_SHIFT ) & SF_SIGNED_MASK) |
1263 | -#define sf_bits(val) (((val) << SF_BITS_SHIFT ) & SF_BITS_MASK) |
1264 | -#define sf_rate(val) (((val) << SF_RATE_SHIFT ) & SF_RATE_MASK) |
1265 | -#define sf_channels(val) (((val) << SF_CHANNELS_SHIFT ) & SF_CHANNELS_MASK) |
1266 | - |
1267 | -#define sf_get_sample_size(sf) (sf_get_bits((sf)) >> 3) |
1268 | -#define sf_get_frame_size(sf) (sf_get_sample_size((sf)) * sf_get_channels((sf))) |
1269 | -#define sf_get_second_size(sf) (sf_get_rate((sf)) * sf_get_frame_size((sf))) |
1270 | - |
1271 | -#endif |
1272 | |
1273 | === modified file 'mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp' |
1274 | --- mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp 2011-03-31 16:07:22 +0000 |
1275 | +++ mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp 2012-08-25 14:26:19 +0000 |
1276 | @@ -1,9 +1,14 @@ |
1277 | -/*************************************************************************** |
1278 | - soundsourcem4a.cpp - mp4/m4a decoder |
1279 | - ------------------- |
1280 | - copyright : (C) 2008 by Garth Dahlstrom |
1281 | - email : ironstorm@users.sf.net |
1282 | - ***************************************************************************/ |
1283 | +/* soundsourcem4a.cpp - mp4/m4a decoder |
1284 | + * |
1285 | + * Author: Neale Pickett <neale@woozle.org> |
1286 | + * |
1287 | + * Based on: |
1288 | + * soundsourcem4a.cpp (C) 2008 Garth Dahlstrom <ironstorm@users.sf.net> |
1289 | + * faad 2.7 (C) Ahead Software |
1290 | + * |
1291 | + * |
1292 | + * Both GPLv2. |
1293 | + */ |
1294 | |
1295 | /*************************************************************************** |
1296 | * * |
1297 | @@ -14,14 +19,11 @@ |
1298 | * * |
1299 | ***************************************************************************/ |
1300 | |
1301 | +#include <stdio.h> |
1302 | + |
1303 | #include <taglib/mp4file.h> |
1304 | #include <neaacdec.h> |
1305 | - |
1306 | -#ifdef __MP4V2__ |
1307 | - #include <mp4v2/mp4v2.h> |
1308 | -#else |
1309 | - #include <mp4.h> |
1310 | -#endif |
1311 | +#include <mp4ff.h> |
1312 | |
1313 | #ifdef __WINDOWS__ |
1314 | #include <io.h> |
1315 | @@ -30,7 +32,90 @@ |
1316 | |
1317 | #include <QtDebug> |
1318 | #include "soundsourcem4a.h" |
1319 | -#include "m4a/mp4-mixxx.cpp" |
1320 | + |
1321 | + |
1322 | +/* Some things I use for debugging */ |
1323 | +#ifdef NODUMP |
1324 | +# define DUMPf(fmt, args...) |
1325 | +#else |
1326 | +# define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args) |
1327 | +#endif |
1328 | +#define DUMP() DUMPf("") |
1329 | +#define DUMP_d(v) DUMPf("%s = %d", #v, v) |
1330 | +#define DUMP_x(v) DUMPf("%s = 0x%x", #v, v) |
1331 | +#define DUMP_s(v) DUMPf("%s = %s", #v, v) |
1332 | +#define DUMP_c(v) DUMPf("%s = '%c' (0x%02x)", #v, v, v) |
1333 | +#define DUMP_p(v) DUMPf("%s = %p", #v, v) |
1334 | + |
1335 | +static uint32_t |
1336 | +read_callback(void *user_data, void *buffer, uint32_t length) |
1337 | +{ |
1338 | + return fread(buffer, 1, length, (FILE*)user_data); |
1339 | +} |
1340 | + |
1341 | +static uint32_t |
1342 | +seek_callback(void *user_data, uint64_t position) |
1343 | +{ |
1344 | + return fseek((FILE*)user_data, position, SEEK_SET); |
1345 | +} |
1346 | + |
1347 | +static int |
1348 | +GetAACTrack(mp4ff_t *infile) |
1349 | +{ |
1350 | + /* find AAC track */ |
1351 | + int i, rc; |
1352 | + int numTracks = mp4ff_total_tracks(infile); |
1353 | + |
1354 | + for (i = 0; i < numTracks; i++) |
1355 | + { |
1356 | + mp4AudioSpecificConfig mp4ASC; |
1357 | + unsigned char *buff = NULL; |
1358 | + unsigned int buff_size = 0; |
1359 | + |
1360 | + mp4ff_get_decoder_config(infile, i, &buff, &buff_size); |
1361 | + |
1362 | + if (buff) |
1363 | + { |
1364 | + rc = NeAACDecAudioSpecificConfig(buff, buff_size, &mp4ASC); |
1365 | + free(buff); |
1366 | + |
1367 | + if (rc < 0) |
1368 | + continue; |
1369 | + return i; |
1370 | + } |
1371 | + } |
1372 | + |
1373 | + /* can't decode this */ |
1374 | + return -1; |
1375 | +} |
1376 | + |
1377 | +static long |
1378 | +read_decode_frame(NeAACDecHandle hDecoder, |
1379 | + mp4ff_t *infile, |
1380 | + unsigned long track, |
1381 | + unsigned long frameno, |
1382 | + char *buf, |
1383 | + size_t buflen) |
1384 | +{ |
1385 | + NeAACDecFrameInfo frameInfo; |
1386 | + unsigned char frame[8192]; |
1387 | + size_t framelen; |
1388 | + |
1389 | + /* Read a frame (mp4ff calls it a "sample") of encoded data */ |
1390 | + framelen = mp4ff_read_sample_v2(infile, track, frameno, frame); |
1391 | + if (framelen == 0) { |
1392 | + qWarning() << "SoundSourceM4A::read failed!"; |
1393 | + return -1; |
1394 | + } |
1395 | + |
1396 | + /* Decode that into the buffer */ |
1397 | + NeAACDecDecode2(hDecoder, &frameInfo, frame, framelen, (void **)&buf, buflen); |
1398 | + if (frameInfo.bytesconsumed <= 0) { |
1399 | + qWarning() << "m4a fatal error:" << faacDecGetErrorMessage(frameInfo.error); |
1400 | + return -1; |
1401 | + } |
1402 | + return frameInfo.samples; |
1403 | +} |
1404 | |
1405 | namespace Mixxx { |
1406 | |
1407 | @@ -38,146 +123,247 @@ |
1408 | : SoundSource(qFileName) { |
1409 | |
1410 | // Initialize variables to invalid values in case loading fails. |
1411 | - mp4file = MP4_INVALID_FILE_HANDLE; |
1412 | - filelength = 0; |
1413 | - memset(&ipd, 0, sizeof(ipd)); |
1414 | + stream = NULL; |
1415 | + infile = NULL; |
1416 | + hDecoder = NULL; |
1417 | + frame = NULL; |
1418 | } |
1419 | |
1420 | SoundSourceM4A::~SoundSourceM4A() { |
1421 | - if (ipd.filename) { |
1422 | - delete [] ipd.filename; |
1423 | - ipd.filename = NULL; |
1424 | - } |
1425 | - |
1426 | - if (mp4file != MP4_INVALID_FILE_HANDLE) { |
1427 | - mp4_close(&ipd); |
1428 | - mp4file = MP4_INVALID_FILE_HANDLE; |
1429 | - } |
1430 | + close(); |
1431 | +} |
1432 | + |
1433 | +void |
1434 | +SoundSourceM4A::close() { |
1435 | + if (infile) { |
1436 | + mp4ff_close(infile); |
1437 | + infile = NULL; |
1438 | + } |
1439 | + if (stream) { |
1440 | + fclose(stream); |
1441 | + stream = NULL; |
1442 | + } |
1443 | + if (hDecoder) { |
1444 | + NeAACDecClose(hDecoder); |
1445 | + hDecoder = NULL; |
1446 | + } |
1447 | + |
1448 | + if (frame) { |
1449 | + free(frame); |
1450 | + frame = NULL; |
1451 | + } |
1452 | + |
1453 | +} |
1454 | + |
1455 | +int SoundSourceM4A::decoder_init() |
1456 | +{ |
1457 | + int passed = 0; |
1458 | + unsigned char *buffer = NULL; |
1459 | + |
1460 | + do { |
1461 | + unsigned int buffer_size = 0; |
1462 | + unsigned long samplerate; |
1463 | + unsigned char channels; |
1464 | + NeAACDecConfigurationPtr config; |
1465 | + |
1466 | + if (hDecoder) { |
1467 | + NeAACDecClose(hDecoder); |
1468 | + } |
1469 | + hDecoder = NeAACDecOpen(); |
1470 | + |
1471 | + config = NeAACDecGetCurrentConfiguration(hDecoder); |
1472 | + config->outputFormat = FAAD_FMT_16BIT; |
1473 | + config->downMatrix = 1; /* 5.1 -> stereo */ |
1474 | + config->defObjectType = LC; |
1475 | + NeAACDecSetConfiguration(hDecoder, config); |
1476 | + |
1477 | + mp4ff_get_decoder_config(infile, track, &buffer, &buffer_size); |
1478 | + |
1479 | + if (NeAACDecInit2(hDecoder, buffer, buffer_size, &samplerate, &channels) < 0) break; |
1480 | + |
1481 | + // Apparently this prevents dropping the first frame. |
1482 | + // http://ac3filter.net/repo/valib/file/37171946becf/valib/parsers/aac/aac_parser.cpp |
1483 | + |
1484 | + m_iSampleRate = samplerate; |
1485 | + m_iChannels = channels; |
1486 | + |
1487 | + passed = 1; |
1488 | + } while (0); |
1489 | + |
1490 | + if (buffer) { |
1491 | + free(buffer); |
1492 | + } |
1493 | + |
1494 | + if (passed) { |
1495 | + return 0; |
1496 | + } |
1497 | + |
1498 | + return -1; |
1499 | } |
1500 | |
1501 | int SoundSourceM4A::open() |
1502 | { |
1503 | - //Initialize the FAAD2 decoder... |
1504 | - initializeDecoder(); |
1505 | - |
1506 | - //qDebug() << "SSM4A: channels:" << m_iChannels |
1507 | - // << "filelength:" << filelength |
1508 | - // << "Sample Rate:" << m_iSampleRate; |
1509 | - return OK; |
1510 | -} |
1511 | - |
1512 | -int SoundSourceM4A::initializeDecoder() |
1513 | -{ |
1514 | - // Copy QString to char[] buffer for mp4_open to read from later |
1515 | - QByteArray qbaFileName; |
1516 | + int passed = 0; |
1517 | + char filename[PATH_MAX]; |
1518 | + |
1519 | + { |
1520 | + QByteArray qbaFileName; |
1521 | + size_t fnlen; |
1522 | + |
1523 | #ifdef Q_OS_WIN32 |
1524 | - // fopen() doesn't do utf8 on windows |
1525 | - qbaFileName = m_qFilename.toLocal8Bit(); |
1526 | + qbaFileName = m_qFilename.toLocal8Bit(); // fopen() doesn't do utf8 on windows |
1527 | #else |
1528 | - qbaFileName = m_qFilename.toUtf8(); |
1529 | + qbaFileName = m_qFilename.toUtf8(); |
1530 | #endif |
1531 | - int bytes = qbaFileName.length() + 1; |
1532 | - ipd.filename = new char[bytes]; |
1533 | - strncpy(ipd.filename, qbaFileName.constData(), bytes); |
1534 | - ipd.filename[bytes-1] = '\0'; |
1535 | - ipd.remote = false; // File is not an stream |
1536 | - // The file was loading and failing erratically because |
1537 | - // ipd.remote was an in an uninitialized state, it needed to be |
1538 | - // set to false. |
1539 | - |
1540 | - int mp4_open_status = mp4_open(&ipd); |
1541 | - if (mp4_open_status != 0) { |
1542 | - qWarning() << "SSM4A::initializeDecoder failed" |
1543 | - << m_qFilename << " with status:" << mp4_open_status; |
1544 | + fnlen = qbaFileName.length() + 1; |
1545 | + |
1546 | + if (fnlen >= sizeof filename) { |
1547 | + fnlen = sizeof filename; |
1548 | + } |
1549 | + |
1550 | + strncpy(filename, qbaFileName.constData(), fnlen); |
1551 | + filename[fnlen-1] = '\0'; |
1552 | + } |
1553 | + |
1554 | + do { |
1555 | + |
1556 | + stream = fopen(filename, "r"); |
1557 | + if (! stream) break; |
1558 | + |
1559 | + mp4cb.read = read_callback; |
1560 | + mp4cb.seek = seek_callback; |
1561 | + mp4cb.user_data = stream; |
1562 | + |
1563 | + infile = mp4ff_open_read(&mp4cb); |
1564 | + if (! infile) break; |
1565 | + |
1566 | + if ((track = GetAACTrack(infile)) < 0) break; |
1567 | + |
1568 | + if (-1 == decoder_init()) break; |
1569 | + |
1570 | + nframes = mp4ff_num_samples(infile, track); |
1571 | + |
1572 | + passed = 1; |
1573 | + } while (0); |
1574 | + |
1575 | + // It's okay to pass through mp4ff's approximation of track duration. |
1576 | + // This is the only place in this plugin where mixxx can handle an inexact value. |
1577 | + audiolen = mp4ff_get_track_duration(infile, track) * 2; |
1578 | + |
1579 | + // I have seen no frame larger than 700 bytes. Hopefully this is big enough. |
1580 | + frame = (unsigned char *)malloc(4096); |
1581 | + |
1582 | + mixxx_pos = 0; |
1583 | + |
1584 | + if (! passed) { |
1585 | + close(); |
1586 | + |
1587 | + qWarning() << "SoundSourceM4A::open failed"; |
1588 | return ERR; |
1589 | } |
1590 | |
1591 | - // mp4_open succeeded -> populate variables |
1592 | - mp4_private* mp = (struct mp4_private*)ipd.private_ipd; |
1593 | - Q_ASSERT(mp); |
1594 | - mp4file = mp->mp4.handle; |
1595 | - filelength = mp4_total_samples(&ipd); |
1596 | - m_iSampleRate = mp->sample_rate; |
1597 | - m_iChannels = mp->channels; |
1598 | - |
1599 | return OK; |
1600 | } |
1601 | |
1602 | -long SoundSourceM4A::seek(long filepos){ |
1603 | + |
1604 | +long SoundSourceM4A::seek(long filepos) { |
1605 | // Abort if file did not load. |
1606 | - if (filelength == 0) |
1607 | + if (! infile) { |
1608 | return 0; |
1609 | - |
1610 | - //qDebug() << "SSM4A::seek()" << filepos; |
1611 | - |
1612 | - // qDebug() << "MP4SEEK: seek time:" << filepos / (m_iChannels * m_iSampleRate) ; |
1613 | - |
1614 | - int position = mp4_seek_sample(&ipd, filepos); |
1615 | - //int position = mp4_seek(&ipd, filepos / (m_iChannels * m_iSampleRate)); |
1616 | - return position; |
1617 | + } |
1618 | + |
1619 | + if (mixxx_pos == filepos) { |
1620 | + // Don't reset decoder state |
1621 | + return mixxx_pos; |
1622 | + } |
1623 | + |
1624 | + if (filepos < 0) { |
1625 | + mixxx_pos = 0; |
1626 | + } else if (filepos > audiolen) { |
1627 | + mixxx_pos = audiolen; |
1628 | + } else { |
1629 | + mixxx_pos = filepos; |
1630 | + } |
1631 | + |
1632 | + decoder_init(); |
1633 | + { |
1634 | + // In my tests, faad needs about 2 frames to establish enough |
1635 | + // state that there are no abrupt pops. So we'll do 4. |
1636 | + long f = mp4ff_find_sample(infile, track, mixxx_pos/2, NULL); |
1637 | + long g = f - 4; |
1638 | + |
1639 | + if (f == 0) g = 0; |
1640 | + NeAACDecPostSeekReset(hDecoder, g); |
1641 | + |
1642 | + for (; g < f; g += 1) { |
1643 | + char buf[8192]; |
1644 | + |
1645 | + read_decode_frame(hDecoder, infile, track, g, buf, sizeof buf); |
1646 | + } |
1647 | + } |
1648 | + |
1649 | + return mixxx_pos; |
1650 | } |
1651 | |
1652 | -unsigned SoundSourceM4A::read(volatile unsigned long size, const SAMPLE* destination) { |
1653 | + |
1654 | +unsigned SoundSourceM4A::read(unsigned long samples_wanted, const SAMPLE *destination) { |
1655 | + SAMPLE *dbuf = (SAMPLE *)destination; |
1656 | + unsigned int samples_decoded = 0; |
1657 | + int32_t toskip; |
1658 | + long f; // Current frame number |
1659 | + |
1660 | // Abort if file did not load. |
1661 | - if (filelength == 0) |
1662 | - return 0; |
1663 | - |
1664 | - //qDebug() << "SSM4A::read()" << size; |
1665 | - |
1666 | - // We want to read a total of "size" samples, and the mp4_read() |
1667 | - // function wants to know how many bytes we want to decode. One |
1668 | - // sample is 16-bits = 2 bytes here, so we multiply size by channels to |
1669 | - // get the number of bytes we want to decode. |
1670 | - |
1671 | - int total_bytes_to_decode = size * m_iChannels; |
1672 | - int total_bytes_decoded = 0; |
1673 | - int num_bytes_req = 4096; |
1674 | - char* buffer = (char*)destination; |
1675 | - SAMPLE * as_buffer = (SAMPLE*) destination; //pointer for mono->stereo filling. |
1676 | - do { |
1677 | - if (total_bytes_decoded + num_bytes_req > total_bytes_to_decode) |
1678 | - num_bytes_req = total_bytes_to_decode - total_bytes_decoded; |
1679 | - |
1680 | - // (char *)&destination[total_bytes_decoded/2], |
1681 | - int numRead = mp4_read(&ipd, |
1682 | - buffer, |
1683 | - num_bytes_req); |
1684 | - if(numRead <= 0) { |
1685 | - //qDebug() << "SSM4A::read: EOF"; |
1686 | - break; |
1687 | - } |
1688 | - buffer += numRead; |
1689 | - total_bytes_decoded += numRead; |
1690 | - } while (total_bytes_decoded < total_bytes_to_decode); |
1691 | - |
1692 | - // At this point *destination should be filled. If mono : double all samples |
1693 | - // (L => R) |
1694 | - if (m_iChannels == 1) { |
1695 | - for (int i = total_bytes_decoded/2-1; i >= 0; --i) { |
1696 | - // as_buffer[i] is an audio sample (s16) |
1697 | - //scroll through , copying L->R & expanding buffer |
1698 | - as_buffer[i*2+1] = as_buffer[i]; |
1699 | - as_buffer[i*2] = as_buffer[i]; |
1700 | - } |
1701 | - } |
1702 | - |
1703 | - // Tell us about it only if we end up decoding a different value |
1704 | - // then what we expect. |
1705 | - |
1706 | - if (total_bytes_decoded % (size * 2)) { |
1707 | - qDebug() << "SSM4A::read : total_bytes_decoded:" |
1708 | - << total_bytes_decoded |
1709 | - << "size:" |
1710 | - << size; |
1711 | - } |
1712 | - |
1713 | - //There are two bytes in a 16-bit sample, so divide by 2. |
1714 | - return total_bytes_decoded / 2; |
1715 | + if (! infile) { |
1716 | + return 0; |
1717 | + } |
1718 | + |
1719 | + // Figure out which frame to start reading |
1720 | + f = mp4ff_find_sample(infile, track, mixxx_pos/2, &toskip); |
1721 | + if (-1 == f) { |
1722 | + // Can't find that sample, probably because mp4ff_get_track_duration |
1723 | + // overestimated earlier |
1724 | + return 0; |
1725 | + } |
1726 | + |
1727 | + // In theory, we could see a tiny speedup by caching the output of |
1728 | + // the last-read frame's decode, in case f here is the same as it |
1729 | + // was at the end of the last call to this function. |
1730 | + // |
1731 | + // In practice, this situation never seems to arise. The complexity |
1732 | + // caching introduces into the code isn't worth the diminutive |
1733 | + // theoretical performance gain. |
1734 | + |
1735 | + while ((f < nframes) && (samples_decoded < samples_wanted)) { |
1736 | + long samples; |
1737 | + |
1738 | + samples = read_decode_frame(hDecoder, infile, track, f, (char *)dbuf, (samples_wanted - samples_decoded)*2); |
1739 | + |
1740 | + /* If we were supposed to skip some samples, shuffle */ |
1741 | + if (toskip > 0) { |
1742 | + // XXX: I have no idea what the unit of toskip is. I've never seen it non-zero. |
1743 | + qDebug() << "Warning: toskip=" << toskip << ", this may sound glitchy!"; |
1744 | + memmove(dbuf, (char *)dbuf + toskip, (samples - toskip) * 2); |
1745 | + toskip = 0; |
1746 | + } |
1747 | + |
1748 | + /* Next! */ |
1749 | + f += 1; |
1750 | + |
1751 | + dbuf += samples; |
1752 | + samples_decoded += samples; |
1753 | + } |
1754 | + |
1755 | + // Advance where mixxx thinks we are in the file |
1756 | + mixxx_pos += samples_decoded; |
1757 | + |
1758 | + return samples_decoded; |
1759 | } |
1760 | |
1761 | inline long unsigned SoundSourceM4A::length(){ |
1762 | - return filelength; |
1763 | - //return m_iChannels * mp4_duration(&ipd) * m_iSampleRate; |
1764 | + if (! infile) { |
1765 | + return 0; |
1766 | + } |
1767 | + return audiolen; |
1768 | } |
1769 | |
1770 | int SoundSourceM4A::parseHeader(){ |
1771 | @@ -210,4 +396,5 @@ |
1772 | return list; |
1773 | } |
1774 | |
1775 | + |
1776 | } |
1777 | |
1778 | === modified file 'mixxx/plugins/soundsourcem4a/soundsourcem4a.h' |
1779 | --- mixxx/plugins/soundsourcem4a/soundsourcem4a.h 2011-10-09 19:05:15 +0000 |
1780 | +++ mixxx/plugins/soundsourcem4a/soundsourcem4a.h 2012-08-25 14:26:19 +0000 |
1781 | @@ -17,17 +17,13 @@ |
1782 | #ifndef SOUNDSOURCEM4A_H |
1783 | #define SOUNDSOURCEM4A_H |
1784 | |
1785 | -#ifdef __MP4V2__ |
1786 | - #include <mp4v2/mp4v2.h> |
1787 | -#else |
1788 | - #include <mp4.h> |
1789 | -#endif |
1790 | +#include <stdio.h> |
1791 | |
1792 | #include <neaacdec.h> |
1793 | +#include <mp4ff.h> |
1794 | #include <QString> |
1795 | #include "soundsource.h" |
1796 | #include "defs_version.h" |
1797 | -#include "m4a/ip.h" |
1798 | |
1799 | //As per QLibrary docs: http://doc.trolltech.com/4.6/qlibrary.html#resolve |
1800 | #ifdef Q_WS_WIN |
1801 | @@ -43,6 +39,7 @@ |
1802 | SoundSourceM4A(QString qFileName); |
1803 | ~SoundSourceM4A(); |
1804 | int open(); |
1805 | + void close(); |
1806 | long seek(long); |
1807 | int initializeDecoder(); |
1808 | unsigned read(unsigned long size, const SAMPLE*); |
1809 | @@ -50,10 +47,19 @@ |
1810 | int parseHeader(); |
1811 | static QList<QString> supportedFileExtensions(); |
1812 | private: |
1813 | - int trackId; |
1814 | - unsigned long filelength; |
1815 | - MP4FileHandle mp4file; |
1816 | - input_plugin_data ipd; |
1817 | + int decoder_init(); |
1818 | + |
1819 | + FILE *stream; // Input stream |
1820 | + mp4ff_t *infile; // Container context object |
1821 | + mp4ff_callback_t mp4cb; |
1822 | + |
1823 | + NeAACDecHandle hDecoder; // AAC context object |
1824 | + long nframes; // Number of frames in container |
1825 | + long audiolen; // Length of track (in samples) |
1826 | + long mixxx_pos; // Where mixxx thinks we are (in samples) |
1827 | + int track; |
1828 | + |
1829 | + unsigned char *frame; // A little buffer space for reading container frames |
1830 | }; |
1831 | |
1832 | extern "C" MY_EXPORT const char* getMixxxVersion() |
As the faad2 developers mention, mp4ff is not meant as a library for external programs like Mixxx. They do not ship the library by default anymore, resulting in quite some bug reports about broken dependencies. See e.g. http:// bugs.debian. org/cgi- bin/bugreport. cgi?bug= 550679# 57
Quote:
"again, libmp4ff was never meant to be used outside of faad2 and its
use is explicitely discouraged upstream and we deeply regret having
shipped it at all. Please get your application fixed to use mp4v2
instead (if it is MPL-compatible) or ffmpeg (prefered)."
I therefore propose to keep the old implementation using mp4v2. What is/was the reason for using mp4ff instead, anyway?