~ubuntu-branches/ubuntu/saucy/xmms2/saucy-proposed

« back to all changes in this revision

Viewing changes to src/plugins/mpg123/mpg123.c

  • Committer: Bazaar Package Importer
  • Author(s): Florian Ragwitz
  • Date: 2009-05-02 08:31:32 UTC
  • mto: (1.1.6 upstream) (6.1.3 sid)
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: james.westby@ubuntu.com-20090502083132-y0ulwiqbk4lxfd4z
ImportĀ upstreamĀ versionĀ 0.6DrMattDestruction

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  XMMS2 plugin for decoding MPEG audio using libmpg123
 
2
 *  Copyright (C) 2007-2009 XMMS2 Team
 
3
 *
 
4
 *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
 
5
 *
 
6
 *  This library is free software; you can redistribute it and/or
 
7
 *  modify it under the terms of the GNU Lesser General Public
 
8
 *  License as published by the Free Software Foundation; either
 
9
 *  version 2.1 of the License, or (at your option) any later version.
 
10
 *
 
11
 *  This library is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 *  Lesser General Public License for more details.
 
15
 *
 
16
 *  For libmpg123 API have a look at http://mpg123.org/api/ .
 
17
 *
 
18
 *  This is also a very basic mpg123 decoder plugin.
 
19
 *  Configurable goodies of libmpg123 are missing:
 
20
 *   - equalizer (fast, using the MPEG frequency bands directly)
 
21
 *   - forced mono, resampling (xmms2 can handle that itself, I guess)
 
22
 *   - choose RVA preamp (album/mix, from lame tag ReplayGain info or
 
23
 *     ID3v2 tags)
 
24
 *  This should be easy to add for an XMMS2 hacker.
 
25
 *
 
26
 *  Note on metadata:
 
27
 *  With libmpg123 you can get at least all text and comment ID3v2
 
28
 *  (version 2.2, 2.3, 2.4) frames as well as the usual id3v1 info
 
29
 *  (when you get to the end of the file...).
 
30
 *  The decoder also likes to read ID3 tags for getting RVA-related
 
31
 *  info that players like foobar2000 store there... Now the problem is:
 
32
 *  Usually, the id3 xform reads and cuts the id3 data, killing the info
 
33
 *  for mpg123...
 
34
 *  Perhaps one can make the generic id3 plugin store the necessary info
 
35
 *  for retrieval here, or just keep the id3 tags there...
 
36
 */
 
37
 
 
38
#include "xmms/xmms_xformplugin.h"
 
39
#include "xmms/xmms_log.h"
 
40
#include "xmms/xmms_medialib.h"
 
41
 
 
42
#include <stdlib.h>
 
43
#include <glib.h>
 
44
#include <mpg123.h>
 
45
 
 
46
#include "id3v1.h"
 
47
 
 
48
#define BUFSIZE 4096
 
49
 
 
50
typedef struct xmms_mpg123_data_St {
 
51
        mpg123_handle *decoder;
 
52
        mpg123_pars *param;
 
53
 
 
54
        glong samplerate;
 
55
        gint channels;
 
56
        gboolean eof_found;
 
57
        gint filesize;
 
58
 
 
59
        /* input data buffer */
 
60
        guint8 buf[BUFSIZE];
 
61
} xmms_mpg123_data_t;
 
62
 
 
63
/*
 
64
 * Function prototypes
 
65
 */
 
66
 
 
67
static gboolean xmms_mpg123_plugin_setup (xmms_xform_plugin_t *xform_plugin);
 
68
static gint xmms_mpg123_read (xmms_xform_t *xform, xmms_sample_t *buf,
 
69
                              gint len, xmms_error_t *err);
 
70
static gboolean xmms_mpg123_init (xmms_xform_t *xform);
 
71
static void xmms_mpg123_destroy (xmms_xform_t *xform);
 
72
static gint64 xmms_mpg123_seek (xmms_xform_t *xform, gint64 samples,
 
73
                                xmms_xform_seek_mode_t whence,
 
74
                                xmms_error_t *err);
 
75
 
 
76
/*
 
77
 * Plugin header
 
78
 */
 
79
 
 
80
XMMS_XFORM_PLUGIN ("mpg123", "mpg123 decoder", XMMS_VERSION,
 
81
                   "mpg123 decoder for MPEG 1.0/2.0/2.5 layer 1/2/3 audio",
 
82
                   xmms_mpg123_plugin_setup);
 
83
 
 
84
static gboolean
 
85
xmms_mpg123_plugin_setup (xmms_xform_plugin_t *xform_plugin)
 
