1
/* XMMS2 plugin for decoding MPEG audio using libmpg123
2
* Copyright (C) 2007-2009 XMMS2 Team
4
* PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
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.
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.
16
* For libmpg123 API have a look at http://mpg123.org/api/ .
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
24
* This should be easy to add for an XMMS2 hacker.
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
34
* Perhaps one can make the generic id3 plugin store the necessary info
35
* for retrieval here, or just keep the id3 tags there...
38
#include "xmms/xmms_xformplugin.h"
39
#include "xmms/xmms_log.h"
40
#include "xmms/xmms_medialib.h"
50
typedef struct xmms_mpg123_data_St {
51
mpg123_handle *decoder;
59
/* input data buffer */
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,
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);
85
xmms_mpg123_plugin_setup (xmms_xform_plugin_t *xform_plugin)
87
xmms_xform_methods_t methods;
90
result = mpg123_init ();
91
if (result != MPG123_OK) {
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);
102
xmms_xform_plugin_config_property_register (xform_plugin,
108
xmms_xform_plugin_indata_add (xform_plugin,
109
XMMS_STREAM_TYPE_MIMETYPE,
111
XMMS_STREAM_TYPE_PRIORITY,
115
/* Well, I usually only see mp3 and mp2 ... layer 1 files
118
xmms_magic_extension_add ("audio/mpeg", "*.mp3");
119
xmms_magic_extension_add ("audio/mpeg", "*.mp2");
120
xmms_magic_extension_add ("audio/mpeg", "*.mp1");
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",
133
xmms_mpg123_init (xmms_xform_t *xform)
135
xmms_mpg123_data_t *data;
142
g_return_val_if_fail (xform, FALSE);
144
data = g_new0 (xmms_mpg123_data_t, 1);
145
xmms_xform_private_data_set (xform, data);
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,
151
data->filesize = result;
154
mpg123_rates (&rates, &num_rates);
156
data->param = mpg123_new_pars (&result);
157
g_return_val_if_fail (data->param, FALSE);
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...
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);
169
/* You could choose a decoder from the list provided by
170
* mpg123_supported_decoders () and give that as second parameter.
172
data->decoder = mpg123_parnew (data->param, NULL, &result);
173
if (data->decoder == NULL) {
174
xmms_log_error ("%s", mpg123_plain_strerror (result));
178
/* Prepare for buffer input feeding. */
179
result = mpg123_open_feed (data->decoder);
180
if (result != MPG123_OK) {
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)) {
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) {
199
/* Fetch ID3v1 data from the end of file if possible */
200
result = xmms_id3v1_get_tags (xform);
202
xmms_log_error ("Seeking error when reading ID3v1 tags");
204
} else if (data->filesize > result) {
205
/* Reduce the size of tag data from the filesize */
206
data->filesize -= result;
209
/* Read data from input until decoded data is available from decoder */
211
/* Parse stream and get info. */
215
ret = xmms_xform_read (xform, data->buf, BUFSIZE, &err);
217
xmms_log_error ("Error when trying to find beginning of stream");
219
} else if (ret == 0) {
220
/* EOF reached before format was found, handled after loop */
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... */
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)
236
result = mpg123_getformat (data->decoder, &data->samplerate,
237
&data->channels, &encoding);
238
if (result != MPG123_OK) {
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);
247
/* Get duration in samples, convert to ms and save to xmms2 */
248
length = mpg123_length (data->decoder);
250
length = (off_t) ((gfloat) length / data->samplerate * 1000);
251
xmms_xform_metadata_set_int (xform,
252
XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION,
256
XMMS_DBG ("mpg123: got stream with %li Hz %i channels, encoding %i",
257
data->samplerate, data->channels, encoding);
259
xmms_xform_outdata_type_add (xform,
260
XMMS_STREAM_TYPE_MIMETYPE,
262
XMMS_STREAM_TYPE_FMT_FORMAT,
263
XMMS_SAMPLE_FORMAT_S16,
264
XMMS_STREAM_TYPE_FMT_CHANNELS,
266
XMMS_STREAM_TYPE_FMT_SAMPLERATE,
267
(gint) data->samplerate,
268
XMMS_STREAM_TYPE_END);
272
xmms_log_error ("mpg123 error: %s", mpg123_strerror (data->decoder));
275
mpg123_delete (data->decoder);
276
mpg123_delete_pars (data->param);
283
xmms_mpg123_destroy (xmms_xform_t *xform)
285
xmms_mpg123_data_t *data;
287
data = xmms_xform_private_data_get (xform);
288
g_return_if_fail (data);
291
mpg123_delete (data->decoder);
292
mpg123_delete_pars (data->param);
298
xmms_mpg123_read (xmms_xform_t *xform, xmms_sample_t *buf, gint len,
301
xmms_mpg123_data_t *data;
302
int result = MPG123_OK;
305
data = xmms_xform_private_data_get (xform);
306
g_return_val_if_fail (data, -1);
311
if (result == MPG123_NEED_MORE) {
312
ret = xmms_xform_read (xform, data->buf, BUFSIZE, err);
315
} else if (ret == 0) {
316
data->eof_found = TRUE;
320
result = mpg123_decode (data->decoder, data->buf, (size_t) ret,
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;
328
} else if (result != MPG123_OK && result != MPG123_NEED_MORE) {
329
/* This is some uncommon result like EOF, handle outside
335
if (result == MPG123_DONE) {
336
/* This is just normal EOF reported from libmpg123 */
337
XMMS_DBG ("Got EOF while decoding stream");
339
} else if (result == MPG123_NEW_FORMAT) {
340
/* FIXME: When we can handle format changes, modify this */
343
"The output format changed, XMMS2 can't handle that");
345
} else if (result == MPG123_ERR) {
348
mpg123_strerror (data->decoder));
356
xmms_mpg123_seek (xmms_xform_t *xform, gint64 samples,
357
xmms_xform_seek_mode_t whence,
360
xmms_mpg123_data_t *data;
362
off_t byteoff, samploff;
365
data = xmms_xform_private_data_get (xform);
366
g_return_val_if_fail (data, -1);
368
if (whence == XMMS_XFORM_SEEK_SET) {
370
} else if (whence == XMMS_XFORM_SEEK_CUR) {
372
} else if (whence == XMMS_XFORM_SEEK_END) {
376
/* Get needed input position and possibly reached sample offset
379
samploff = mpg123_feedseek (data->decoder, samples, mwhence,
382
XMMS_DBG ("seeked to %li ... input stream seek following",
388
mpg123_strerror (data->decoder));
392
/* Seek in input stream. */
393
ret = xmms_xform_seek (xform, byteoff, XMMS_XFORM_SEEK_SET, err);