mpg123-1.32.0

This commit is contained in:
Ozkan Sezer
2023-09-24 08:51:02 +03:00
parent 89d671ef14
commit 2279cffc0f
241 changed files with 18940 additions and 16360 deletions

View File

@@ -1,3 +1,115 @@
The state from 1.32.0 on:
=========================
Yet again, things had to change. The previous state works fine for Unix (adjacent)
sytems, also MinGW stuff on Windows, but it still suffers from off_t being shifty
and not behaving as I expect, even, when people try to build mpg123 with rogue
compilers like MSVC, where off_t is always 32 bits, no largefile support, the whole
idea falls flat. I was pushed to introduce API without off_t where the user can
provide I/O and ensure largefile support and whatnot without being encumbered by
offsets possibly limited to 32 bits.
So this is a new set of API functions with the explicit suffix 64 (not _64), notably
simple functions like
int64_t mpg123_tell64()
but also
int mpg123_reader64()
which takes callbacks that work on int64_t, always. The internal reader code path
in libmpg123 uses either 32 bit or 64 bit offsets, but throughout the library,
it is 64 bits being used as if we supported large files everywhere. And in fact,
we do, if the user provides callbacks that are smart enough.
All existing offset-sensitive code paths are to be rephrased in terms of the new
portable API (portable, because the platform specifics of off_t implementation are
not present). All old API entry points in that regard become wrappers over this
portable API.
The build of libmpg123 needs explicit awareness of largefile support to properly
reason about which wrapper uses which types. It makes use of off_t at default
size and explicit off64_t if available. This results in this for a typical 32 bit
Linux system:
int64_t mpg123_tell64(); // passing on the internal int64_t value
off_t mpg123_tell(); // converting to 32 bit off_t (overflow check)
off_t mpg123_tell_32(); // converting to 32 bit off_t (overflow check)
off64_t mpg123_tell_64(); // converting to 64 bit off_t (no overflow)
A 64 bit Linux would have only these:
int64_t mpg123_tell64(); // passing on the internal int64_t value
off_t mpg123_tell(); // converting to 64 bit off_t (no overflow)
off_t mpg123_tell_64(); // converting to 64 bit off_t (no overflow)
This would look the same on a 32 bit system that also went for fixed 64 bit offsets,
or if you
#define _FILE_OFFSET_BITS 64
during build on a sensitive system. On a funny system that has 64 bit native integers
but still 32 bit off_t, this would also be:
int64_t mpg123_tell64(); // passing on the internal int64_t value
off_t mpg123_tell(); // converting to 32 bit off_t (overflow check)
off_t mpg123_tell_32(); // converting to 32 bit off_t (overflow check)
If this weird system would be also largefile-sensitive, you get the additional
off64_t mpg123_tell_64(); // converting to 64 bit off_t (no overflow)
entry point.
The whole point here: If you stick to mpg123_tell64(), you always can assume
64 bit offsets on any platform. If you also provide your own reader functions, you
can ensure it. Even if we don't properly do large file I/O in libmpg123 itself in
an MSVC build, users can provide it with their application. Providing a full mpg123
program binary along with it in that environment is not a current target. You want
the library.
The point that every platform gets the _64 suffix means that the header can still
do the renaming of function calls if client code defines _FILE_OFFSET_BITS and not
depend on any build-time switches for that. This is what we do officially now with
headers that are not preprocessed anymore, independent of platform.
The build, just assuming int64 is available, needs to know some bits:
- SIZEOF_OFF_T==8: Is the system 64 bit clean, anyway, with 64 bit off_t?
- LFS_LARGEFILE_64: Is there explicit 64 bit I/O for the 32 bit system?
This includes availability of off64_t.
If SIZEOF_OFF_T==8, there will be all 64 bit internal code and two wrappers:
off_t mpg123_tell(); // converting to 64 bit off_t (no overflow)
off_t mpg123_tell_64(); // converting to 64 bit off_t (no overflow)
The second question isn't even needed. If SIZEOF_OFF_T==4, you get
off_t mpg123_tell(); // converting to 32 bit off_t (overflow check)
off_t mpg123_tell_32(); // converting to 32 bit off_t (overflow check)
and the additional decision of LFS_LARGEFILE_64 gives actual 64 bit internal
reader code and the wrapper
off64_t mpg123_tell_64(); // converting to 64 bit off_t (no overflow)
along with that. There is nothing more to know about a system. I do not care how
large your long is. I only ask
1. How big is your off_t?
2. If small, can you give me some off64_t?
That's all what there is to it. The build is not even interested in whether off_t
changes size. This is a detail on the client side that it then gets the off64_t
versions. I do not support a system that allows changing off_t size _without_
off64_t being available.
The state from 1.15.4 on:
=========================
Regarding largefile setup, client apps can be built three ways:
1. _FILE_OFFSET_BITS == 64 (header maps to mpg123_open_64)

