Merge lp:~neale/mixxx/m4a into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Neale Pickett
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
Reviewer Review Type Date Requested Status
Stefan Nürnberger (community) Needs Information
RJ Skerry-Ryan Pending
Review via email: mp+121310@code.launchpad.net

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.

To post a comment you must log in.
Revision history for this message
Stefan Nürnberger (kabelfrickler) wrote :

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?

review: Needs Information
Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'mixxx/build/features.py'
--- mixxx/build/features.py 2012-07-08 07:30:32 +0000
+++ mixxx/build/features.py 2012-08-25 14:26:19 +0000
@@ -465,21 +465,16 @@
465 if not self.enabled(build):465 if not self.enabled(build):
466 return466 return
467467
468 have_mp4v2_h = conf.CheckHeader('mp4v2/mp4v2.h')
469 have_mp4v2 = conf.CheckLib(['mp4v2','libmp4v2'], autoadd=False)
470 have_mp4 = conf.CheckLib('mp4', autoadd=False)
471
472 # Either mp4 or mp4v2 works
473 have_mp4 = (have_mp4v2_h and have_mp4v2) or have_mp4
474
475 if not have_mp4:
476 raise Exception('Could not find libmp4, libmp4v2 or the libmp4v2 development headers.')
477
478 have_faad = conf.CheckLib(['faad','libfaad'], autoadd=False)468 have_faad = conf.CheckLib(['faad','libfaad'], autoadd=False)
479469
480 if not have_faad:470 if not have_faad:
481 raise Exception('Could not find libfaad or the libfaad development headers.')471 raise Exception('Could not find libfaad or the libfaad development headers.')
482472
473 have_mp4ff = conf.CheckLib(['mp4ff', 'libmp4ff'], autoadd=False)
474
475 if not have_mp4ff:
476 raise Exception('Could not find libmp4ff or the libmp4ff development headers.')
477
483478
484class WavPack(Feature):479class WavPack(Feature):
485 def description(self):480 def description(self):
486481
=== modified file 'mixxx/plugins/soundsourcem4a/SConscript'
--- mixxx/plugins/soundsourcem4a/SConscript 2011-03-07 22:25:20 +0000
+++ mixxx/plugins/soundsourcem4a/SConscript 2012-08-25 14:26:19 +0000
@@ -23,9 +23,7 @@
2323
24 conf = Configure(env)24 conf = Configure(env)
2525
26 have_mp4v2_h = conf.CheckHeader('mp4v2/mp4v2.h')26 have_mp4ff = conf.CheckLib(['mp4ff', 'libmp4ff'])
27 have_mp4 = (have_mp4v2_h and conf.CheckLib(['mp4v2', 'libmp4v2'])) or \
28 conf.CheckLib('mp4')
2927
30 have_faad = conf.CheckLib(['faad', 'libfaad'])28 have_faad = conf.CheckLib(['faad', 'libfaad'])
31 have_faad_26 = False29 have_faad_26 = False
@@ -47,8 +45,6 @@
47 if have_faad_26:45 if have_faad_26:
48 env.Append(CPPDEFINES = '__M4AHACK__')46 env.Append(CPPDEFINES = '__M4AHACK__')
49 print "libfaad 2.6 compatibility mode... enabled"47 print "libfaad 2.6 compatibility mode... enabled"
50 if have_mp4v2_h:
51 env.Append(CPPDEFINES = '__MP4V2__')
5248
53 env = conf.Finish()49 env = conf.Finish()
54 SHLIBPREFIX='lib' #Makes the filename "libsoundsourcem4a" consistently across platforms to make our lives easier.50 SHLIBPREFIX='lib' #Makes the filename "libsoundsourcem4a" consistently across platforms to make our lives easier.
@@ -63,6 +59,3 @@
63 Return("ssm4a_bin")59 Return("ssm4a_bin")
64else:60else:
65 Return("")61 Return("")
66
67
68
6962
=== removed directory 'mixxx/plugins/soundsourcem4a/m4a'
=== removed file 'mixxx/plugins/soundsourcem4a/m4a/comment.h'
--- mixxx/plugins/soundsourcem4a/m4a/comment.h 2008-12-14 09:12:07 +0000
+++ mixxx/plugins/soundsourcem4a/m4a/comment.h 1970-01-01 00:00:00 +0000
@@ -1,30 +0,0 @@
1#ifndef _COMMENT_H
2#define _COMMENT_H
3
4struct keyval {
5 char *key;
6 char *val;
7};
8
9struct growing_keyvals {
10 struct keyval *comments;
11 int alloc;
12 int count;
13};
14
15#define GROWING_KEYVALS(name) struct growing_keyvals name = { NULL, 0, 0 }
16
17struct keyval *comments_dup(const struct keyval *comments);
18void comments_free(struct keyval *comments);
19
20/* case insensitive key */
21const char *comments_get_val(const struct keyval *comments, const char *key);
22const char *comments_get_albumartist(const struct keyval *comments);
23int comments_get_int(const struct keyval *comments, const char *key);
24int comments_get_date(const struct keyval *comments, const char *key);
25
26int comments_add(struct growing_keyvals *c, const char *key, char *val);
27int comments_add_const(struct growing_keyvals *c, const char *key, const char *val);
28void comments_terminate(struct growing_keyvals *c);
29
30#endif
310
=== removed file 'mixxx/plugins/soundsourcem4a/m4a/ip.h'
--- mixxx/plugins/soundsourcem4a/m4a/ip.h 2008-12-14 09:12:07 +0000
+++ mixxx/plugins/soundsourcem4a/m4a/ip.h 1970-01-01 00:00:00 +0000
@@ -1,99 +0,0 @@
1/*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
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.
8 *
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.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 */
19
20#ifndef _IP_H
21#define _IP_H
22
23#include "comment.h"
24#include "sf.h"
25
26enum {
27 /* no error */
28 IP_ERROR_SUCCESS,
29 /* system error (error code in errno) */
30 IP_ERROR_ERRNO,
31 /* file type not supported */
32 IP_ERROR_UNRECOGNIZED_FILE_TYPE,
33 /* function not supported (usually seek) */
34 IP_ERROR_FUNCTION_NOT_SUPPORTED,
35 /* input plugin detected corrupted file */
36 IP_ERROR_FILE_FORMAT,
37 /* malformed uri */
38 IP_ERROR_INVALID_URI,
39 /* sample format not supported */
40 IP_ERROR_SAMPLE_FORMAT,
41 /* error parsing response line / headers */
42 IP_ERROR_HTTP_RESPONSE,
43 /* usually 404 */
44 IP_ERROR_HTTP_STATUS,
45 /* */
46 IP_ERROR_INTERNAL
47};
48
49/*
50struct input_plugin_data {
51 // filled by ip-layer
52 char *filename;
53 int fd;
54
55 unsigned int remote : 1;
56 unsigned int metadata_changed : 1;
57
58 // shoutcast
59 int counter;
60 int metaint;
61 char *metadata;
62
63 // filled by plugin
64 sample_format_t sf;
65 void * private_ipd;
66};
67*/
68
69struct input_plugin_data {
70 // filled by ip-layer
71// QString filename;
72 char *filename;
73 int fd;
74
75 unsigned int remote : 1;
76 unsigned int metadata_changed : 1;
77
78 // shoutcast
79 int counter;
80 int metaint;
81 char *metadata;
82
83 // filled by plugin
84 sample_format_t sf;
85 void * private_ipd;
86};
87
88
89struct input_plugin_ops {
90 int (*open)(struct input_plugin_data *ip_data);
91 int (*close)(struct input_plugin_data *ip_data);
92 int (*read)(struct input_plugin_data *ip_data, char *buffer, int count);
93 int (*seek)(struct input_plugin_data *ip_data, double offset);
94 int (*read_comments)(struct input_plugin_data *ip_data,
95 struct keyval **comments);
96 int (*duration)(struct input_plugin_data *ip_data);
97};
98
99#endif
1000
=== removed file 'mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp'
--- mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp 2012-05-04 03:44:44 +0000
+++ mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp 1970-01-01 00:00:00 +0000
@@ -1,587 +0,0 @@
1// mp4-mixxx.cpp
2// This file is a hopefully shortlived fork + convertsion to C++ of the M4A audio playback plugin from the C* Music Player (cmus) project.
3// The original file mp4.c is also in this directory.
4//
5// This forked and converted by Garth and Albert in Summer 2008 to support M4A playback in Mixxx
6//
7// g++ $(pkg-config --cflags QtCore) $(pkg-config --libs-only-l QtCore) -lmp4v2 -lfaad -o mp4-mixxx mp4-mixxx.cpp
8//
9#include <QtCore>
10#include <stdlib.h>
11
12#include "mathstuff.h"
13
14/*
15 * Copyright 2006 dnk <dnk@bjum.net>
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of the
20 * License, or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
30 * 02111-1307, USA.
31 */
32
33#include "ip.h"
34// #include "xmalloc.h"
35// #include "debug.h"
36// #include "file.h"
37
38#ifdef __MP4V2__
39 #include <mp4v2/mp4v2.h>
40#else
41 #include <mp4.h>
42#endif
43
44#include <neaacdec.h>
45
46#include <errno.h>
47#include <string.h>
48#include <sys/types.h>
49#ifndef _MSC_VER
50 #include <unistd.h>
51#endif
52
53#ifdef _MSC_VER
54 #define S_ISDIR(mode) (mode & _S_IFDIR)
55 #define strcasecmp stricmp
56 #define strncasecmp strnicmp
57#endif
58
59#ifdef __M4AHACK__
60 typedef uint32_t SAMPLERATE_TYPE;
61#else
62 typedef unsigned long SAMPLERATE_TYPE;
63#endif
64
65struct mp4_private {
66 char *overflow_buf;
67 int overflow_buf_len;
68
69 unsigned char *aac_data;
70 unsigned int aac_data_len;
71
72 char *sample_buf;
73 unsigned int sample_buf_frame;
74 unsigned int sample_buf_len;
75
76 unsigned char channels;
77 unsigned long sample_rate;
78
79 faacDecHandle decoder; /* typedef void * */
80
81 struct {
82 MP4FileHandle handle; /* typedef void * */
83
84 MP4TrackId track;
85 MP4SampleId sample;
86 MP4SampleId num_samples;
87 } mp4;
88};
89
90
91static MP4TrackId mp4_get_track(MP4FileHandle *handle)
92{
93 MP4TrackId num_tracks;
94 const char *track_type;
95 uint8_t obj_type;
96 MP4TrackId i;
97
98 num_tracks = MP4GetNumberOfTracks(handle, NULL, 0);
99
100 for (i = 1; i <= num_tracks; i++) {
101 track_type = MP4GetTrackType(handle, i);
102 if (!track_type)
103 continue;
104
105 if (!MP4_IS_AUDIO_TRACK_TYPE(track_type))
106 continue;
107
108 /* MP4GetTrackAudioType */
109 obj_type = MP4GetTrackEsdsObjectTypeId(handle, i);
110 if (obj_type == MP4_INVALID_AUDIO_TYPE)
111 continue;
112
113 if (obj_type == MP4_MPEG4_AUDIO_TYPE) {
114 obj_type = MP4GetTrackAudioMpeg4Type(handle, i);
115
116 if (MP4_IS_MPEG4_AAC_AUDIO_TYPE(obj_type))
117 return i;
118 } else {
119 if (MP4_IS_AAC_AUDIO_TYPE(obj_type))
120 return i;
121 }
122 }
123
124 return MP4_INVALID_TRACK_ID;
125}
126
127static int mp4_open(struct input_plugin_data *ip_data)
128{
129 struct mp4_private *priv;
130 faacDecConfigurationPtr neaac_cfg;
131 unsigned char *buf;
132 unsigned int buf_size;
133
134 /* http://sourceforge.net/forum/message.php?msg_id=3578887 */
135 if (ip_data->remote)
136 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
137
138 /* init private_ipd struct */
139 // priv = xnew0(struct mp4_private, 1);
140 priv = new mp4_private();
141 //priv = (mp4_private*) calloc(1, sizeof(mp4_private));
142 // FIXME: there was some alloc error checking in the orgininal ver
143 memset(priv, 0, sizeof(*priv));
144
145 priv->overflow_buf_len = 0;
146 priv->overflow_buf = NULL;
147
148 priv->sample_buf_len = 4096;
149 priv->sample_buf = new char[priv->sample_buf_len];
150 priv->sample_buf_frame = -1;
151
152 ip_data->private_ipd = priv;
153
154 priv->decoder = faacDecOpen();
155 /* set decoder config */
156 neaac_cfg = faacDecGetCurrentConfiguration(priv->decoder);
157 neaac_cfg->outputFormat = FAAD_FMT_16BIT; /* force 16 bit audio */
158 neaac_cfg->downMatrix = 1; /* 5.1 -> stereo */
159 neaac_cfg->defObjectType = LC;
160 //qDebug() << "Decoder Config" << neaac_cfg->defObjectType
161 // << neaac_cfg->defSampleRate
162 // << neaac_cfg->useOldADTSFormat
163 // << neaac_cfg->dontUpSampleImplicitSBR;
164 faacDecSetConfiguration(priv->decoder, neaac_cfg);
165
166 /* open mpeg-4 file, check for >= ver 1.9.1 */
167#if MP4V2_PROJECT_version_hex <= 0x00010901
168 priv->mp4.handle = MP4Read(ip_data->filename, 0);
169#else
170 priv->mp4.handle = MP4Read(ip_data->filename);
171#endif
172 if (!priv->mp4.handle) {
173 qDebug() << "MP4Read failed";
174 goto out;
175 }
176
177 /* find aac audio track */
178 priv->mp4.track = mp4_get_track((MP4FileHandle*)priv->mp4.handle);
179 if (priv->mp4.track == MP4_INVALID_TRACK_ID) {
180 qDebug() << "MP4FindTrackId failed";
181 goto out;
182 }
183
184 // Allocate AAC read buffer
185 priv->aac_data_len = MP4GetTrackMaxSampleSize(priv->mp4.handle, priv->mp4.track);
186 priv->aac_data = new unsigned char[priv->aac_data_len];
187
188 priv->mp4.num_samples = MP4GetTrackNumberOfSamples(priv->mp4.handle, priv->mp4.track);
189 // MP4 frames are 1-indexed
190 priv->mp4.sample = 1;
191
192 buf = NULL;
193 buf_size = 0;
194 if (!MP4GetTrackESConfiguration(priv->mp4.handle, priv->mp4.track, &buf, &buf_size)) {
195 /* failed to get mpeg-4 audio config... this is ok.
196 * faacDecInit2() will simply use default values instead.
197 */
198 qDebug() << "Didn't get MP4 Audio Config (not a bad thing)";
199 buf = NULL;
200 buf_size = 0;
201 }
202
203 /* init decoder according to mpeg-4 audio config */
204 if (faacDecInit2(priv->decoder, buf, buf_size,
205 (SAMPLERATE_TYPE*)&priv->sample_rate, &priv->channels) < 0) {
206 free(buf);
207 goto out;
208 }
209 free(buf);
210
211 // qDebug() << "sample rate "<< priv->sample_rate <<"hz, channels" << priv->channels;
212
213 ip_data->sf = sf_rate(priv->sample_rate) | sf_channels(priv->channels) | sf_bits(16) | sf_signed(1);
214#if defined(WORDS_BIGENDIAN)
215 ip_data->sf |= sf_bigendian(1);
216#endif
217
218 return 0;
219out:
220 if (priv->mp4.handle)
221 MP4Close(priv->mp4.handle);
222 if (priv->decoder)
223 faacDecClose(priv->decoder);
224 delete [] priv->sample_buf;
225 delete [] priv->aac_data;
226 delete priv;
227 return -IP_ERROR_FILE_FORMAT;
228}
229
230static int mp4_close(struct input_plugin_data *ip_data)
231{
232 struct mp4_private *priv;
233
234 priv = (mp4_private*) ip_data->private_ipd;
235
236 if (priv->mp4.handle)
237 MP4Close(priv->mp4.handle);
238
239 if (priv->decoder)
240 faacDecClose(priv->decoder);
241
242 if (priv->sample_buf) {
243 delete [] priv->sample_buf;
244 }
245
246 if (priv->aac_data) {
247 delete [] priv->aac_data;
248 }
249
250 delete priv;
251 ip_data->private_ipd = NULL;
252
253 return 0;
254}
255
256/* returns -1 on fatal errors
257 * returns -2 on non-fatal errors
258 * 0 on eof
259 * number of bytes put in 'buffer' on success */
260static int decode_one_frame(struct input_plugin_data *ip_data, void *buffer, int count)
261{
262 struct mp4_private *priv = (mp4_private*) ip_data->private_ipd;
263 faacDecFrameInfo frame_info;
264 int bytes;
265
266 //BUG_ON(priv->overflow_buf_len);
267
268 if (priv->mp4.sample > priv->mp4.num_samples)
269 return 0; /* EOF */
270
271 unsigned char *aac_data = priv->aac_data;
272 unsigned int aac_data_len = priv->aac_data_len;
273
274 // If you do this, then MP4ReadSample allocates the buffer for you. We don't
275 // want this because it's slow.
276 // unsigned char *aac_data = NULL;
277 // unsigned int aac_data_len = 0;
278
279 int this_frame = priv->mp4.sample;
280 if (MP4ReadSample(priv->mp4.handle, priv->mp4.track, this_frame,
281 &aac_data, &aac_data_len,
282 NULL, NULL, NULL, NULL) == 0) {
283 qWarning() << "m4a: error reading mp4 sample" << priv->mp4.sample;
284 errno = EINVAL;
285 return -1;
286 }
287
288 if (!aac_data) {
289 qWarning() << "m4a: aac_data == NULL";
290 errno = EINVAL;
291 return -1;
292 }
293
294 char* sample_buf = priv->sample_buf;
295 int sample_buf_len = priv->sample_buf_len;
296
297 NeAACDecDecode2(priv->decoder,
298 &frame_info,
299 aac_data, aac_data_len,
300 (void**)&sample_buf, sample_buf_len);
301
302 // qDebug() << "Sample frame" << priv->mp4.sample
303 // << "has" << frame_info.samples << "samples"
304 // << frame_info.bytesconsumed << "bytes"
305 // << frame_info.channels << "channels"
306 // << frame_info.error << "error"
307 // << frame_info.samplerate << "samplerate";
308
309 if (!sample_buf || frame_info.bytesconsumed <= 0) {
310 qWarning() << "m4a fatal error:" << faacDecGetErrorMessage(frame_info.error);
311 errno = EINVAL;
312 return -1;
313 }
314
315 if (frame_info.error != 0) {
316 qDebug() << "frame error:" << faacDecGetErrorMessage(frame_info.error);
317 return -2;
318 }
319
320 if (frame_info.samples <= 0) {
321 return -2;
322 }
323
324 if (frame_info.channels != priv->channels ||
325 frame_info.samplerate != priv->sample_rate) {
326 qDebug() << "invalid channel or sample_rate count\n";
327 return -2;
328 }
329
330 // The frame read was successful
331 priv->sample_buf_frame = this_frame;
332 priv->mp4.sample++;
333
334 /* 16-bit samples */
335 bytes = frame_info.samples * 2;
336
337 if (bytes > count) {
338 /* decoded too much; keep overflow. */
339 //memcpy(priv->overflow_buf_base, sample_buf + count, bytes - count);
340 //priv->overflow_buf = priv->overflow_buf_base;
341
342 priv->overflow_buf = sample_buf + count;
343 priv->overflow_buf_len = bytes - count;
344 memcpy(buffer, sample_buf, count);
345 return count;
346 }
347
348 memcpy(buffer, sample_buf, bytes);
349 return bytes;
350}
351
352static int mp4_read(struct input_plugin_data *ip_data, char *buffer, int count)
353{
354 struct mp4_private *priv = (mp4_private*) ip_data->private_ipd;
355 int rc;
356
357 /* use overflow from previous call (if any) */
358 if (priv->overflow_buf_len > 0) {
359 int len = priv->overflow_buf_len;
360
361 if (len > count)
362 len = count;
363
364 memcpy(buffer, priv->overflow_buf, len);
365 priv->overflow_buf += len;
366 priv->overflow_buf_len -= len;
367
368 //qDebug() << "Reading" << len << "from overflow."
369 // << priv->overflow_buf_len << "overflow remains";
370
371 return len;
372 }
373
374 do {
375 rc = decode_one_frame(ip_data, buffer, count);
376 } while (rc == -2);
377
378 return rc;
379}
380
381static int mp4_total_samples(struct input_plugin_data *ip_data) {
382 struct mp4_private *priv = (struct mp4_private*)ip_data->private_ipd;
383 return priv->channels * priv->mp4.num_samples * 1024;
384}
385
386static int mp4_current_sample(struct input_plugin_data *ip_data) {
387 struct mp4_private *priv = (struct mp4_private*)ip_data->private_ipd;
388 int frame_length = priv->channels * 1024;
389 if (priv->overflow_buf_len == 0) {
390 return priv->mp4.sample * frame_length - priv->overflow_buf_len;
391 }
392 // rryan 9/2009 This is equivalent to the current sample. The full expression
393 // is (priv->mp4.sample - 1) * frame_length + (frame_length -
394 // priv->overflow_buf_len); but the frame_length lone terms drop out.
395
396 // -1 because if overflow buf is filled then mp4.sample is incremented, and
397 // the samples in the overflow buf are for sample - 1
398 return (priv->mp4.sample - 1) * frame_length - priv->overflow_buf_len;
399}
400
401static int mp4_seek_sample(struct input_plugin_data *ip_data, int sample)
402{
403 struct mp4_private *priv;
404 priv = (mp4_private*) ip_data->private_ipd;
405
406 Q_ASSERT(sample >= 0);
407 // The first frame is samples 0 through 2047. The first sample of the second
408 // frame is 2048. 2048 / 2048 = 1, so frame_for_sample will be 2 on the
409 // 2048'th sample. The frame_offset_samples is how many samples into the frame
410 // the sample'th sample is. For x in (0,2047), the frame offset is x. For x in
411 // (2048,4095) the offset is x-2048 and so on. sample % 2048 is therefore
412 // suitable for calculating the offset.
413 unsigned int frame_for_sample = 1 + (sample / (2 * 1024));
414 unsigned int frame_offset_samples = sample % (2 * 1024);
415 unsigned int frame_offset_bytes = frame_offset_samples * 2;
416
417 //qDebug() << "Seeking to" << frame_for_sample << ":" << frame_offset;
418
419 // Invalid sample requested -- return the current position.
420 if (frame_for_sample < 1 || frame_for_sample > priv->mp4.num_samples)
421 return mp4_current_sample(ip_data);
422
423 // We don't have the current frame decoded -- decode it.
424 if (priv->sample_buf_frame != frame_for_sample) {
425
426 // We might have to 'prime the pump' if this isn't the first frame. The
427 // decoder has internal state that it builds as it plays, and just seeking
428 // to the frame we want will result in poor audio quality (clicks and
429 // pops). This is akin to seeking in a video and seeing MPEG
430 // artifacts. Figure out how many frames we need to go backward -- 1 seems
431 // to work.
432 const int how_many_backwards = 1;
433 int start_frame = math_max(frame_for_sample - how_many_backwards, 1);
434 priv->mp4.sample = start_frame;
435
436 // rryan 9/2009 -- the documentation is sketchy on this, but I think that
437 // it tells the decoder that you are seeking so it should flush its state
438 faacDecPostSeekReset(priv->decoder, priv->mp4.sample);
439
440 // Loop until the current frame is past the frame we intended to read
441 // (i.e. we have decoded how_many_backwards + 1 frames). The desidered
442 // decoded frame will be stored in the overflow buffer, since we're asking
443 // to read 0 bytes.
444 int result;
445 do {
446 result = decode_one_frame(ip_data, 0, 0);
447 if (result < 0) qDebug() << "SEEK_ERROR";
448 } while (result == -2 || priv->mp4.sample <= frame_for_sample);
449
450 if (result == -1 || result == 0) {
451 return mp4_current_sample(ip_data);
452 }
453 } else {
454 qDebug() << "Seek within frame";
455 }
456
457 // Now the overflow buffer contains the sample we want to seek to. Fix the
458 // overflow buffer so that the next call to read() will read starting with the
459 // requested sample.
460 priv->overflow_buf = priv->sample_buf;
461 priv->overflow_buf += frame_offset_bytes;
462 priv->overflow_buf_len -= frame_offset_bytes;
463
464 return mp4_current_sample(ip_data);
465}
466
467static int mp4_seek(struct input_plugin_data *ip_data, double offset)
468{
469 struct mp4_private *priv;
470 MP4SampleId sample;
471 uint32_t scale;
472
473 priv = (mp4_private*) ip_data->private_ipd;
474
475 scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track);
476 if (scale == 0)
477 return -IP_ERROR_INTERNAL;
478
479 sample = MP4GetSampleIdFromTime(priv->mp4.handle, priv->mp4.track,
480 (MP4Timestamp)(offset * (double)scale), 0);
481 if (sample == MP4_INVALID_SAMPLE_ID)
482 return -IP_ERROR_INTERNAL;
483
484 qDebug() << "seeking from sample" << priv->mp4.sample << "to sample" << sample;
485 priv->mp4.sample = sample;
486 priv->overflow_buf_len = 0;
487
488 return priv->mp4.sample;
489}
490
491/* commented because we use TagLib now ??? -- bkgood
492static int mp4_read_comments(struct input_plugin_data *ip_data,
493 struct keyval **comments)
494{
495 struct mp4_private *priv;
496 uint16_t meta_num, meta_total;
497 uint8_t val;
498 uint8_t *ustr;
499 uint32_t size;
500 char *str;
501 GROWING_KEYVALS(c);
502
503 priv = ip_data->private;
504
505 MP4GetMetadata* provides malloced pointers, and the data
506 * is in UTF-8 (or at least it should be).
507 if (MP4GetMetadataArtist(priv->mp4.handle, &str))
508 comments_add(&c, "artist", str);
509 if (MP4GetMetadataAlbum(priv->mp4.handle, &str))
510 comments_add(&c, "album", str);
511 if (MP4GetMetadataName(priv->mp4.handle, &str))
512 comments_add(&c, "title", str);
513 if (MP4GetMetadataGenre(priv->mp4.handle, &str))
514 comments_add(&c, "genre", str);
515 if (MP4GetMetadataYear(priv->mp4.handle, &str))
516 comments_add(&c, "date", str);
517
518 if (MP4GetMetadataCompilation(priv->mp4.handle, &val))
519 comments_add_const(&c, "compilation", val ? "yes" : "no");
520#if 0
521 if (MP4GetBytesProperty(priv->mp4.handle, "moov.udta.meta.ilst.aART.data", &ustr, &size)) {
522 char *xstr;
523
524 What's this?
525 * This is the result from lack of documentation.
526 * It's supposed to return just a string, but it
527 * returns an additional 16 bytes of junk at the
528 * beginning. Could be a bug. Could be intentional.
529 * Hopefully this works around it:
530
531 if (ustr[0] == 0 && size > 16) {
532 ustr += 16;
533 size -= 16;
534 }
535 xstr = xmalloc(size + 1);
536 memcpy(xstr, ustr, size);
537 xstr[size] = 0;
538 comments_add(&c, "albumartist", xstr);
539 free(xstr);
540 }
541#endif
542 if (MP4GetMetadataTrack(priv->mp4.handle, &meta_num, &meta_total)) {
543 char buf[6];
544 snprintf(buf, 6, "%u", meta_num);
545 comments_add_const(&c, "tracknumber", buf);
546 }
547 if (MP4GetMetadataDisk(priv->mp4.handle, &meta_num, &meta_total)) {
548 char buf[6];
549 snprintf(buf, 6, "%u", meta_num);
550 comments_add_const(&c, "discnumber", buf);
551 }
552
553 comments_terminate(&c);
554 *comments = c.comments;
555 return 0;
556}
557*/
558
559static int mp4_duration(struct input_plugin_data *ip_data)
560{
561 struct mp4_private *priv;
562 uint32_t scale;
563 uint64_t duration;
564
565 priv = (mp4_private*) ip_data->private_ipd;
566
567 scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track);
568 if (scale == 0)
569 return 0;
570
571 duration = MP4GetTrackDuration(priv->mp4.handle, priv->mp4.track);
572
573 return duration / scale;
574}
575/*
576const struct input_plugin_ops ip_ops = {
577 .open = mp4_open,
578 .close = mp4_close,
579 .read = mp4_read,
580 .seek = mp4_seek,
581 .read_comments = mp4_read_comments,
582 .duration = mp4_duration
583};
584
585const char * const ip_extensions[] = { "mp4", "m4a", "m4b", NULL };
586const char * const ip_mime_types[] = { "audio/mp4", "audio/mp4a-latm", NULL };
587*/
5880
=== removed file 'mixxx/plugins/soundsourcem4a/m4a/mp4.c'
--- mixxx/plugins/soundsourcem4a/m4a/mp4.c 2010-01-19 21:49:19 +0000
+++ mixxx/plugins/soundsourcem4a/m4a/mp4.c 1970-01-01 00:00:00 +0000
@@ -1,407 +0,0 @@
1/*
2 * Copyright 2006 dnk <dnk@bjum.net>
3 *
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.
8 *
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.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 */
19
20#include "ip.h"
21#include "xmalloc.h"
22#include "debug.h"
23#include "file.h"
24
25#ifdef __MP4V2__
26 #include <mp4v2/mp4v2.h>
27#else
28 #include <mp4.h>
29#endif
30#include <faad.h>
31
32#include <errno.h>
33#include <string.h>
34#include <sys/types.h>
35#include <unistd.h>
36
37struct mp4_private {
38 char *overflow_buf;
39 int overflow_buf_len;
40
41 unsigned char channels;
42 unsigned long sample_rate;
43
44 faacDecHandle decoder; /* typedef void * */
45
46 struct {
47 MP4FileHandle handle; /* typedef void * */
48
49 MP4TrackId track;
50 MP4SampleId sample;
51 MP4SampleId num_samples;
52 } mp4;
53};
54
55
56static MP4TrackId mp4_get_track(MP4FileHandle *handle)
57{
58 MP4TrackId num_tracks;
59 const char *track_type;
60 uint8_t obj_type;
61 MP4TrackId i;
62
63 num_tracks = MP4GetNumberOfTracks(handle, NULL, 0);
64
65 for (i = 1; i <= num_tracks; i++) {
66 track_type = MP4GetTrackType(handle, i);
67 if (!track_type)
68 continue;
69
70 if (!MP4_IS_AUDIO_TRACK_TYPE(track_type))
71 continue;
72
73 /* MP4GetTrackAudioType */
74 obj_type = MP4GetTrackEsdsObjectTypeId(handle, i);
75 if (obj_type == MP4_INVALID_AUDIO_TYPE)
76 continue;
77
78 if (obj_type == MP4_MPEG4_AUDIO_TYPE) {
79 obj_type = MP4GetTrackAudioMpeg4Type(handle, i);
80
81 if (MP4_IS_MPEG4_AAC_AUDIO_TYPE(obj_type))
82 return i;
83 } else {
84 if (MP4_IS_AAC_AUDIO_TYPE(obj_type))
85 return i;
86 }
87 }
88
89 return MP4_INVALID_TRACK_ID;
90}
91
92static int mp4_open(struct input_plugin_data *ip_data)
93{
94 struct mp4_private *priv;
95 faacDecConfigurationPtr neaac_cfg;
96 unsigned char *buf;
97 unsigned int buf_size;
98
99
100 /* http://sourceforge.net/forum/message.php?msg_id=3578887 */
101 if (ip_data->remote)
102 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
103
104 /* init private struct */
105 priv = xnew0(struct mp4_private, 1);
106 ip_data->private = priv;
107
108 priv->decoder = faacDecOpen();
109
110 /* set decoder config */
111 neaac_cfg = faacDecGetCurrentConfiguration(priv->decoder);
112 neaac_cfg->outputFormat = FAAD_FMT_16BIT; /* force 16 bit audio */
113 neaac_cfg->downMatrix = 1; /* 5.1 -> stereo */
114 faacDecSetConfiguration(priv->decoder, neaac_cfg);
115
116 /* open mpeg-4 file */
117 priv->mp4.handle = MP4Read(ip_data->filename, 0);
118 if (!priv->mp4.handle) {
119 d_print("MP4Read failed\n");
120 goto out;
121 }
122
123 /* find aac audio track */
124 priv->mp4.track = mp4_get_track(priv->mp4.handle);
125 if (priv->mp4.track == MP4_INVALID_TRACK_ID) {
126 d_print("MP4FindTrackId failed\n");
127 goto out;
128 }
129
130 priv->mp4.num_samples = MP4GetTrackNumberOfSamples(priv->mp4.handle, priv->mp4.track);
131
132 priv->mp4.sample = 1;
133
134 buf = NULL;
135 buf_size = 0;
136 if (!MP4GetTrackESConfiguration(priv->mp4.handle, priv->mp4.track, &buf, &buf_size)) {
137 /* failed to get mpeg-4 audio config... this is ok.
138 * faacDecInit2() will simply use default values instead.
139 */
140 buf = NULL;
141 buf_size = 0;
142 }
143
144 /* init decoder according to mpeg-4 audio config */
145 if (faacDecInit2(priv->decoder, buf, buf_size, &priv->sample_rate, &priv->channels) < 0) {
146 free(buf);
147 goto out;
148 }
149
150 free(buf);
151
152 d_print("sample rate %luhz, channels %u\n", priv->sample_rate, priv->channels);
153
154 ip_data->sf = sf_rate(priv->sample_rate) | sf_channels(priv->channels) | sf_bits(16) | sf_signed(1);
155#if defined(WORDS_BIGENDIAN)
156 ip_data->sf |= sf_bigendian(1);
157#endif
158
159 return 0;
160
161out:
162 if (priv->mp4.handle)
163 MP4Close(priv->mp4.handle);
164 if (priv->decoder)
165 faacDecClose(priv->decoder);
166 free(priv);
167 return -IP_ERROR_FILE_FORMAT;
168}
169
170static int mp4_close(struct input_plugin_data *ip_data)
171{
172 struct mp4_private *priv;
173
174 priv = ip_data->private;
175
176 if (priv->mp4.handle)
177 MP4Close(priv->mp4.handle);
178
179 if (priv->decoder)
180 faacDecClose(priv->decoder);
181
182 free(priv);
183 ip_data->private = NULL;
184
185 return 0;
186}
187
188/* returns -1 on fatal errors
189 * returns -2 on non-fatal errors
190 * 0 on eof
191 * number of bytes put in 'buffer' on success */
192static int decode_one_frame(struct input_plugin_data *ip_data, void *buffer, int count)
193{
194 struct mp4_private *priv;
195 unsigned char *aac_data = NULL;
196 unsigned int aac_data_len = 0;
197 faacDecFrameInfo frame_info;
198 char *sample_buf;
199 int bytes;
200
201 priv = ip_data->private;
202
203 BUG_ON(priv->overflow_buf_len);
204
205 if (priv->mp4.sample > priv->mp4.num_samples)
206 return 0; /* EOF */
207
208 if (MP4ReadSample(priv->mp4.handle, priv->mp4.track, priv->mp4.sample,
209 &aac_data, &aac_data_len, NULL, NULL, NULL, NULL) == 0) {
210 d_print("error reading mp4 sample %d\n", priv->mp4.sample);
211 errno = EINVAL;
212 return -1;
213 }
214
215 priv->mp4.sample++;
216
217 if (!aac_data) {
218 d_print("aac_data == NULL\n");
219 errno = EINVAL;
220 return -1;
221 }
222
223 sample_buf = faacDecDecode(priv->decoder, &frame_info, aac_data, aac_data_len);
224
225 free(aac_data);
226
227 if (!sample_buf || frame_info.bytesconsumed <= 0) {
228 d_print("fatal error: %s\n", faacDecGetErrorMessage(frame_info.error));
229 errno = EINVAL;
230 return -1;
231 }
232
233 if (frame_info.error != 0) {
234 d_print("frame error: %s\n", faacDecGetErrorMessage(frame_info.error));
235 return -2;
236 }
237
238 if (frame_info.samples <= 0)
239 return -2;
240
241 if (frame_info.channels != priv->channels || frame_info.samplerate != priv->sample_rate) {
242 d_print("invalid channel or sample_rate count\n");
243 return -2;
244 }
245
246 /* 16-bit samples */
247 bytes = frame_info.samples * 2;
248
249 if (bytes > count) {
250 /* decoded too much; keep overflow. */
251 priv->overflow_buf = sample_buf + count;
252 priv->overflow_buf_len = bytes - count;
253 memcpy(buffer, sample_buf, count);
254 return count;
255 } else {
256 memcpy(buffer, sample_buf, bytes);
257 }
258
259 return bytes;
260}
261
262static int mp4_read(struct input_plugin_data *ip_data, char *buffer, int count)
263{
264 struct mp4_private *priv;
265 int rc;
266
267 priv = ip_data->private;
268
269 /* use overflow from previous call (if any) */
270 if (priv->overflow_buf_len > 0) {
271 int len = priv->overflow_buf_len;
272
273 if (len > count)
274 len = count;
275
276 memcpy(buffer, priv->overflow_buf, len);
277 priv->overflow_buf += len;
278 priv->overflow_buf_len -= len;
279
280 return len;
281 }
282
283 do {
284 rc = decode_one_frame(ip_data, buffer, count);
285 } while (rc == -2);
286
287 return rc;
288}
289
290static int mp4_seek(struct input_plugin_data *ip_data, double offset)
291{
292 struct mp4_private *priv;
293 MP4SampleId sample;
294 uint32_t scale;
295
296 priv = ip_data->private;
297
298 scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track);
299 if (scale == 0)
300 return -IP_ERROR_INTERNAL;
301
302 sample = MP4GetSampleIdFromTime(priv->mp4.handle, priv->mp4.track,
303 (MP4Timestamp)(offset * (double)scale), 0);
304 if (sample == MP4_INVALID_SAMPLE_ID)
305 return -IP_ERROR_INTERNAL;
306
307 priv->mp4.sample = sample;
308
309 d_print("seeking to sample %d\n", sample);
310
311 return 0;
312}
313
314static int mp4_read_comments(struct input_plugin_data *ip_data,
315 struct keyval **comments)
316{
317 struct mp4_private *priv;
318 uint16_t meta_num, meta_total;
319 uint8_t val;
320 /*uint8_t *ustr;
321 uint32_t size;*/
322 char *str;
323 GROWING_KEYVALS(c);
324
325 priv = ip_data->private;
326
327 /* MP4GetMetadata* provides malloced pointers, and the data
328 * is in UTF-8 (or at least it should be). */
329 if (MP4GetMetadataArtist(priv->mp4.handle, &str))
330 comments_add(&c, "artist", str);
331 if (MP4GetMetadataAlbum(priv->mp4.handle, &str))
332 comments_add(&c, "album", str);
333 if (MP4GetMetadataName(priv->mp4.handle, &str))
334 comments_add(&c, "title", str);
335 if (MP4GetMetadataGenre(priv->mp4.handle, &str))
336 comments_add(&c, "genre", str);
337 if (MP4GetMetadataYear(priv->mp4.handle, &str))
338 comments_add(&c, "date", str);
339
340 if (MP4GetMetadataCompilation(priv->mp4.handle, &val))
341 comments_add_const(&c, "compilation", val ? "yes" : "no");
342#if 0
343 if (MP4GetBytesProperty(priv->mp4.handle, "moov.udta.meta.ilst.aART.data", &ustr, &size)) {
344 char *xstr;
345
346 /* What's this?
347 * This is the result from lack of documentation.
348 * It's supposed to return just a string, but it
349 * returns an additional 16 bytes of junk at the
350 * beginning. Could be a bug. Could be intentional.
351 * Hopefully this works around it:
352 */
353 if (ustr[0] == 0 && size > 16) {
354 ustr += 16;
355 size -= 16;
356 }
357 xstr = xmalloc(size + 1);
358 memcpy(xstr, ustr, size);
359 xstr[size] = 0;
360 comments_add(&c, "albumartist", xstr);
361 free(xstr);
362 }
363#endif
364 if (MP4GetMetadataTrack(priv->mp4.handle, &meta_num, &meta_total)) {
365 char buf[6];
366 snprintf(buf, 6, "%u", meta_num);
367 comments_add_const(&c, "tracknumber", buf);
368 }
369 if (MP4GetMetadataDisk(priv->mp4.handle, &meta_num, &meta_total)) {
370 char buf[6];
371 snprintf(buf, 6, "%u", meta_num);
372 comments_add_const(&c, "discnumber", buf);
373 }
374
375 comments_terminate(&c);
376 *comments = c.comments;
377 return 0;
378}
379
380static int mp4_duration(struct input_plugin_data *ip_data)
381{
382 struct mp4_private *priv;
383 uint32_t scale;
384 uint64_t duration;
385
386 priv = ip_data->private;
387
388 scale = MP4GetTrackTimeScale(priv->mp4.handle, priv->mp4.track);
389 if (scale == 0)
390 return 0;
391
392 duration = MP4GetTrackDuration(priv->mp4.handle, priv->mp4.track);
393
394 return duration / scale;
395}
396
397const struct input_plugin_ops ip_ops = {
398 .open = mp4_open,
399 .close = mp4_close,
400 .read = mp4_read,
401 .seek = mp4_seek,
402 .read_comments = mp4_read_comments,
403 .duration = mp4_duration
404};
405
406const char * const ip_extensions[] = { "mp4", "m4a", "m4b", NULL };
407const char * const ip_mime_types[] = { /*"audio/mp4", "audio/mp4a-latm",*/ NULL };
4080
=== removed file 'mixxx/plugins/soundsourcem4a/m4a/sf.h'
--- mixxx/plugins/soundsourcem4a/m4a/sf.h 2008-12-14 09:12:07 +0000
+++ mixxx/plugins/soundsourcem4a/m4a/sf.h 1970-01-01 00:00:00 +0000
@@ -1,61 +0,0 @@
1/*
2 * Copyright 2004 Timo Hirvonen
3 *
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.
8 *
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.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 */
19
20#ifndef _SF_H
21#define _SF_H
22
23/*
24 * 0 1 big_endian 0-1
25 * 1 1 is_signed 0-1
26 * 2 1 unused 0
27 * 3-5 3 bits >> 3 0-7 (* 8 = 0-56)
28 * 6-23 18 rate 0-262143
29 * 24-31 8 channels 0-255
30 */
31typedef unsigned int sample_format_t;
32
33#define SF_BIGENDIAN_MASK 0x00000001
34#define SF_SIGNED_MASK 0x00000002
35#define SF_BITS_MASK 0x00000038
36#define SF_RATE_MASK 0x00ffffc0
37#define SF_CHANNELS_MASK 0xff000000
38
39#define SF_BIGENDIAN_SHIFT 0
40#define SF_SIGNED_SHIFT 1
41#define SF_BITS_SHIFT 0
42#define SF_RATE_SHIFT 6
43#define SF_CHANNELS_SHIFT 24
44
45#define sf_get_bigendian(sf) (((sf) & SF_BIGENDIAN_MASK) >> SF_BIGENDIAN_SHIFT)
46#define sf_get_signed(sf) (((sf) & SF_SIGNED_MASK ) >> SF_SIGNED_SHIFT)
47#define sf_get_bits(sf) (((sf) & SF_BITS_MASK ) >> SF_BITS_SHIFT)
48#define sf_get_rate(sf) (((sf) & SF_RATE_MASK ) >> SF_RATE_SHIFT)
49#define sf_get_channels(sf) (((sf) & SF_CHANNELS_MASK ) >> SF_CHANNELS_SHIFT)
50
51#define sf_bigendian(val) (((val) << SF_BIGENDIAN_SHIFT) & SF_BIGENDIAN_MASK)
52#define sf_signed(val) (((val) << SF_SIGNED_SHIFT ) & SF_SIGNED_MASK)
53#define sf_bits(val) (((val) << SF_BITS_SHIFT ) & SF_BITS_MASK)
54#define sf_rate(val) (((val) << SF_RATE_SHIFT ) & SF_RATE_MASK)
55#define sf_channels(val) (((val) << SF_CHANNELS_SHIFT ) & SF_CHANNELS_MASK)
56
57#define sf_get_sample_size(sf) (sf_get_bits((sf)) >> 3)
58#define sf_get_frame_size(sf) (sf_get_sample_size((sf)) * sf_get_channels((sf)))
59#define sf_get_second_size(sf) (sf_get_rate((sf)) * sf_get_frame_size((sf)))
60
61#endif
620
=== modified file 'mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp'
--- mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp 2011-03-31 16:07:22 +0000
+++ mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp 2012-08-25 14:26:19 +0000
@@ -1,9 +1,14 @@
1/***************************************************************************1/* soundsourcem4a.cpp - mp4/m4a decoder
2 soundsourcem4a.cpp - mp4/m4a decoder2 *
3 -------------------3 * Author: Neale Pickett <neale@woozle.org>
4 copyright : (C) 2008 by Garth Dahlstrom4 *
5 email : ironstorm@users.sf.net5 * Based on:
6 ***************************************************************************/6 * soundsourcem4a.cpp (C) 2008 Garth Dahlstrom <ironstorm@users.sf.net>
7 * faad 2.7 (C) Ahead Software
8 *
9 *
10 * Both GPLv2.
11 */
712
8/***************************************************************************13/***************************************************************************
9 * *14 * *
@@ -14,14 +19,11 @@
14 * *19 * *
15 ***************************************************************************/20 ***************************************************************************/
1621
22#include <stdio.h>
23
17#include <taglib/mp4file.h>24#include <taglib/mp4file.h>
18#include <neaacdec.h>25#include <neaacdec.h>
1926#include <mp4ff.h>
20#ifdef __MP4V2__
21 #include <mp4v2/mp4v2.h>
22#else
23 #include <mp4.h>
24#endif
2527
26#ifdef __WINDOWS__28#ifdef __WINDOWS__
27#include <io.h>29#include <io.h>
@@ -30,7 +32,90 @@
3032
31#include <QtDebug>33#include <QtDebug>
32#include "soundsourcem4a.h"34#include "soundsourcem4a.h"
33#include "m4a/mp4-mixxx.cpp"35
36
37/* Some things I use for debugging */
38#ifdef NODUMP
39# define DUMPf(fmt, args...)
40#else
41# define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
42#endif
43#define DUMP() DUMPf("")
44#define DUMP_d(v) DUMPf("%s = %d", #v, v)
45#define DUMP_x(v) DUMPf("%s = 0x%x", #v, v)
46#define DUMP_s(v) DUMPf("%s = %s", #v, v)
47#define DUMP_c(v) DUMPf("%s = '%c' (0x%02x)", #v, v, v)
48#define DUMP_p(v) DUMPf("%s = %p", #v, v)
49
50static uint32_t
51read_callback(void *user_data, void *buffer, uint32_t length)
52{
53 return fread(buffer, 1, length, (FILE*)user_data);
54}
55
56static uint32_t
57seek_callback(void *user_data, uint64_t position)
58{
59 return fseek((FILE*)user_data, position, SEEK_SET);
60}
61
62static int
63GetAACTrack(mp4ff_t *infile)
64{
65 /* find AAC track */
66 int i, rc;
67 int numTracks = mp4ff_total_tracks(infile);
68
69 for (i = 0; i < numTracks; i++)
70 {
71 mp4AudioSpecificConfig mp4ASC;
72 unsigned char *buff = NULL;
73 unsigned int buff_size = 0;
74
75 mp4ff_get_decoder_config(infile, i, &buff, &buff_size);
76
77 if (buff)
78 {
79 rc = NeAACDecAudioSpecificConfig(buff, buff_size, &mp4ASC);
80 free(buff);
81
82 if (rc < 0)
83 continue;
84 return i;
85 }
86 }
87
88 /* can't decode this */
89 return -1;
90}
91
92static long
93read_decode_frame(NeAACDecHandle hDecoder,
94 mp4ff_t *infile,
95 unsigned long track,
96 unsigned long frameno,
97 char *buf,
98 size_t buflen)
99{
100 NeAACDecFrameInfo frameInfo;
101 unsigned char frame[8192];
102 size_t framelen;
103
104 /* Read a frame (mp4ff calls it a "sample") of encoded data */
105 framelen = mp4ff_read_sample_v2(infile, track, frameno, frame);
106 if (framelen == 0) {
107 qWarning() << "SoundSourceM4A::read failed!";
108 return -1;
109 }
110
111 /* Decode that into the buffer */
112 NeAACDecDecode2(hDecoder, &frameInfo, frame, framelen, (void **)&buf, buflen);
113 if (frameInfo.bytesconsumed <= 0) {
114 qWarning() << "m4a fatal error:" << faacDecGetErrorMessage(frameInfo.error);
115 return -1;
116 }
117 return frameInfo.samples;
118}
34119
35namespace Mixxx {120namespace Mixxx {
36121
@@ -38,146 +123,247 @@
38 : SoundSource(qFileName) {123 : SoundSource(qFileName) {
39124
40 // Initialize variables to invalid values in case loading fails.125 // Initialize variables to invalid values in case loading fails.
41 mp4file = MP4_INVALID_FILE_HANDLE;126 stream = NULL;
42 filelength = 0;127 infile = NULL;
43 memset(&ipd, 0, sizeof(ipd));128 hDecoder = NULL;
129 frame = NULL;
44}130}
45131
46SoundSourceM4A::~SoundSourceM4A() {132SoundSourceM4A::~SoundSourceM4A() {
47 if (ipd.filename) {133 close();
48 delete [] ipd.filename;134}
49 ipd.filename = NULL;135
50 }136void
51137SoundSourceM4A::close() {
52 if (mp4file != MP4_INVALID_FILE_HANDLE) {138 if (infile) {
53 mp4_close(&ipd);139 mp4ff_close(infile);
54 mp4file = MP4_INVALID_FILE_HANDLE;140 infile = NULL;
55 }141 }
142 if (stream) {
143 fclose(stream);
144 stream = NULL;
145 }
146 if (hDecoder) {
147 NeAACDecClose(hDecoder);
148 hDecoder = NULL;
149 }
150
151 if (frame) {
152 free(frame);
153 frame = NULL;
154 }
155
156}
157
158int SoundSourceM4A::decoder_init()
159{
160 int passed = 0;
161 unsigned char *buffer = NULL;
162
163 do {
164 unsigned int buffer_size = 0;
165 unsigned long samplerate;
166 unsigned char channels;
167 NeAACDecConfigurationPtr config;
168
169 if (hDecoder) {
170 NeAACDecClose(hDecoder);
171 }
172 hDecoder = NeAACDecOpen();
173
174 config = NeAACDecGetCurrentConfiguration(hDecoder);
175 config->outputFormat = FAAD_FMT_16BIT;
176 config->downMatrix = 1; /* 5.1 -> stereo */
177 config->defObjectType = LC;
178 NeAACDecSetConfiguration(hDecoder, config);
179
180 mp4ff_get_decoder_config(infile, track, &buffer, &buffer_size);
181
182 if (NeAACDecInit2(hDecoder, buffer, buffer_size, &samplerate, &channels) < 0) break;
183
184 // Apparently this prevents dropping the first frame.
185 // http://ac3filter.net/repo/valib/file/37171946becf/valib/parsers/aac/aac_parser.cpp
186
187 m_iSampleRate = samplerate;
188 m_iChannels = channels;
189
190 passed = 1;
191 } while (0);
192
193 if (buffer) {
194 free(buffer);
195 }
196
197 if (passed) {
198 return 0;
199 }
200
201 return -1;
56}202}
57203
58int SoundSourceM4A::open()204int SoundSourceM4A::open()
59{205{
60 //Initialize the FAAD2 decoder...206 int passed = 0;
61 initializeDecoder();207 char filename[PATH_MAX];
62208
63 //qDebug() << "SSM4A: channels:" << m_iChannels209 {
64 // << "filelength:" << filelength210 QByteArray qbaFileName;
65 // << "Sample Rate:" << m_iSampleRate;211 size_t fnlen;
66 return OK;212
67}
68
69int SoundSourceM4A::initializeDecoder()
70{
71 // Copy QString to char[] buffer for mp4_open to read from later
72 QByteArray qbaFileName;
73#ifdef Q_OS_WIN32213#ifdef Q_OS_WIN32
74 // fopen() doesn't do utf8 on windows214 qbaFileName = m_qFilename.toLocal8Bit(); // fopen() doesn't do utf8 on windows
75 qbaFileName = m_qFilename.toLocal8Bit();
76#else215#else
77 qbaFileName = m_qFilename.toUtf8();216 qbaFileName = m_qFilename.toUtf8();
78#endif217#endif
79 int bytes = qbaFileName.length() + 1;218 fnlen = qbaFileName.length() + 1;
80 ipd.filename = new char[bytes];219
81 strncpy(ipd.filename, qbaFileName.constData(), bytes);220 if (fnlen >= sizeof filename) {
82 ipd.filename[bytes-1] = '\0';221 fnlen = sizeof filename;
83 ipd.remote = false; // File is not an stream222 }
84 // The file was loading and failing erratically because223
85 // ipd.remote was an in an uninitialized state, it needed to be224 strncpy(filename, qbaFileName.constData(), fnlen);
86 // set to false.225 filename[fnlen-1] = '\0';
87226 }
88 int mp4_open_status = mp4_open(&ipd);227
89 if (mp4_open_status != 0) {228 do {
90 qWarning() << "SSM4A::initializeDecoder failed"229
91 << m_qFilename << " with status:" << mp4_open_status;230 stream = fopen(filename, "r");
231 if (! stream) break;
232
233 mp4cb.read = read_callback;
234 mp4cb.seek = seek_callback;
235 mp4cb.user_data = stream;
236
237 infile = mp4ff_open_read(&mp4cb);
238 if (! infile) break;
239
240 if ((track = GetAACTrack(infile)) < 0) break;
241
242 if (-1 == decoder_init()) break;
243
244 nframes = mp4ff_num_samples(infile, track);
245
246 passed = 1;
247 } while (0);
248
249 // It's okay to pass through mp4ff's approximation of track duration.
250 // This is the only place in this plugin where mixxx can handle an inexact value.
251 audiolen = mp4ff_get_track_duration(infile, track) * 2;
252
253 // I have seen no frame larger than 700 bytes. Hopefully this is big enough.
254 frame = (unsigned char *)malloc(4096);
255
256 mixxx_pos = 0;
257
258 if (! passed) {
259 close();
260
261 qWarning() << "SoundSourceM4A::open failed";
92 return ERR;262 return ERR;
93 }263 }
94264
95 // mp4_open succeeded -> populate variables
96 mp4_private* mp = (struct mp4_private*)ipd.private_ipd;
97 Q_ASSERT(mp);
98 mp4file = mp->mp4.handle;
99 filelength = mp4_total_samples(&ipd);
100 m_iSampleRate = mp->sample_rate;
101 m_iChannels = mp->channels;
102
103 return OK;265 return OK;
104}266}
105267
106long SoundSourceM4A::seek(long filepos){268
269long SoundSourceM4A::seek(long filepos) {
107 // Abort if file did not load.270 // Abort if file did not load.
108 if (filelength == 0)271 if (! infile) {
109 return 0;272 return 0;
110273 }
111 //qDebug() << "SSM4A::seek()" << filepos;274
112275 if (mixxx_pos == filepos) {
113 // qDebug() << "MP4SEEK: seek time:" << filepos / (m_iChannels * m_iSampleRate) ;276 // Don't reset decoder state
114277 return mixxx_pos;
115 int position = mp4_seek_sample(&ipd, filepos);278 }
116 //int position = mp4_seek(&ipd, filepos / (m_iChannels * m_iSampleRate));279
117 return position;280 if (filepos < 0) {
281 mixxx_pos = 0;
282 } else if (filepos > audiolen) {
283 mixxx_pos = audiolen;
284 } else {
285 mixxx_pos = filepos;
286 }
287
288 decoder_init();
289 {
290 // In my tests, faad needs about 2 frames to establish enough
291 // state that there are no abrupt pops. So we'll do 4.
292 long f = mp4ff_find_sample(infile, track, mixxx_pos/2, NULL);
293 long g = f - 4;
294
295 if (f == 0) g = 0;
296 NeAACDecPostSeekReset(hDecoder, g);
297
298 for (; g < f; g += 1) {
299 char buf[8192];
300
301 read_decode_frame(hDecoder, infile, track, g, buf, sizeof buf);
302 }
303 }
304
305 return mixxx_pos;
118}306}
119307
120unsigned SoundSourceM4A::read(volatile unsigned long size, const SAMPLE* destination) {308
309unsigned SoundSourceM4A::read(unsigned long samples_wanted, const SAMPLE *destination) {
310 SAMPLE *dbuf = (SAMPLE *)destination;
311 unsigned int samples_decoded = 0;
312 int32_t toskip;
313 long f; // Current frame number
314
121 // Abort if file did not load.315 // Abort if file did not load.
122 if (filelength == 0)316 if (! infile) {
123 return 0;317 return 0;
124318 }
125 //qDebug() << "SSM4A::read()" << size;319
126320 // Figure out which frame to start reading
127 // We want to read a total of "size" samples, and the mp4_read()321 f = mp4ff_find_sample(infile, track, mixxx_pos/2, &toskip);
128 // function wants to know how many bytes we want to decode. One322 if (-1 == f) {
129 // sample is 16-bits = 2 bytes here, so we multiply size by channels to323 // Can't find that sample, probably because mp4ff_get_track_duration
130 // get the number of bytes we want to decode.324 // overestimated earlier
131325 return 0;
132 int total_bytes_to_decode = size * m_iChannels;326 }
133 int total_bytes_decoded = 0;327
134 int num_bytes_req = 4096;328 // In theory, we could see a tiny speedup by caching the output of
135 char* buffer = (char*)destination;329 // the last-read frame's decode, in case f here is the same as it
136 SAMPLE * as_buffer = (SAMPLE*) destination; //pointer for mono->stereo filling.330 // was at the end of the last call to this function.
137 do {331 //
138 if (total_bytes_decoded + num_bytes_req > total_bytes_to_decode)332 // In practice, this situation never seems to arise. The complexity
139 num_bytes_req = total_bytes_to_decode - total_bytes_decoded;333 // caching introduces into the code isn't worth the diminutive
140334 // theoretical performance gain.
141 // (char *)&destination[total_bytes_decoded/2],335
142 int numRead = mp4_read(&ipd,336 while ((f < nframes) && (samples_decoded < samples_wanted)) {
143 buffer,337 long samples;
144 num_bytes_req);338
145 if(numRead <= 0) {339 samples = read_decode_frame(hDecoder, infile, track, f, (char *)dbuf, (samples_wanted - samples_decoded)*2);
146 //qDebug() << "SSM4A::read: EOF";340
147 break;341 /* If we were supposed to skip some samples, shuffle */
148 }342 if (toskip > 0) {
149 buffer += numRead;343 // XXX: I have no idea what the unit of toskip is. I've never seen it non-zero.
150 total_bytes_decoded += numRead;344 qDebug() << "Warning: toskip=" << toskip << ", this may sound glitchy!";
151 } while (total_bytes_decoded < total_bytes_to_decode);345 memmove(dbuf, (char *)dbuf + toskip, (samples - toskip) * 2);
152346 toskip = 0;
153 // At this point *destination should be filled. If mono : double all samples347 }
154 // (L => R)348
155 if (m_iChannels == 1) {349 /* Next! */
156 for (int i = total_bytes_decoded/2-1; i >= 0; --i) {350 f += 1;
157 // as_buffer[i] is an audio sample (s16)351
158 //scroll through , copying L->R & expanding buffer352 dbuf += samples;
159 as_buffer[i*2+1] = as_buffer[i];353 samples_decoded += samples;
160 as_buffer[i*2] = as_buffer[i];354 }
161 }355
162 }356 // Advance where mixxx thinks we are in the file
163357 mixxx_pos += samples_decoded;
164 // Tell us about it only if we end up decoding a different value358
165 // then what we expect.359 return samples_decoded;
166
167 if (total_bytes_decoded % (size * 2)) {
168 qDebug() << "SSM4A::read : total_bytes_decoded:"
169 << total_bytes_decoded
170 << "size:"
171 << size;
172 }
173
174 //There are two bytes in a 16-bit sample, so divide by 2.
175 return total_bytes_decoded / 2;
176}360}
177361
178inline long unsigned SoundSourceM4A::length(){362inline long unsigned SoundSourceM4A::length(){
179 return filelength;363 if (! infile) {
180 //return m_iChannels * mp4_duration(&ipd) * m_iSampleRate;364 return 0;
365 }
366 return audiolen;
181}367}
182368
183int SoundSourceM4A::parseHeader(){369int SoundSourceM4A::parseHeader(){
@@ -210,4 +396,5 @@
210 return list;396 return list;
211}397}
212398
399
213}400}
214401
=== modified file 'mixxx/plugins/soundsourcem4a/soundsourcem4a.h'
--- mixxx/plugins/soundsourcem4a/soundsourcem4a.h 2011-10-09 19:05:15 +0000
+++ mixxx/plugins/soundsourcem4a/soundsourcem4a.h 2012-08-25 14:26:19 +0000
@@ -17,17 +17,13 @@
17#ifndef SOUNDSOURCEM4A_H17#ifndef SOUNDSOURCEM4A_H
18#define SOUNDSOURCEM4A_H18#define SOUNDSOURCEM4A_H
1919
20#ifdef __MP4V2__20#include <stdio.h>
21 #include <mp4v2/mp4v2.h>
22#else
23 #include <mp4.h>
24#endif
2521
26#include <neaacdec.h>22#include <neaacdec.h>
23#include <mp4ff.h>
27#include <QString>24#include <QString>
28#include "soundsource.h"25#include "soundsource.h"
29#include "defs_version.h"26#include "defs_version.h"
30#include "m4a/ip.h"
3127
32//As per QLibrary docs: http://doc.trolltech.com/4.6/qlibrary.html#resolve28//As per QLibrary docs: http://doc.trolltech.com/4.6/qlibrary.html#resolve
33#ifdef Q_WS_WIN29#ifdef Q_WS_WIN
@@ -43,6 +39,7 @@
43 SoundSourceM4A(QString qFileName);39 SoundSourceM4A(QString qFileName);
44 ~SoundSourceM4A();40 ~SoundSourceM4A();
45 int open();41 int open();
42 void close();
46 long seek(long);43 long seek(long);
47 int initializeDecoder();44 int initializeDecoder();
48 unsigned read(unsigned long size, const SAMPLE*);45 unsigned read(unsigned long size, const SAMPLE*);
@@ -50,10 +47,19 @@
50 int parseHeader();47 int parseHeader();
51 static QList<QString> supportedFileExtensions();48 static QList<QString> supportedFileExtensions();
52 private:49 private:
53 int trackId;50 int decoder_init();
54 unsigned long filelength;51
55 MP4FileHandle mp4file;52 FILE *stream; // Input stream
56 input_plugin_data ipd;53 mp4ff_t *infile; // Container context object
54 mp4ff_callback_t mp4cb;
55
56 NeAACDecHandle hDecoder; // AAC context object
57 long nframes; // Number of frames in container
58 long audiolen; // Length of track (in samples)
59 long mixxx_pos; // Where mixxx thinks we are (in samples)
60 int track;
61
62 unsigned char *frame; // A little buffer space for reading container frames
57};63};
5864
59extern "C" MY_EXPORT const char* getMixxxVersion()65extern "C" MY_EXPORT const char* getMixxxVersion()