86
{
 
87
        xmms_xform_methods_t methods;
 
88
        int result;
 
89
 
 
90
        result = mpg123_init ();
 
91
        if (result != MPG123_OK) {
 
92
            return FALSE;
 
93
        }
 
94
 
 
95
        XMMS_XFORM_METHODS_INIT (methods);
 
96
        methods.init = xmms_mpg123_init;
 
97
        methods.destroy = xmms_mpg123_destroy;
 
98
        methods.read = xmms_mpg123_read;
 
99
        methods.seek = xmms_mpg123_seek;
 
100
        xmms_xform_plugin_methods_set (xform_plugin, &methods);
 
101
 
 
102
        xmms_xform_plugin_config_property_register (xform_plugin,
 
103
                                                    "id3v1_encoding",
 
104
                                                    "ISO8859-1",
 
105
                                                    NULL,
 
106
                                                    NULL);
 
107
 
 
108
        xmms_xform_plugin_indata_add (xform_plugin,
 
109
                                      XMMS_STREAM_TYPE_MIMETYPE,
 
110
                                      "audio/mpeg",
 
111
                                      XMMS_STREAM_TYPE_PRIORITY,
 
112
                                      40,
 
113
                                      NULL);
 
114
 
 
115
        /* Well, I usually only see mp3 and mp2 ... layer 1 files
 
116
         * are quite rare.
 
117
         */
 
118
        xmms_magic_extension_add ("audio/mpeg", "*.mp3");
 
119
        xmms_magic_extension_add ("audio/mpeg", "*.mp2");
 
120
        xmms_magic_extension_add ("audio/mpeg", "*.mp1");
 
121
 
 
122
        /* That's copied from the mad xform. */
 
123
        xmms_magic_add ("mpeg header", "audio/mpeg",
 
124
                        "0 beshort&0xfff6 0xfff6",
 
125
                        "0 beshort&0xfff6 0xfff4",
 
126
                        "0 beshort&0xffe6 0xffe2",
 
127
                        NULL);
 
128
 
 
129
        return TRUE;
 
130
}
 
131
 
 
132
static gboolean
 
133
xmms_mpg123_init (xmms_xform_t *xform)
 