View File

@@ -14,6 +14,7 @@ EXTRA_DIST += \
doc/THANKS \
doc/ACCURACY \
doc/LARGEFILE \
doc/READERS \
doc/libmpg123_speed.txt \
doc/doxyhead.xhtml \
doc/doxyfoot.xhtml \
@@ -21,6 +22,8 @@ EXTRA_DIST += \
doc/doxygen.conf \
doc/windows-notes.html \
doc/examples/mpg123_to_out123.c \
doc/examples/mpg123_to_wav_replaced_io.c \
doc/examples/mpg123_to_wav_reader64.c \
doc/examples/scan.c \
doc/examples/mpglib.c \
doc/examples/id3dump.c \

209
doc/READERS Normal file
View File

@@ -0,0 +1,209 @@
The libmpg123 web of reader abstractions
========================================
Somehow the differing ways of getting compressed data into libmpg123 reached
unholy numbers with the years. As keeper of the legacy, I got quite some of that
to keep. There are intersectional layers ... however you might call it.
An attempt to get an overview and be able to refactor that for the glorious
portable API of mpg123 1.32.0.
The frame struct has two parts concerned with input streams.
struct reader *rd; /* pointer to the reading functions */
struct reader_data rdat; /* reader data and state info */
The distinction is blurred a bit: Over time, I added function pointers to
the latter.
1. Basic methods of data input for (seekable) streams
-----------------------------------------------------
These reside in the struct reader_data member of the frame struct (mpg123_handle), so
normally fr->rdat. This is an assortment of (user-supplied) function pointers
and stores the stream position and length. The latter was the initial purpose.
1.1 Reader based around POSIX I/O
---------------------------------
This one relies on some read() and lseek() to give the input bytes. With an
innocent mpg123_open(), you trigger compat_open() and further work on the
resulting fd.
But you can also do mpg123_open_fd() to provide the descriptor yourself. Same
further code path, just no closing.
1.1.1 Timeout read
------------------
The timeout reader is a variant that squeezes into the internal POSIX I/O with
some fcntl() and select() on the file descriptor, and a separate reader callback
stored as fdread().
1.2 Replaced I/O on file descriptor
-----------------------------------
After calling mpg123_replace_reader(), you have your callbacks (or the respective
fallback callback) operating on the file descriptor that could have resulted
from internal opening or been handed over to libmpg123.
1.3 Replaced I/O on custom handle
---------------------------------
This replaces both read() and lseek() with your callbacks and opening is
full external, just the handle being handed over via mpg123_open_handle().
1.4 Replaced I/O on custom handle using 64 bit offsets
------------------------------------------------------
This is to come, the above just with a differing style of callbacks that avoid
off_t. I intend to pack all the above into wrapper code and have this whole
first aspect of differing callbacks removed.
2. Abstractions
---------------
The actual interface to the parser is given by instances of struct reader. This
is usually accessed as fr->rd and contains function pointers to specific routines
like fullread() and head_read(). These access the basic methods behind the scenes.
There is overlap in the functions. The main differentiator is the fullread() call,
which is the next layer of read(). I guess code sharing could be one excuse not
to have each of these as a wholly separate I/O layer implementation.
2.1 READER_STREAM
-----------------
This reader handles a plain possibly seekable input stream. It introduces the
plain_fullread() function which loops over fr->rdat.fdread() until the desired
bytes are aquired or EOF occurs. There is no signal handling. A return value
less than zero is an error and the end, the function returning a short byte
count. This function also advances fr->rdat.filepos if the reader is not
buffered.
2.2 READER_ICY_STREAM
---------------------
This replaces plain_fullread() with icy_fullread(), which looks out for ICY
metadata at the configured interval. It resorts to fr->rdat.fdread() and
plain_fullread() to do its chores.
2.3 READER_FEED
---------------
The reader handling libmpg123 feeder mode. It stuffs data into an internal buffer
and extracts from that, handing out READER_MORE as error to be recovered from
when the client provides more data. It provides a bit of seeking within the
buffer for parsing (look-ahead) purposes, before read data is purged from the
buffer chain.
The actual mid-level reader here is feed_read(), wrapping over the bufferchain data
structre with its methods.
2.4 READER_BUF_STREAM
---------------------
For some reason, I had to add a mode for a stream with some buffering
(MPG123_SEEKBUFFER). Well ... yes, MPEG parsing is just more fun if you can peek
ahead and have a little window of input data to work with. This used to employ
buffered_fullread(), which in turn called fr->rdat.fullread(), which was
plain_fullread() for this variant, and wraped a bufferchain around it.
Now this got its separate buffered_plain_fullread() without the extra function
pointer in fr->rdat.
2.5 READER_BUF_ICY_STREAM
-------------------------
This is the same buffered reader but with buffered_icy_fullread() instead.
3. Control flow for setting up a stream
=======================================
3.1 mpg123_open()
-----------------
Client code just opens a track or possibly called mpg123_replace_reader()
beforehand, which does not change the opening behaviour.
This accesses the given file path via compat_open(), stores a file descriptor,
calls reader stream init which checks seekability (also fetching ID3v1) and
stores file size. This results in one of the READER_*STREAM family.
The lfs_wrap machinery gets triggrered and inserts its callbacks, working
on the prepared wrapper data.
3.2 mpg123_open_fd()
--------------------
This just skips the compat_open() and stores the given file descriptor, assuming
that it works with the configured callbacks. The same dance with stream setup.
The lfs_wrap machinery gets triggrered and inserts its callbacks, working
on the prepared wrapper data.
3.3 mpg123_open_handle()
------------------------
Also skips the opening, stores the handle and does the stream setup.
This shall not trigger callback insertion. The idea is that the user did
call mpg123_reader64() or a wrapper variant of it before. The wrapper code
itself finalizes its work with a call to mpg123_reader64().
Oh, wait. What about the other wrapper calls? Client code calls
mpg123_replace_reader_handle() with its callbacks, be it off_t or off64_t.
This needs to trigger preparation of wrapperdata and installment of wrapper
callbacks via mpg123_reader64(). A subsequent mpg123_open_handle() needs
to store the actual client handle inside the wrapperdata structure and use
the latter as iohandle for stream operation. I need tell apart internal and
external use of mpg123_reader64().
So store a flag for that? Is there another way without introducing yet another
function? Well, the wrapperdata can have two states that fit this scenario:
- not present at all
- present with a respective state set
I want to avoid unnecessary allocation of the wrapperdata (just because I am that
kind of person). So I need to ensure that INT123_wrap_open() when called with
an external handle and not encountering an existing wrapperdata instance, does not
allocate one, but just silently does nothing, as there is nothing to do. Well,
it can check if callbacks are in place. At least that.
3.4 mpg123_open_feed()
----------------------
Prepares for the non-seekable feeder mode, limited stream setup because peeking
at the end won't work.
This does not trigger the wrapper ... except ... should it unregister its
callbacks? No. The code path of the feeder is separate enough that it does
not interfere.
4. Plan
=======
Keep the abstractions in readers.c, move all variants of POSIX-like callback stuff
into the wrapper section (lfs_wrap.c for now). In theory, the buffer chain for
the feeder could also be moved into a variant hidden behind mpg123_reader64(). Maybe
in the future. At some point.
I don't want any mpg123 internals regarding the frame struct in the wrapper
implementation. So maybe lfs_wrap.c only offers wrapper setup as such and
libmpg123.c does the actual stream opening? No. The explicit largefile stuff
needs to be handled (O_LARGEFILE). But the further stream setup ... that
should happen in mpg123_open() and friends, after reader handle setup.
I still need fr->rdat.flags for selecting buffered etc., but not for
READER_FD_OPENEND or READER_NONBLOCK.
Both fr->rdat.fullread and fr->rdat.fdread are gone now. The picture is getting
clearer.
I made the static functions fdread() and fdseek() robust against missing
callbacks. It's a question if we'd rather want to catch those earlier, though.

