mpg123-1.32.0
This commit is contained in:
112
doc/LARGEFILE
112
doc/LARGEFILE
@@ -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)
|
||||
|
||||
@@ -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
209
doc/READERS
Normal 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.
|
||||
@@ -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
|
||||
|
||||
@@ -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? -->
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
203
doc/examples/mpg123_to_wav_reader64.c
Normal file
203
doc/examples/mpg123_to_wav_reader64.c
Normal 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;
|
||||
}
|
||||
185
doc/examples/mpg123_to_wav_replaced_io.c
Normal file
185
doc/examples/mpg123_to_wav_replaced_io.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user