134
{
 
135
        xmms_mpg123_data_t *data;
 
136
        const long *rates;
 
137
        size_t num_rates;
 
138
        int encoding;
 
139
        off_t length;
 
140
        int i, result;
 
141
 
 
142
        g_return_val_if_fail (xform, FALSE);
 
143
 
 
144
        data = g_new0 (xmms_mpg123_data_t, 1);
 
145
        xmms_xform_private_data_set (xform, data);
 
146
 
 
147
        /* Get the total size of this stream and store it for later */
 
148
        if (xmms_xform_metadata_get_int (xform,
 
149
                                         XMMS_MEDIALIB_ENTRY_PROPERTY_SIZE,
 
150
                                         &result)) {
 
151
                data->filesize = result;
 
152
        }
 
153
 
 
154
        mpg123_rates (&rates, &num_rates);
 
155
 
 
156
        data->param = mpg123_new_pars (&result);
 
157
        g_return_val_if_fail (data->param, FALSE);
 
158
 
 
159
        /* Create a quiet (stderr) decoder with auto choosen optimization.
 
160
         * Stuff set here should be tunable via plugin config properties!
 
161
         * You can also change some things during playback...
 
162
         */
 
163
        mpg123_par (data->param, MPG123_ADD_FLAGS, MPG123_QUIET, 0);
 
164
        mpg123_par (data->param, MPG123_ADD_FLAGS, MPG123_GAPLESS, 0);
 
165
        /* choose: MPG123_RVA_OFF, MPG123_RVA_MIX, MPG123_RVA_ALBUM
 
166
         * xmms2 has its own ReplayGain plugin to handle the RVA field */
 
167
        mpg123_par (data->param, MPG123_RVA, MPG123_RVA_OFF, 0);
 
168
 
 
169
        /* You could choose a decoder from the list provided by
 
170
         * mpg123_supported_decoders () and give that as second parameter.
 
171
         */
 
172
        data->decoder = mpg123_parnew (data->param, NULL, &result);
 
173
        if (data->decoder == NULL) {
 
174
                xmms_log_error ("%s", mpg123_plain_strerror (result));
 
175
                goto bad;
 
176
        }
 
177
 
 
178
        /* Prepare for buffer input feeding. */
 
179
        result = mpg123_open_feed (data->decoder);
 
180
        if (result != MPG123_OK) {
 
181
                goto mpg123_bad;
 
182
        }
 
183
 
 
184
        /* Let's always decode to signed 16bit for a start.
 
185
           Any mpg123-supported sample rate is accepted. */
 
186
        if (MPG123_OK != mpg123_format_none (data->decoder)) {
 
187
                goto mpg123_bad;
 
188
        }
 
189
 
 
190
        for (i = 0; i < num_rates; i++) {
 
191
                result = mpg123_format (data->decoder, rates[i],
 
192
                                        MPG123_MONO | MPG123_STEREO,
 
193
                                        MPG123_ENC_SIGNED_16);
 
194
                if (result != MPG123_OK) {
 
195
                        goto mpg123_bad;
 
196
                }
 
197
        }
 
198
 
 
199
        /* Fetch ID3v1 data from the end of file if possible */
 
200
        result = xmms_id3v1_get_tags (xform);
 
201
        if (result < 0) {
 
202
                xmms_log_error ("Seeking error when reading ID3v1 tags");
 
203
                goto bad;
 
204
        } else if (data->filesize > result) {
 
205
                /* Reduce the size of tag data from the filesize */
 
206
                data->filesize -= result;
 
207
        }
 
208
 
 
209
        /* Read data from input until decoded data is available from decoder */
 
210
        do {
 
211
                /* Parse stream and get info. */
 
212
                gint ret;
 
213
                xmms_error_t err;
 
214
 
 
215
                ret = xmms_xform_read (xform, data->buf, BUFSIZE, &err);
 
216
                if (ret < 0) {
 
217
                        xmms_log_error ("Error when trying to find beginning of stream");
 
218
                        goto bad;
 
219
                } else if (ret == 0) {
 
220
                        /* EOF reached before format was found, handled after loop */
 
221
                        break;
 
222
                }
 
223
 
 
224
                /* With zero output size nothing is actually outputted */
 
225
                result = mpg123_decode (data->decoder, data->buf,
 
226
                                        (size_t) ret, NULL, 0, NULL);
 
227
        } while (result == MPG123_NEED_MORE); /* Keep feeding... */
 
228
 
 
229
        if (result != MPG123_NEW_FORMAT) {
 
230
                xmms_log_error ("Unable to find beginning of stream (%s)!",
 
231
                                result == MPG123_ERR ? mpg123_strerror (data->decoder)
 
232
                                : "unexpected EOF");
 
233
                goto bad;
 
234
        }
 
235
 
 
236
        result = mpg123_getformat (data->decoder, &data->samplerate,
 
237
                                   &data->channels, &encoding);
 
238
        if (result != MPG123_OK) {
 
239
                goto mpg123_bad;
 
240
        }
 
241
 
 
242
        /* Set the filesize so it can be used for duration estimation */
 
243
        if (data->filesize > 0) {
 
244
                mpg123_set_filesize (data->decoder, data->filesize);
 
245
        }
 
246
 
 
247
        /* Get duration in samples, convert to ms and save to xmms2 */
 
248
        length = mpg123_length (data->decoder);
 
249
        if (length > 0) {
 
250
                length = (off_t) ((gfloat) length / data->samplerate * 1000);
 
251
                xmms_xform_metadata_set_int (xform,
 
252
                                             XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION,
 
253
                                             (gint) length);
 
254
        }
 
255
 
 
256
        XMMS_DBG ("mpg123: got stream with %li Hz %i channels, encoding %i",
 
257
                  data->samplerate, data->channels, encoding);
 
258
 
 
259
        xmms_xform_outdata_type_add (xform,
 
260
                                     XMMS_STREAM_TYPE_MIMETYPE,
 
261
                                     "audio/pcm",
 
262
                                     XMMS_STREAM_TYPE_FMT_FORMAT,
 
263
                                     XMMS_SAMPLE_FORMAT_S16,
 
264
                                     XMMS_STREAM_TYPE_FMT_CHANNELS,
 
265
                                     data->channels,
 
266
                                     XMMS_STREAM_TYPE_FMT_SAMPLERATE,
 
267
                                     (gint) data->samplerate,
 
268
                                     XMMS_STREAM_TYPE_END);
 
269
        return TRUE;
 
270
 
 
271
mpg123_bad:
 
272
        xmms_log_error ("mpg123 error: %s", mpg123_strerror (data->decoder));
 
273
 
 
274
bad:
 
275
        mpg123_delete (data->decoder);
 
276
        mpg123_delete_pars (data->param);
 
277
        g_free (data);
 
278
 
 
279
        return FALSE;
 
280
}
 
281
 
 
282
static void
 
283
xmms_mpg123_destroy (xmms_xform_t *xform)
 