View File

@@ -179,6 +179,10 @@ to help parsers decide if they got the whole list.
a = 2: playing unpaused
a = 3: end of track reached (followed by another status response)
@DRAIN <s>
Output buffer of s seconds is being drained, without chance of interruption.
(since v11)
@E <a>
An error occured
Errors may be also reported by mpg123 through

View File

@@ -9,10 +9,10 @@
<strong>Note:</strong>
This API doc is automatically generated from the current development version that you can get via Subversion or as a daily snapshot from <a href="http://mpg123.org/snapshot">http://mpg123.org/snapshot</a>.
There may be differences (additions) compared to the latest stable release. See
<a href="http://mpg123.org/cgi-bin/viewvc.cgi/trunk/NEWS.libmpg123?view=markup">NEWS.libmpg123</a>,
<a href="http://mpg123.org/cgi-bin/viewvc.cgi/trunk/NEWS.libout123?view=markup">NEWS.libout123</a>,
<a href="http://mpg123.org/cgi-bin/viewvc.cgi/trunk/NEWS.libsyn123?view=markup">NEWS.libsyn123</a>,
and the overall <a href="http://mpg123.org/cgi-bin/viewvc.cgi/trunk/NEWS?view=markup">NEWS</a> file on libmpg123 versions and important changes between them.<br />
<a href="https://mpg123.org/trunk/NEWS.libmpg123">NEWS.libmpg123</a>,
<a href="https://mpg123.org/trunk/NEWS.libout123">NEWS.libout123</a>,
<a href="https://mpg123.org/trunk/NEWS.libsyn123">NEWS.libsyn123</a>,
and the overall <a href="https://mpg123.org/trunk/NEWS">NEWS</a> file on libmpg123 versions and important changes between them.<br />
Let me emphasize that the policy for the lib*123 family is to always stay backwards compatible -- only <em>additions</em> are planned (and it's not yet planned to change the plans;-).
</div>
<div> <!-- for some reason, doxygen closes a div where non is open, bug in my version? -->

View File

@@ -2,6 +2,7 @@ targets = \
feedseek \
mpg123_to_out123 \
mpg123_to_wav_replaced_io \
mpg123_to_wav_reader64 \
scan \
id3dump \
mpglib \
@@ -40,6 +41,9 @@ mpg123_to_out123: mpg123_to_out123.c
mpg123_to_wav_replaced_io: mpg123_to_wav_replaced_io.c
$(compile) -o $@ $< $(OUT123_CFLAGS) $(OUT123_LDFLAGS) $(linkflags)
mpg123_to_wav_reader64: mpg123_to_wav_reader64.c
$(compile) -o $@ $< $(OUT123_CFLAGS) $(OUT123_LDFLAGS) $(linkflags)
feedseek: feedseek.c
$(compile) -o feedseek feedseek.c $(OUT123_CFLAGS) $(OUT123_LDFLAGS) $(linkflags)

View File

@@ -69,7 +69,8 @@ int do_work(mpg123_handle *m)
ret = mpg123_open_fd(m, STDIN_FILENO);
if(ret != MPG123_OK) return ret;
while( (ret = mpg123_framebyframe_next(m)) == MPG123_OK || ret == MPG123_NEW_FORMAT )
ssize_t wret = 0;
while( !wret && (ret = mpg123_framebyframe_next(m)) == MPG123_OK || ret == MPG123_NEW_FORMAT )
{
unsigned long header;
unsigned char *bodydata;
@@ -82,14 +83,20 @@ int do_work(mpg123_handle *m)
for(i=0; i<4; ++i) hbuf[i] = (unsigned char) ((header >> ((3-i)*8)) & 0xff);
/* Now write out both header and data, fire and forget. */
write(STDOUT_FILENO, hbuf, 4);
write(STDOUT_FILENO, bodydata, bodybytes);
wret = write(STDOUT_FILENO, hbuf, 4);
if(!wret)
wret = write(STDOUT_FILENO, bodydata, bodybytes);
fprintf(stderr, "%zu: header 0x%08lx, %zu body bytes\n", ++count, header, bodybytes);
}
}
if(ret != MPG123_DONE)
fprintf(stderr, "Some error occured (non-fatal?): %s\n", mpg123_strerror(m));
fprintf(stderr, "Some error occured (non-fatal?): %s\n", mpg123_strerror(m));
if(wret)
{
fprintf(stderr, "Write error.\n");
ret = MPG123_ERR;
}
fprintf(stderr, "Done with %zu MPEG frames.\n", count);