284
{
 
285
        xmms_mpg123_data_t *data;
 
286
 
 
287
        data = xmms_xform_private_data_get (xform);
 
288
        g_return_if_fail (data);
 
289
 
 
290
        if (data != NULL) {
 
291
                mpg123_delete (data->decoder);
 
292
                mpg123_delete_pars (data->param);
 
293
        }
 
294
        g_free (data);
 
295
}
 
296
 
 
297
static gint
 
298
xmms_mpg123_read (xmms_xform_t *xform, xmms_sample_t *buf, gint len,
 
299
                  xmms_error_t *err)
 
300
{
 
301
        xmms_mpg123_data_t *data;
 
302
        int result = MPG123_OK;
 
303
        size_t read = 0;
 
304
 
 
305
        data = xmms_xform_private_data_get (xform);
 
306
        g_return_val_if_fail (data, -1);
 
307
 
 
308
        while (read == 0) {
 
309
                gint ret = 0;
 
310
 
 
311
                if (result == MPG123_NEED_MORE) {
 
312
                        ret = xmms_xform_read (xform, data->buf, BUFSIZE, err);
 
313
                        if (ret < 0) {
 
314
                                return ret;
 
315
                        } else if (ret == 0) {
 
316
                                data->eof_found = TRUE;
 
317
                        }
 
318
                }
 
319
 
 
320
                result = mpg123_decode (data->decoder, data->buf, (size_t) ret,
 
321
                                        buf, len, &read);
 
322
 
 
323
                if (result == MPG123_NEED_MORE && data->eof_found) {
 
324
                        /* We need more data, but there's none available
 
325
                         * so libmpg123 apparently missed an EOF */
 
326
                        result = MPG123_DONE;
 
327
                        break;
 
328
                } else if (result != MPG123_OK && result != MPG123_NEED_MORE) {
 
329
                        /* This is some uncommon result like EOF, handle outside
 
330
                         * the loop */
 
331
                        break;
 
332
                }
 
333
        }
 
334
 
 
335
        if (result == MPG123_DONE) {
 
336
                /* This is just normal EOF reported from libmpg123 */
 
337
                XMMS_DBG ("Got EOF while decoding stream");
 
338
                return 0;
 
339
        } else if (result == MPG123_NEW_FORMAT) {
 
340
                /* FIXME: When we can handle format changes, modify this */
 
341
                xmms_error_set (err,
 
342
                                XMMS_ERROR_GENERIC,
 
343
                                "The output format changed, XMMS2 can't handle that");
 
344
                return -1;
 
345
        } else if (result == MPG123_ERR) {
 
346
                xmms_error_set (err,
 
347
                                XMMS_ERROR_GENERIC,
 
348
                                mpg123_strerror (data->decoder));
 
349
                return -1;
 
350
        }
 
351
 
 
352
        return (gint) read;
 
353
}
 
354
 
 
355
static gint64
 
356
xmms_mpg123_seek (xmms_xform_t *xform, gint64 samples,
 
357
                  xmms_xform_seek_mode_t whence,
 
358
                  xmms_error_t *err)
 
359
{
 
360
        xmms_mpg123_data_t *data;
 
361
        gint64 ret;
 
362
        off_t byteoff, samploff;
 
363
        int mwhence = -1;
 
364
 
 
365
        data = xmms_xform_private_data_get (xform);
 
366
        g_return_val_if_fail (data, -1);
 
367
 
 
368
        if (whence == XMMS_XFORM_SEEK_SET) {
 
369
                mwhence = SEEK_SET;
 
370
        } else if (whence == XMMS_XFORM_SEEK_CUR) {
 
371
                mwhence = SEEK_CUR;
 
372
        } else if (whence == XMMS_XFORM_SEEK_END) {
 
373
                mwhence = SEEK_END;
 
374
        }
 
375
 
 
376
        /* Get needed input position and possibly reached sample offset
 
377
         * from mpg123.
 
378
         */
 
379
        samploff = mpg123_feedseek (data->decoder, samples, mwhence,
 
380
                                    &byteoff);
 
381
 
 
382
        XMMS_DBG ("seeked to %li ... input stream seek following",
 
383
                  (long) samploff);
 
384
 
 
385
        if (samploff < 0) {
 
386
                xmms_error_set (err,
 
387
                                XMMS_ERROR_GENERIC,
 
388
                                mpg123_strerror (data->decoder));
 
389
                return -1;
 
390
        }
 
391
 
 
392
        /* Seek in input stream. */
 
393
        ret = xmms_xform_seek (xform, byteoff, XMMS_XFORM_SEEK_SET, err);
 
394
        if (ret < 0) {
 
395
                return ret;
 
396
        }
 
397
 
 
398
        return samploff;
 
399
}