View File

@@ -0,0 +1,203 @@
/*
mpg123_to_wav_replaced_io.c
This is example code only sensible to be considered in the public domain.
Initially written by Nicholas Humfrey (moved to 64 bit handle I/O by Thomas Orgis).
This example program demonstrates how to use libmpg123 to decode a file to WAV (writing via libsndfile), while doing the I/O (read and seek) with custom callback functions.
This should cater for any situation where you have some special means to get to the data (like, mmapped files / plain buffers in memory, funky network streams).
Disregarding format negotiations, the basic synopsis is:
mpg123_init()
mpg123_new()
mpg123_replace_reader_handle()
mpg123_open_handle()
mpg123_read()
mpg123_close()
mpg123_delete()
mpg123_exit()
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <mpg123.h>
#include <out123.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef _MSC_VER
#include <unistd.h>
#else
#include <io.h>
#endif
#include <string.h>
#if MPG123_API_VERSION < 48
#error "Need minimum mpg123 API version 48."
#endif
void usage(const char *cmd)
{
printf("Usage: %s <input> <output>\n", cmd);
exit(99);
}
void cleanup(mpg123_handle *mh, out123_handle *ao)
{
out123_del(ao);
/* It's really to late for error checks here;-) */
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
}
/* Simple handle for you private I/O data. */
struct ioh { int fd; };
/* The callback functions; simple wrappers over standard I/O.
They could be anything you like... */
static int read_cb(void *handle, void *buf, size_t sz, size_t *got)
{
if(!handle)
return -1;
struct ioh *h = handle;
errno = 0;
// Ideally, you check for singnals in here where they should not interrupt
// operations (handle EINTR, for example).
ssize_t gots = read(h->fd, buf, sz);
if(gots < 0)
{
fprintf(stderr, "read error: %s\n", strerror(errno));
return -1;
}
if(got)
*got = (size_t)gots;
return 0;
}
// You should be more careful if off_t < int64_t. Ideally, you know
// you have 64 bit I/O.
static int64_t lseek_cb(void *handle, int64_t offset, int whence)
{
if(!handle)
return -1;
int64_t ret;
struct ioh *h = handle;
ret = (int64_t)lseek(h->fd, (off_t)offset, whence);
if(ret < 0) fprintf(stderr, "seek error: %s\n", strerror(errno));
return ret;
}
/* The cleanup handler is called on mpg123_close(), it can cleanup your part of the mess... */
void cleanup_cb(void *handle)
{
if(!handle)
return;
struct ioh *h = handle;
close(h->fd);
h->fd = -1;
}
int main(int argc, char *argv[])
{
mpg123_handle *mh = NULL;
out123_handle *ao = NULL;
unsigned char* buffer = NULL;
size_t buffer_size = 0;
size_t done = 0;
int channels = 0, encoding = 0;
long rate = 0;
int err = MPG123_OK;
off_t samples = 0;
struct ioh *iohandle;
if (argc!=3) usage(argv[0]);
printf( "Input file: %s\n", argv[1]);
printf( "Output file: %s\n", argv[2]);
#if MPG123_API_VERSION < 46
// Newer versions of the library don't need that anymore, but it is safe
// to have the no-op call present for compatibility with old versions.
err = mpg123_init();
#endif
errno = 0;
iohandle = malloc(sizeof(struct ioh));
iohandle->fd = open(argv[1], O_RDONLY);
if(iohandle->fd < 0)
{
fprintf(stderr, "Cannot open input file (%s).\n", strerror(errno));
return -1;
}
if( err != MPG123_OK || (mh = mpg123_new(NULL, &err)) == NULL
/* Let mpg123 work with the file, that excludes MPG123_NEED_MORE messages. */
|| mpg123_reader64(mh, read_cb, lseek_cb, cleanup_cb) != MPG123_OK
|| mpg123_open_handle(mh, iohandle) != MPG123_OK
/* Peek into track and get first output format. */
|| mpg123_getformat(mh, &rate, &channels, &encoding) != MPG123_OK )
{
fprintf( stderr, "Trouble with mpg123: %s\n",
mh==NULL ? mpg123_plain_strerror(err) : mpg123_strerror(mh) );
cleanup(mh, ao);
return -1;
}
if(encoding != MPG123_ENC_SIGNED_16)
{ /* Signed 16 is the default output format anyways; it would actually by only different if we forced it.
So this check is here just for this explanation. */
cleanup(mh, ao);
fprintf(stderr, "Bad encoding: 0x%x!\n", encoding);
return -2;
}
/* Ensure that this output format will not change (it could, when we allow it). */
mpg123_format_none(mh);
mpg123_format(mh, rate, channels, encoding);
printf("Creating 16bit WAV with %i channels and %liHz.\n", channels, rate);
if(
!(ao = out123_new())
|| out123_open(ao, "wav", argv[2])
|| out123_start(ao, rate, channels, encoding)
)
{
fprintf(stderr, "Cannot create / start output: %s\n"
, out123_strerror(ao));
cleanup(mh, ao);
return -1;
}
/* Buffer could be almost any size here, mpg123_outblock() is just some recommendation.
Important, especially for sndfile writing, is that the size is a multiple of sample size. */
buffer_size = mpg123_outblock( mh );
buffer = malloc( buffer_size );
do
{
err = mpg123_read( mh, buffer, buffer_size, &done );
out123_play(ao, buffer, done);
samples += done/sizeof(short);
/* We are not in feeder mode, so MPG123_OK, MPG123_ERR and MPG123_NEW_FORMAT are the only possibilities.
We do not handle a new format, MPG123_DONE is the end... so abort on anything not MPG123_OK. */
} while (done && err==MPG123_OK);
free(buffer);
if(err != MPG123_DONE)
fprintf( stderr, "Warning: Decoding ended prematurely because: %s\n",
err == MPG123_ERR ? mpg123_strerror(mh) : mpg123_plain_strerror(err) );
samples /= channels;
printf("%li samples written.\n", (long)samples);
cleanup(mh, ao);
free(iohandle);
return 0;
}

View File

@@ -0,0 +1,185 @@
/*
mpg123_to_wav_replaced_io.c
This is example code only sensible to be considered in the public domain.
Initially written by Nicholas Humfrey (moved to handle I/O by Thomas Orgis).
This example program demonstrates how to use libmpg123 to decode a file to WAV (writing via libout123), while doing the I/O (read and seek) with custom callback functions.
This should cater for any situation where you have some special means to get to the data (like, mmapped files / plain buffers in memory, funky network streams).
Disregarding format negotiations, the basic synopsis is:
mpg123_init()
mpg123_new()
mpg123_replace_reader_handle()
mpg123_open_handle()
mpg123_read()
mpg123_close()
mpg123_delete()
mpg123_exit()
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <mpg123.h>
#include <out123.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef _MSC_VER
#include <unistd.h>
#else
#include <io.h>
#endif
#include <string.h>
void usage(const char *cmd)
{
printf("Usage: %s <input> <output>\n", cmd);
exit(99);
}
void cleanup(mpg123_handle *mh, out123_handle *ao)
{
out123_del(ao);
/* It's really to late for error checks here;-) */
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
}
/* Simple handle for you private I/O data. */
struct ioh { int fd; };
/* The callback functions; simple wrappers over standard I/O.
They could be anything you like... */
static mpg123_ssize_t read_cb(void *handle, void *buf, size_t sz)
{
mpg123_ssize_t ret;
struct ioh *h = handle;
errno = 0;
ret = read(h->fd, buf, sz);
if(ret < 0) fprintf(stderr, "read error: %s\n", strerror(errno));
return ret;
}
static off_t lseek_cb(void *handle, off_t offset, int whence)
{
off_t ret;
struct ioh *h = handle;
ret = lseek(h->fd, offset, whence);
if(ret < 0) fprintf(stderr, "seek error: %s\n", strerror(errno));
return ret;
}
/* The cleanup handler is called on mpg123_close(), it can cleanup your part of the mess... */
void cleanup_cb(void *handle)
{
struct ioh *h = handle;
close(h->fd);
h->fd = -1;
}
int main(int argc, char *argv[])
{
mpg123_handle *mh = NULL;
out123_handle *ao = NULL;
unsigned char* buffer = NULL;
size_t buffer_size = 0;
size_t done = 0;
int channels = 0, encoding = 0;
long rate = 0;
int err = MPG123_OK;
off_t samples = 0;
struct ioh *iohandle;
if (argc!=3) usage(argv[0]);
printf( "Input file: %s\n", argv[1]);
printf( "Output file: %s\n", argv[2]);
#if MPG123_API_VERSION < 46
// Newer versions of the library don't need that anymore, but it is safe
// to have the no-op call present for compatibility with old versions.
err = mpg123_init();
#endif
errno = 0;
iohandle = malloc(sizeof(struct ioh));
iohandle->fd = open(argv[1], O_RDONLY);
if(iohandle->fd < 0)
{
fprintf(stderr, "Cannot open input file (%s).\n", strerror(errno));
return -1;
}
if( err != MPG123_OK || (mh = mpg123_new(NULL, &err)) == NULL
/* Let mpg123 work with the file, that excludes MPG123_NEED_MORE messages. */
|| mpg123_replace_reader_handle(mh, read_cb, lseek_cb, cleanup_cb) != MPG123_OK
|| mpg123_open_handle(mh, iohandle) != MPG123_OK
/* Peek into track and get first output format. */
|| mpg123_getformat(mh, &rate, &channels, &encoding) != MPG123_OK )
{
fprintf( stderr, "Trouble with mpg123: %s\n",
mh==NULL ? mpg123_plain_strerror(err) : mpg123_strerror(mh) );
cleanup(mh, ao);
return -1;
}
if(encoding != MPG123_ENC_SIGNED_16)
{ /* Signed 16 is the default output format anyways; it would actually by only different if we forced it.
So this check is here just for this explanation. */
cleanup(mh, ao);
fprintf(stderr, "Bad encoding: 0x%x!\n", encoding);
return -2;
}
/* Ensure that this output format will not change (it could, when we allow it). */
mpg123_format_none(mh);
mpg123_format(mh, rate, channels, encoding);
printf("Creating 16bit WAV with %i channels and %liHz.\n", channels, rate);
if(
!(ao = out123_new())
|| out123_open(ao, "wav", argv[2])
|| out123_start(ao, rate, channels, encoding)
)
{
fprintf(stderr, "Cannot create / start output: %s\n"
, out123_strerror(ao));
cleanup(mh, ao);
return -1;
}
/* Buffer could be almost any size here, mpg123_outblock() is just some recommendation.
Important, especially for sndfile writing, is that the size is a multiple of sample size. */
buffer_size = mpg123_outblock( mh );
buffer = malloc( buffer_size );
do
{
err = mpg123_read( mh, buffer, buffer_size, &done );
out123_play(ao, buffer, done);
samples += done/sizeof(short);
/* We are not in feeder mode, so MPG123_OK, MPG123_ERR and MPG123_NEW_FORMAT are the only possibilities.
We do not handle a new format, MPG123_DONE is the end... so abort on anything not MPG123_OK. */
} while (done && err==MPG123_OK);
free(buffer);
if(err != MPG123_DONE)
fprintf( stderr, "Warning: Decoding ended prematurely because: %s\n",
err == MPG123_ERR ? mpg123_strerror(mh) : mpg123_plain_strerror(err) );
samples /= channels;
printf("%li samples written.\n", (long)samples);
cleanup(mh, ao);
free(iohandle);
return 0;
}