diff --git a/NEWS b/NEWS index 8b137891791fe96927ad78e64b0aad7bded08bdc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/NEWS +++ b/NEWS @@ -1 +0,0 @@ - diff --git a/README b/README index 139597f9cb07c5d48bed18984ec4747f4b4f3438..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/README +++ b/README @@ -1,2 +0,0 @@ - - diff --git a/configure.ac b/configure.ac index 672001516443e3416b28fe559308a06cecb9996f..4d6497db1360c3d25a864c43991326b48f19c3d1 100644 --- a/configure.ac +++ b/configure.ac @@ -3,24 +3,39 @@ AC_INIT AC_CONFIG_SRCDIR([src/liboggz/oggz.c]) AC_PREREQ(2.53) -AC_CONFIG_AUX_DIR(autotools) + AC_CANONICAL_TARGET -AM_INIT_AUTOMAKE(liboggz, 0.5.43) +AM_INIT_AUTOMAKE(liboggz, 0.8.0) AM_CONFIG_HEADER(config.h) -SHARED_VERSION_INFO="0:5:0" +SHARED_VERSION_INFO="1:0:0" SHLIB_VERSION_ARG="" # Checks for programs. AC_PROG_CC +AC_PROG_CPP AC_PROG_INSTALL +AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_LIBTOOL +AC_PROG_RANLIB AC_C_CONST AC_C_BIGENDIAN +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h inttypes.h stdlib.h string.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_OFF_T +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_REALLOC +AC_CHECK_FUNCS([memmove]) + # Check for doxygen AC_CHECK_PROG(HAVE_DOXYGEN, doxygen, true, false) @@ -30,15 +45,15 @@ if test $HAVE_DOXYGEN = "false"; then fi # Check for docbook -AC_CHECK_PROG(HAVE_DOCBOOK, docbook2man, true, false) -AM_CONDITIONAL(HAVE_DOCBOOK,$HAVE_DOCBOOK) - -dnl Check for types +AC_CHECK_PROG(HAVE_DOCBOOKTOMAN, docbook-to-man, true, false) +AM_CONDITIONAL(HAVE_DOCBOOKTOMAN,$HAVE_DOCBOOKTOMAN) +AC_CHECK_PROG(HAVE_DOCBOOK2HTML, docbook2html, true, false) +AM_CONDITIONAL(HAVE_DOCBOOK2HTML,$HAVE_DOCBOOK2HTML) dnl Checks for libraries. LIBS="" -# check for getopt in standard library +# check for getopt in a separate library HAVE_GETOPT=no AC_CHECK_LIB(getopt, getopt, HAVE_GETOPT="yes") if test "x$HAVE_GETOPT" = xyes ; then @@ -46,6 +61,13 @@ if test "x$HAVE_GETOPT" = xyes ; then AC_SUBST(GETOPT_LIBS) fi +# check for getopt_long in standard library +HAVE_GETOPT_LONG=no +AC_CHECK_FUNC(getopt_long, HAVE_GETOPT_LONG="yes") +if test "x$HAVE_GETOPT_LONG" = xyes ; then + AC_DEFINE(HAVE_GETOPT_LONG, [], [Define to 1 if you have the 'getopt_long' function]) +fi + dnl Overall configuration success flag oggz_config_ok=yes @@ -211,6 +233,7 @@ src/Makefile src/liboggz/Version_script src/liboggz/Makefile src/tools/Makefile +src/tools/oggzdiff src/tests/Makefile src/examples/Makefile oggz.pc diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 68e990c8a6883d6114f94733356224efaabe4c8e..37b1dbdfc24db949e43e1efac5afe7236d366f12 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -419,7 +419,7 @@ EXAMPLE_RECURSIVE = NO # directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = +IMAGE_PATH = . # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program diff --git a/doc/Makefile.am b/doc/Makefile.am index 9c0044c7919c8ff1c52e3a486a7ccd2876889fa0..c77ca11df81f56242b49371072d6b9b314c26c1a 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,11 +1,32 @@ docdir=$(prefix)/share/doc/@PACKAGE@ -EXTRA_DIST = Doxyfile.in liboggz +EXTRA_DIST = Doxyfile.in \ + forcefeed.fig forcefeed.eps forcefeed.png \ + hungry.fig hungry.eps hungry.png \ + oggzdump.1.sgml oggzdiff.1.sgml \ + oggzdump.1 oggzdiff.1 -all: doxygen-build.stamp +man_MANS = oggzdump.1 oggzdiff.1 + +doc_DATA = doxygen-build.stamp + +html: oggzdump.1.html + +if HAVE_DOCBOOKTOMAN +%.1: %.1.sgml + docbook-to-man $< > $@ +else +%.1: %.1.sgml +endif + +if HAVE_DOCBOOK2HTML +%.1.html: %.1.sgml + -docbook2html $< + mv index.html $@ +endif if HAVE_DOXYGEN -doxygen-build.stamp: Doxyfile $(top_srcdir)/include/oggz/*.h +doxygen-build.stamp: Doxyfile $(top_srcdir)/include/oggz/*.h $(top_srcdir)/src/examples/*.c doxygen touch doxygen-build.stamp else @@ -14,6 +35,23 @@ doxygen-build.stamp: touch doxygen-build.stamp endif +dist_docdir = $(distdir)/liboggz + +dist-hook: + if test -d liboggz; then \ + mkdir $(dist_docdir); \ + for dir in liboggz/*; do \ + if test -d $$dir; then \ + b=`basename $$dir`; \ + mkdir $(dist_docdir)/$$b; \ + for f in $$dir/*; do \ + cp -p $$f $(dist_docdir)/$$b; \ + done \ + fi \ + done \ + fi + + install-data-local: doxygen-build.stamp $(mkinstalldirs) $(docdir) if test -d liboggz; then \ @@ -21,7 +59,9 @@ install-data-local: doxygen-build.stamp if test -d $$dir; then \ b=`basename $$dir`; \ $(mkinstalldirs) $(docdir)/$$b; \ - (cd $$dir && $(INSTALL_DATA) * $(docdir)/$$b) \ + for f in $$dir/*; do \ + $(INSTALL_DATA) $$f $(docdir)/$$b; \ + done \ fi \ done \ fi diff --git a/include/oggz/Makefile.am b/include/oggz/Makefile.am index 0238c03be629175cdbce9bc97dc4f3da9a28e77f..f0384dcb3a486f06abebac31018cb1afb15df336 100644 --- a/include/oggz/Makefile.am +++ b/include/oggz/Makefile.am @@ -2,5 +2,5 @@ # Include files to install includedir = $(prefix)/include/oggz -include_HEADERS = oggz.h oggz_constants.h +include_HEADERS = oggz.h oggz_constants.h oggz_table.h diff --git a/include/oggz/oggz.h b/include/oggz/oggz.h index da2a68297a5ae3d9783b404fddf7f83b0b5c11c3..df6ff3961099a20ca8a02253fd354a8dc51d0896 100644 --- a/include/oggz/oggz.h +++ b/include/oggz/oggz.h @@ -33,8 +33,11 @@ #ifndef __OGGZ_H__ #define __OGGZ_H__ +#include + #include #include +#include /** \mainpage * @@ -46,11 +49,20 @@ * at Xiph.Org, originally to * support the Ogg Vorbis audio format. * - * liboggz supports the flexibility afforded by the Ogg file format - * - * - A simple, callback based open/read/close or open/write/close interface - * to all Ogg files - * - A customisable seeking abstraction for seeking on multitrack Ogg data + * liboggz supports the flexibility afforded by the Ogg file format while + * presenting the following API niceties: + * + * - Strict adherence to the formatting requirements of Ogg + * \link basics bitstreams \endlink, to ensure that only valid bitstreams + * are generated + * - A simple, callback based open/read/close or open/write/close + * \link oggz.h interface \endlink to raw Ogg files + * - A customisable \link seek_api seeking \endlink abstraction for + * seeking on multitrack Ogg data + * - A packet queue for feeding incoming packets for writing, with callback + * based notification when this queue is empty + * - A handy \link oggz_table.h table \endlink structure for storing + * information on each logical bitstream * * \subsection contents Contents * @@ -58,7 +70,7 @@ * Information about Ogg required to understand liboggz * * - \link oggz.h oggz.h \endlink: - * Documentation of the Oggz API + * Documentation of the Oggz C API * * - \link configuration Configuration \endlink: * Customizing liboggz to only read or write. @@ -88,17 +100,201 @@ * * \section Terminology * - * \subsection Bitstreams + * The monospace text below is quoted directly from RFC 3533. + * For each concept introduced, tips related to liboggz are provided + * in bullet points. * - * Physical bitstreams contain interleaved logical bitstreams. - * Each logical bitstream is uniquely identified by a serial number or - * \a serialno. + * \subsection bitstreams Physical and Logical Bitstreams * - * - \a serialno: an integer identifying a logical bitstream. + * The raw data of an Ogg stream, as read directly from a file or network + * socket, is called a physical bitstream. + * +
+   The result of an Ogg encapsulation is called the "Physical (Ogg)
+   Bitstream".  It encapsulates one or several encoder-created
+   bitstreams, which are called "Logical Bitstreams".  A logical
+   bitstream, provided to the Ogg encapsulation process, has a
+   structure, i.e., it is split up into a sequence of so-called
+   "Packets".  The packets are created by the encoder of that logical
+   bitstream and represent meaningful entities for that encoder only
+   (e.g., an uncompressed stream may use video frames as packets).
+
+ * + * \subsection pages Packets and Pages + * + * Within the Ogg format, packets are written into \a pages. You can think + * of pages like pages in a book, and packets as items of the actual text. + * Consider, for example, individual poems or short stories as the packets. + * Pages are of course all the same size, and a few very short packets could + * be written into a single page. On the other hand, a very long packet will + * use many pages. + * + * - liboggz handles the details of writing packets into pages, and of + * reading packets from pages; your application deals only with + * ogg_packet structures. + * - Each ogg_packet structure contains a block of data and its + * length in bytes, plus other information related to the stream structure + * as explained below. + * + * \subsection serialno * - * \subsection Packets + * Each logical bitstream is uniquely identified by a serial number or + * \a serialno. * - * + * - Packets are always associated with a \a serialno. This is not actually + * part of the ogg_packet structure, so wherever you see an + * ogg_packet in the liboggz API, you will see an accompanying + * \a serialno. + * +
+   This unique serial number is created randomly and does not have any
+   connection to the content or encoder of the logical bitstream it
+   represents.
+
+ * + * - Use oggz_serialno_new() to generate a new serial number for each + * logical bitstream you write. + * - Use an \link oggz_table.h OggzTable \endlink, keyed by \a serialno, + * to store and retrieve data related to each logical bitstream. + * + * \subsection boseos b_o_s and e_o_s +
+   bos page: The initial page (beginning of stream) of a logical
+      bitstream which contains information to identify the codec type
+      and other decoding-relevant information.
+
+   eos page: The final page (end of stream) of a logical bitstream.
+
+ * + * - Every ogg_packet contains \a b_o_s and \a e_o_s flags. + * Of course each of these will be set only once per logical bitstream. + * See the Structuring section below for rules on setting \a b_o_s and + * \a e_o_s when interleaving logical bitstreams. + * - This documentation will refer to \a bos and \a eos packets + * (not pages) as that is more closely represented by the API. + * The \a bos packet is the only packet on the \a bos page, and the + * \a eos packet is the last packet on the \a eos page. + * + * \subsection granulepos +
+   granule position: An increasing position number for a specific
+      logical bitstream stored in the page header.  Its meaning is
+      dependent on the codec for that logical bitstream
+
+ * + * - Every ogg_packet contains a \a granulepos. The \a granulepos + * of each packet is used mostly for \link seek_api seeking. \endlink + * + * \section Structuring + * + * The general structure of an Ogg stream is governed by various rules. + * + * \subsection secondaries Secondary header packets + * + * Some data sources require initial setup information such as comments + * and codebooks to be present near the beginning of the stream (directly + * following the b_o_s packets. + * +
+   Ogg also allows but does not require secondary header packets after
+   the bos page for logical bitstreams and these must also precede any
+   data packets in any logical bitstream.  These subsequent header
+   packets are framed into an integral number of pages, which will not
+   contain any data packets.  So, a physical bitstream begins with the
+   bos pages of all logical bitstreams containing one initial header
+   packet per page, followed by the subsidiary header packets of all
+   streams, followed by pages containing data packets.
+
+ * + * - liboggz handles the framing of \a packets into low-level \a pages. To + * ensure that the pages used by secondary headers contain no data packets, + * set the \a flush parameter of oggz_write_feed() to \a OGGZ_FLUSH_AFTER + * when queueing the last of the secondary headers. + * - or, equivalently, set \a flush to \a OGGZ_FLUSH_BEFORE when queueing + * the first of the data packets. + * + * \subsection boseosseq Sequencing b_o_s and e_o_s packets + * + * The following rules apply for sequencing \a bos and \a eos packets in + * a physical bitstream: +
+   ... All bos pages of all logical bitstreams MUST appear together at
+   the beginning of the Ogg bitstream.
+
+   ... eos pages for the logical bitstreams need not all occur
+   contiguously.  Eos pages may be 'nil' pages, that is, pages
+   containing no content but simply a page header with position
+   information and the eos flag set in the page header.
+
+ * + * - oggz_write_feed() will fail with a return value of + * \a OGGZ_ERR_BOS if an attempt is made to queue a late \a bos packet + * + * \subsection interleaving Interleaving logical bitstreams +
+   It is possible to consecutively chain groups of concurrently
+   multiplexed bitstreams.  The groups, when unchained, MUST stand on
+   their own as a valid concurrently multiplexed bitstream.  The
+   following diagram shows a schematic example of such a physical
+   bitstream that obeys all the rules of both grouped and chained
+   multiplexed bitstreams.
+   
+               physical bitstream with pages of 
+          different logical bitstreams grouped and chained
+      -------------------------------------------------------------
+      |*A*|*B*|*C*|A|A|C|B|A|B|#A#|C|...|B|C|#B#|#C#|*D*|D|...|#D#|
+      -------------------------------------------------------------
+       bos bos bos             eos           eos eos bos       eos
+   
+   In this example, there are two chained physical bitstreams, the first
+   of which is a grouped stream of three logical bitstreams A, B, and C.
+   The second physical bitstream is chained after the end of the grouped
+   bitstream, which ends after the last eos page of all its grouped
+   logical bitstreams.  As can be seen, grouped bitstreams begin
+   together - all of the bos pages MUST appear before any data pages.
+   It can also be seen that pages of concurrently multiplexed bitstreams
+   need not conform to a regular order.  And it can be seen that a
+   grouped bitstream can end long before the other bitstreams in the
+   group end.
+
+ * + * - oggz_write_feed() will fail, returning an explicit error value, if + * an attempt is made to queue a packet in violation of these rules. + * + * \section References + * + * This introduction to the Ogg format is derived from + * IETF RFC 3533 + * The Ogg File Format version 0 in accordance with the + * following copyright statement pertaining to the text of RFC 3533: + * +
+   Copyright (C) The Internet Society (2003).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
* */ @@ -118,7 +314,7 @@ * * Configuring with \a --disable-write will remove all support for writing: * - All internal write related functions will not be built - * - Any attempt to call oggz_new(), oggz_open() or oggz_openfd() + * - Any attempt to call oggz_new(), oggz_open() or oggz_open_stdio() * with \a flags == OGGZ_WRITE will fail, returning NULL * - Any attempt to call oggz_write(), oggz_write_output(), oggz_write_feed(), * oggz_write_set_hungry_callback(), or oggz_write_get_next_page_size() @@ -128,7 +324,7 @@ * * Configuring with \a --disable-read will remove all support for reading: * - All internal reading related functions will not be built - * - Any attempt to call oggz_new(), oggz_open() or oggz_openfd() + * - Any attempt to call oggz_new(), oggz_open() or oggz_open_stdio() * with \a flags == OGGZ_READ will fail, returning NULL * - Any attempt to call oggz_read(), oggz_read_input(), * oggz_set_read_callback(), oggz_seek(), or oggz_seek_units() will return @@ -186,7 +382,7 @@ * in one of three ways: * * - oggz_open() - Open a full pathname - * - oggz_openfd() - Use an already opened file descriptor + * - oggz_open_stdio() - Use an already opened FILE * - oggz_new() - Create an anonymous OGGZ object, which you can later * handle via memory buffers * @@ -206,47 +402,24 @@ * callback with oggz_set_write_callback(). * See the \ref write_api section for details. * + * \section seeking Seeking on Ogg data + * + * To seek while reading Ogg files or streams you must instantiate an OGGZ + * handle for reading, and ensure that an \link metric OggzMetric \endlink + * function is defined to translate packet positions into units such as time. + * See the \ref seek_api section for details. + * * \section headers Headers * * oggz.h provides direct access to libogg types such as ogg_packet, defined * in . */ -/** \defgroup seeking_group Seeking - * \section seeking Seeking - * - * The seeking semantics of the Ogg file format were outlined by Monty in - * a - * post to theora-dev in September 2002. Quoting from that post, we - * have the following assumptions: - * - * - Ogg is not a non-linear format. ... It is a media transport format - * designed to do nothing more than deliver content, in a stream, and - * have all the pieces arrive on time and in sync. - * - The Ogg layer does not know the specifics of the codec data it's - * multiplexing into a stream. It knows nothing beyond 'Oooo, packets!', - * that the packets belong to different buckets, that the packets go in - * order, and that packets have position markers. Ogg does not even have - * a concept of 'time'; it only knows about the sequentially increasing, - * unitless position markers. It is up to higher layers which have - * access to the codec APIs to assign and convert units of framing or - * time. - * - * liboggz provides two abstractions for seeking at an arbitrary level of - * precision, as well as allowing seeking to a direct byte offset. - * - * To seek across non-metric spaces for which a partial order - * exists (ie. data that is not synchronised by a measure such as time, but - * is nevertheless somehow seekably structured), use an OggzOrder. - * - * For most purposes, such as media data, use an OggzMetric instead. - */ - /** * An opaque handle to an Ogg file. This is returned by oggz_open() or * oggz_new(), and is passed to all other oggz_* functions. */ -typedef void * OGGZ; +typedef void OGGZ; /** * Create a new OGGZ object @@ -266,16 +439,16 @@ OGGZ * oggz_new (int flags); OGGZ * oggz_open (char * filename, int flags); /** - * Create an OGGZ handle associated with a file descriptor. - * \param fd An open file descriptor + * Create an OGGZ handle associated with a stdio stream + * \param file An open FILE handle * \param flags OGGZ_READ or OGGZ_WRITE * \returns A new OGGZ handle * \retval NULL System error; check errno for details */ -OGGZ * oggz_openfd (int fd, int flags); +OGGZ * oggz_open_stdio (FILE * file, int flags); /** - * Ensure any associated file descriptors are flushed. + * Ensure any associated stdio streams are flushed. * \param oggz An OGGZ handle * \retval 0 Success * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ @@ -319,10 +492,21 @@ int oggz_get_eos (OGGZ * oggz, long serialno); /** \defgroup read_api OGGZ Read API * + * OGGZ parses Ogg bitstreams, forming ogg_packet structures, and calling + * your OggzReadPacket callback(s). + * + * You provide Ogg data to OGGZ with oggz_read() or oggz_read_input(), and + * independently process it in OggzReadPacket callbacks. + * It is possible to set a different callback per \a serialno (ie. for each + * logical bitstream in the Ogg bitstream - see the \ref basics section for + * more detail). + * + * See \ref seek_api for information on seeking on interleaved Ogg data. + * * \{ */ -/** +/** * This is the signature of a callback which you must provide for Oggz * to call whenever it finds a new packet in the Ogg stream associated * with \a oggz. @@ -392,7 +576,74 @@ long oggz_read_input (OGGZ * oggz, unsigned char * buf, long n); /** \} */ +/** \defgroup force_feed Writing by force feeding OGGZ + * + * Force feeding involves synchronously: + * - Creating an \a ogg_packet structure + * - Adding it to the packet queue with oggz_write_feed() + * - Calling oggz_write() or oggz_write_output(), repeatedly as necessary, + * to generate the Ogg bitstream. + * + * This process is illustrated in the following diagram: + * + * \image html forcefeed.png + * \image latex forcefeed.eps "Force Feeding OGGZ" width=10cm + * + * The following example code generates a stream of ten packets, each + * containing a single byte ('A', 'B', ... , 'J'): + * + * \include write-feed.c + */ + +/** \defgroup hungry Writing with OggzHungry callbacks + * + * You can add packets to the OGGZ packet queue only when it is "hungry" + * by providing an OggzHungry callback. + * + * An OggzHungry callback will: + * - Create an \a ogg_packet structure + * - Add it to the packet queue with oggz_write_feed() + * + * Once you have set such a callback with oggz_write_set_hungry_callback(), + * simply call oggz_write() or oggz_write_output() repeatedly, and OGGZ + * will call your callback to provide packets when it is hungry. + * + * This process is illustrated in the following diagram: + * + * \image html hungry.png + * \image latex hungry.eps "Using OggzHungry" width=10cm + * + * The following example code generates a stream of ten packets, each + * containing a single byte ('A', 'B', ... , 'J'): + * + * \include write-hungry.c + */ + /** \defgroup write_api OGGZ Write API + * + * OGGZ maintains a packet queue, such that you can independently add + * packets to the queue and write an Ogg bitstream. + * There are two complementary methods for adding packets to the + * packet queue. + * + * - by \link force_feed force feeding OGGZ \endlink + * - by using \link hungry OggzHungry \endlink callbacks + * + * As each packet is enqueued, its validity is checked against the framing + * constraints outlined in the \link basics Ogg basics \endlink section. + * If it does not pass these constraints, oggz_write_feed() will fail with + * an appropriate error code. + * + * \note + * - When writing, you can ensure that a packet starts on a new page + * by setting the \a flush parameter of oggz_write_feed() to + * \a OGGZ_FLUSH_BEFORE when enqueuing it. + * Similarly you can ensure that the last page a packet is written into + * won't contain any following packets by setting the \a flush parameter + * of oggz_write_feed() to \a OGGZ_FLUSH_AFTER. + * - The \a OGGZ_FLUSH_BEFORE and \a OGGZ_FLUSH_AFTER flags can be bitwise + * OR'd together to ensure that the packet will not share any pages with + * any other packets, either before or after. * * \{ */ @@ -437,7 +688,7 @@ int oggz_write_set_hungry_callback (OGGZ * oggz, * \param op An ogg_packet with all fields filled in * \param serialno Identify the logical bitstream in \a oggz to add the * packet to - * \param flush Whether to flush this packet to the stream + * \param flush Bitmask of OGGZ_FLUSH_BEFORE, OGGZ_FLUSH_AFTER * \param guard A guard for nocopy, NULL otherwise * \retval 0 Success * \retval OGGZ_ERR_BAD_GUARD \a guard specified has non-zero initialization @@ -515,22 +766,66 @@ long oggz_write_get_next_page_size (OGGZ * oggz); /** \} */ -/** \defgroup metric OggzMetric +/** \defgroup metric Using OggzMetric * - * If every position in an Ogg stream can be described by a metric (eg. time) - * then define this function that returns some arbitrary unit value. - * This is the normal use of OGGZ for media streams. The meaning of units is - * arbitrary, but must be consistent across all logical bitstreams; for - * example a conversion of the time offset of a given packet into nanoseconds - * or a similar stream-specific subdivision may be appropriate. + * An OggzMetric is a helper function for Oggz's seeking mechanism. + * + * If every position in an Ogg stream can be described by a metric such as + * time, then it is possible to define a function that, given a serialno and + * granulepos, returns a measurement in units such as milliseconds. Oggz + * will use this function repeatedly while seeking in order to navigate + * through the Ogg stream. + * + * The meaning of the units is arbitrary, but must be consistent across all + * logical bitstreams. This allows Oggz to seek accurately through Ogg + * bitstreams containing multiple logical bitstreams such as tracks of media. + * + * \section setting How to set metrics + * + * Some common media codecs can be handled automatically by Oggz. For most + * others it is simply a matter of providing a linear multiplication factor + * (such as a sampling rate, if each packet's granulepos represents a + * sample number). For non-linear data streams, you will need to explicitly + * provide your own OggzMetric function. + * + * \subsection auto Automatic Metrics * - * To use OggzMetric: + * Oggz can automatically provide metrics for the common media codecs + * Speex, Vorbis, and Theora, as well as all Annodex streams. You need do + * nothing more than open the file with the OGGZ_AUTO flag set. + * + * - Create an OGGZ handle for reading with \a flags = OGGZ_READ | OGGZ_AUTO + * - Read data, ensuring that you have received all b_o_s pages before + * attempting to seek. + * + * Oggz will silently parse known codec headers and associate metrics + * appropriately; if you attempt to seek before you have received all + * b_o_s pages, Oggz will not have had a chance to parse the codec headers + * and associate metrics. + * It is safe to seek once you have received a packet with \a b_o_s == 0; + * see the \link basics Ogg basics \endlink section for more details. + * + * \note This functionality is provided for convenience. Oggz parses + * these codec headers internally, and so liboggz is \b not linked to + * libspeex, libvorbis, libtheora or libannodex. + * + * \subsection linear Linear Metrics + * + * - Set the \a granule_rate_numerator and \a granule_rate_denominator + * appropriately using oggz_set_metric_linear() + * + * \subsection custom Custom Metrics + * + * For some streams, neither of the above methods are appropriate. * * - Implement an OggzMetric callback * - Set the OggzMetric callback using oggz_set_metric() - * - To seek, use oggz_seek_units(). Oggz will perform a ratio search - * through the Ogg bitstream, using the OggzMetric callback to determine - * its position relative to the desired unit. + * + * \section using Seeking with OggzMetrics + * + * To seek, use oggz_seek_units(). Oggz will perform a ratio search + * through the Ogg bitstream, using the OggzMetric callback to determine + * its position relative to the desired unit. * * \note * @@ -547,7 +842,49 @@ long oggz_write_get_next_page_size (OGGZ * oggz); * This impacts seeking because the portion of the bitstream containing * decode headers should not be considered part of the metric space. To * inform Oggz not to seek earlier than the end of the decode headers, - * use oggz_data_begins_here(). + * use oggz_set_data_start(). + * + */ + +/** \defgroup seek_api OGGZ Seek API + * + * The seeking semantics of the Ogg file format were outlined by Monty in + * a + * post to theora-dev in September 2002. Quoting from that post, we + * have the following assumptions: + * + * - Ogg is not a non-linear format. ... It is a media transport format + * designed to do nothing more than deliver content, in a stream, and + * have all the pieces arrive on time and in sync. + * - The Ogg layer does not know the specifics of the codec data it's + * multiplexing into a stream. It knows nothing beyond 'Oooo, packets!', + * that the packets belong to different buckets, that the packets go in + * order, and that packets have position markers. Ogg does not even have + * a concept of 'time'; it only knows about the sequentially increasing, + * unitless position markers. It is up to higher layers which have + * access to the codec APIs to assign and convert units of framing or + * time. + * + * (For more details on the structure of Ogg streams, see the + * \link basics Ogg Basics \endlink section). + * + * For data such as media, for which it is possible to provide a mapping + * such as 'time', OGGZ can efficiently navigate through an Ogg stream + * by use of an OggzMetric callback, thus allowing automatic seeking to + * points in 'time'. + * For common codecs you can ask Oggz to set this for you automatically by + * instantiating the OGGZ handle with the OGGZ_AUTO flag set. For others + * you can specify a multiplier with oggz_set_metric_linear(), or a generic + * non-linear metric with oggz_set_metric(). + * + * - See the section on \link metric Using OggzMetrics \endlink for details + * of setting up and seeking with metrics. + * + * - It is always possible to seek to exact byte positions using oggz_seek(). + * + * - A mechanism to aid seeking across non-metric spaces for which a partial + * order exists (ie. data that is not synchronised by a measure such as time, + * but is nevertheless somehow seekably structured), is also planned. * * \{ */ @@ -623,9 +960,6 @@ int oggz_set_metric (OGGZ * oggz, long serialno, OggzMetric metric, */ ogg_int64_t oggz_seek_units (OGGZ * oggz, ogg_int64_t units, int whence); -/** \} - */ - #ifdef _UNIMPLEMENTED /** \defgroup order OggzOrder * @@ -677,7 +1011,7 @@ ogg_int64_t oggz_seek_units (OGGZ * oggz, ogg_int64_t units, int whence); * Hacker's hint: if there are no circumstances in which you would return * a value of 2, there is a linear order; it may be possible to define a * Metric rather than an Order. - * \{ + * */ typedef int (*OggzOrder) (OGGZ * oggz, ogg_packet * op, void * target, void * user_data); @@ -691,20 +1025,22 @@ int oggz_set_order (OGGZ * oggz, long serialno, OggzOrder order, long oggz_seek_byorder (OGGZ * oggz, void * target); -/** \} - */ #endif /* _UNIMPLEMENTED */ /** - * Tell OGGZ that we're past the headers, to remember the current packet - * as the start of data. + * Tell OGGZ to remember the given offset as the start of data. * This informs the seeking mechanism that when seeking back to unit 0, - * go to the packet we're on now, not to the start of the file, which - * is usually codec headers. + * go to the given offset, not to the start of the file, which is usually + * codec headers. + * The usual usage is: +
+    oggz_set_data_start (oggz, oggz_tell (oggz));
+
* \param oggz An OGGZ handle previously opened for reading + * \param offset The offset of the start of data * \returns 0 on success, -1 on failure. */ -int oggz_data_begins_here (OGGZ * oggz); +int oggz_set_data_start (OGGZ * oggz, off_t offset); /** * Provide the file offset in bytes corresponding to the data read. @@ -721,7 +1057,7 @@ int oggz_data_begins_here (OGGZ * oggz); off_t oggz_tell (OGGZ * oggz); /** - * Seek to a specific bytes offset + * Seek to a specific byte offset * \param oggz An OGGZ handle * \param offset a byte offset * \param whence As defined in : SEEK_SET, SEEK_CUR or SEEK_END @@ -733,6 +1069,8 @@ off_t oggz_seek (OGGZ * oggz, off_t offset, int whence); long oggz_seek_packets (OGGZ * oggz, long serialno, long packets, int whence); #endif +/** \} + */ /** * Request a new serialno, as required for a new stream, ensuring the serialno diff --git a/include/oggz/oggz_constants.h b/include/oggz/oggz_constants.h index c9c6a0ad9fa01c2ad2c73ac95ebab4379d949970..fcc72c0060f48882ebb794d01ca87e52e04af735 100644 --- a/include/oggz/oggz_constants.h +++ b/include/oggz/oggz_constants.h @@ -39,6 +39,9 @@ /** * Flags to oggz_new(), oggz_open(), and oggz_openfd(). + * Can be or'ed together in the following combinations: + * - OGGZ_READ | OGGZ_AUTO + * - OGGZ_WRITE | OGGZ_NONSTRICT */ enum OggzFlags { /** Read only */ @@ -49,7 +52,15 @@ enum OggzFlags { /** Disable strict adherence to mapping constraints, eg for * handling an incomplete stream */ - OGGZ_NONSTRICT = 0x10 + OGGZ_NONSTRICT = 0x10, + + /** + * Scan for known headers while reading, and automatically set + * metrics appropriately. Opening a file for reading with + * \a flags = OGGZ_READ | OGGZ_AUTO will allow seeking on Speex, + * Vorbis, Theora and all Annodex streams in units of milliseconds, + * once all bos pages have been delivered. */ + OGGZ_AUTO = 0x20 }; /** @@ -88,12 +99,18 @@ enum OggzError { /** Operation is inappropriate for oggz in current eos state */ OGGZ_ERR_EOS = -6, + /** Operation requires a valid metric, but none has been set */ + OGGZ_ERR_BAD_METRIC = -7, + /** System specific error; check errno for details */ OGGZ_ERR_SYSTEM = -10, /** Functionality disabled at build time */ OGGZ_ERR_DISABLED = -11, + /** Seeking operation is not possible for this OGGZ */ + OGGZ_ERR_NOSEEK = -13, + /** The requested serialno does not exist in this OGGZ */ OGGZ_ERR_BAD_SERIALNO = -20, diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am index f4d753872754953716ef420dca840b7da302fff3..3ecf1728c8d90eb006f422aba4d0a8dac1eeb91d 100644 --- a/src/examples/Makefile.am +++ b/src/examples/Makefile.am @@ -17,11 +17,22 @@ if OGGZ_CONFIG_READ oggz_read_programs = read-file endif +if OGGZ_CONFIG_WRITE +oggz_write_programs = write-feed write-hungry +endif + # Programs to build -noinst_PROGRAMS = $(oggz_rw_programs) $(oggz_read_programs) +noinst_PROGRAMS = $(oggz_rw_programs) $(oggz_read_programs) \ + $(oggz_write_programs) identity_SOURCES = identity.c identity_LDADD = $(OGGZ_LIBS) read_file_SOURCES = read-file.c read_file_LDADD = $(OGGZ_LIBS) + +write_feed_SOURCES = write-feed.c +write_feed_LDADD = $(OGGZ_LIBS) + +write_hungry_SOURCES = write-hungry.c +write_hungry_LDADD = $(OGGZ_LIBS) \ No newline at end of file diff --git a/src/examples/identity.c b/src/examples/identity.c index 84e565a8ae42359893132ce0931671fcedc3a1c1..f87e2e1704bb4ac0377c3befafef5bcc98ff7e6c 100644 --- a/src/examples/identity.c +++ b/src/examples/identity.c @@ -34,7 +34,8 @@ #include #include -static int packetno = 0; +#define ID_WRITE_DIRECT + static int flush_next = 0; static int @@ -44,12 +45,20 @@ read_packet (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) int flush; int ret; +#if 0 flush = flush_next; if (op->granulepos == -1) { flush_next = 0; } else { flush_next = OGGZ_FLUSH_BEFORE; } +#else + if (op->granulepos == -1) { + flush = 0; + } else { + flush = OGGZ_FLUSH_AFTER; + } +#endif if ((ret = oggz_write_feed (writer, op, serialno, flush, NULL)) != 0) { printf ("oggz_write_feed: %d\n", ret); @@ -63,6 +72,8 @@ main (int argc, char ** argv) { char * infilename, * outfilename; OGGZ * reader, * writer; + FILE * outfile; + unsigned char buf[1024]; long n; if (argc < 3) { @@ -77,17 +88,34 @@ main (int argc, char ** argv) exit (1); } +#ifdef ID_WRITE_DIRECT if ((writer = oggz_open (outfilename, OGGZ_WRITE)) == NULL) { printf ("unable to open file %s\n", outfilename); exit (1); } +#else + if ((writer = oggz_new (OGGZ_WRITE)) == NULL) { + printf ("Unable to create new writer\n"); + } + outfile = fopen (outfilename, "w"); +#endif oggz_set_read_callback (reader, -1, read_packet, writer); while ((n = oggz_read (reader, 1024)) > 0) { +#ifdef ID_WRITE_DIRECT while (oggz_write (writer, n) > 0); +#else + while (oggz_write_output (writer, buf, n) > 0) { + fwrite (buf, 1, n, outfile); + } +#endif } +#ifndef ID_WRITE_DIRECT + fclose (outfile); +#endif + oggz_close (writer); oggz_close (reader); diff --git a/src/examples/read-file.c b/src/examples/read-file.c index eb68af51b8067ee275dcce33645a9f9714683a58..f5aca395bd0dbd4c60f0ed6f8ee103b1e0ebc3bf 100644 --- a/src/examples/read-file.c +++ b/src/examples/read-file.c @@ -68,7 +68,7 @@ main (int argc, char ** argv) printf ("usage: %s filename\n", argv[0]); } - if ((oggz = oggz_open ((char *)argv[1], OGGZ_READ)) == NULL) { + if ((oggz = oggz_open ((char *)argv[1], OGGZ_READ | OGGZ_AUTO)) == NULL) { printf ("unable to open file %s\n", argv[1]); exit (1); } diff --git a/src/liboggz/Makefile.am b/src/liboggz/Makefile.am index 6872c60ea61d8648e7e0c9b4e04eb4c6e879d365..2696b36d42e14fea7bf4ec4cc04c60dc2760dc36 100644 --- a/src/liboggz/Makefile.am +++ b/src/liboggz/Makefile.am @@ -11,9 +11,10 @@ lib_LTLIBRARIES = liboggz.la liboggz_la_SOURCES = \ oggz.c \ - oggz_private.h \ + oggz_private.h oggz_byteorder.h oggz_macros.h \ oggz_read.c oggz_write.c \ + oggz_auto.c oggz_auto.h \ + oggz_table.c \ oggz_vector.c oggz_vector.h liboggz_la_LDFLAGS = -version-info @SHARED_VERSION_INFO@ @SHLIB_VERSION_ARG@ - diff --git a/src/liboggz/Version_script.in b/src/liboggz/Version_script.in index 6c6f279dfecec327985c10de3a5e3a4169a9b33f..f2feae083658737dea972767049de4701d98a2ea 100644 --- a/src/liboggz/Version_script.in +++ b/src/liboggz/Version_script.in @@ -10,7 +10,7 @@ global: oggz_new; oggz_open; - oggz_openfd; + oggz_open_stdio; oggz_flush; oggz_close; oggz_get_bos; @@ -24,11 +24,19 @@ oggz_write_output; oggz_write_get_next_page_size; oggz_set_metric; + oggz_set_metric_linear; oggz_seek_units; oggz_tell; oggz_seek; - oggz_data_begins_here; + oggz_set_data_start; oggz_serialno_new; + + oggz_table_new; + oggz_table_delete; + oggz_table_insert; + oggz_table_lookup; + oggz_table_size; + oggz_table_nth; local: *; }; diff --git a/src/liboggz/oggz.c b/src/liboggz/oggz.c index bbcb13719c777552c6880952e9dd4c981325e0bc..d822723adb84a13e37935c4c702442bee596bc58 100644 --- a/src/liboggz/oggz.c +++ b/src/liboggz/oggz.c @@ -46,6 +46,7 @@ #include #include "oggz_private.h" +#include "oggz_vector.h" /*#define DEBUG*/ @@ -72,12 +73,12 @@ oggz_new (int flags) if (oggz == NULL) return NULL; oggz->flags = flags; - oggz->fd = -1; + oggz->file = NULL; oggz->offset = 0; oggz->offset_data_begin = 0; - oggz_vector_init (&oggz->streams, sizeof (oggz_stream_t)); + oggz->streams = oggz_vector_new (); oggz->all_at_eos = 0; oggz->metric = NULL; @@ -100,32 +101,32 @@ OGGZ * oggz_open (char * filename, int flags) { OGGZ * oggz = NULL; - int fd = -1; + FILE * file = NULL; if (oggz_flags_disabled (flags)) return NULL; if (flags & OGGZ_WRITE) { - fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + file = fopen (filename, "w"); } else { - fd = open (filename, O_RDONLY); + file = fopen (filename, "r"); } - if (fd == -1) return NULL; + if (file == NULL) return NULL; oggz = oggz_new (flags); - oggz->fd = fd; + oggz->file = file; return oggz; } OGGZ * -oggz_openfd (int fd, int flags) +oggz_open_stdio (FILE * file, int flags) { OGGZ * oggz = NULL; if (oggz_flags_disabled (flags)) return NULL; oggz = oggz_new (flags); - oggz->fd = fd; + oggz->file = file; return oggz; } @@ -135,9 +136,9 @@ oggz_flush (OGGZ * oggz) { if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; - if (oggz->fd == -1) return OGGZ_ERR_INVALID; + if (oggz->file == NULL) return OGGZ_ERR_INVALID; - if (fsync (oggz->fd) == -1) { + if (fflush (oggz->file) == EOF) { return OGGZ_ERR_SYSTEM; } @@ -163,8 +164,8 @@ oggz_close (OGGZ * oggz) { if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; - oggz_vector_foreach (&oggz->streams, oggz_stream_clear); - oggz_vector_clear (&oggz->streams); + oggz_vector_foreach (oggz->streams, oggz_stream_clear); + oggz_vector_delete (oggz->streams); if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) { oggz_write_close (oggz); @@ -172,8 +173,8 @@ oggz_close (OGGZ * oggz) oggz_read_close (oggz); } - if (oggz->fd != -1) { - if (close (oggz->fd) == -1) { + if (oggz->file != NULL) { + if (fclose (oggz->file) == EOF) { return OGGZ_ERR_SYSTEM; } } @@ -206,7 +207,7 @@ oggz_get_stream (OGGZ * oggz, long serialno) { if (serialno < 0) return NULL; - return oggz_vector_find (&oggz->streams, oggz_find_stream, serialno); + return oggz_vector_find (oggz->streams, oggz_find_stream, serialno); } oggz_stream_t * @@ -233,7 +234,7 @@ oggz_add_stream (OGGZ * oggz, long serialno) stream->read_packet = NULL; stream->read_user_data = NULL; - oggz_vector_add_element (&oggz->streams, stream); + oggz_vector_insert_p (oggz->streams, stream); return stream; } @@ -242,13 +243,14 @@ int oggz_get_bos (OGGZ * oggz, long serialno) { oggz_stream_t * stream; - int i; + int i, size; if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; if (serialno == -1) { - for (i = 0; i < oggz->streams.nr_elements; i++) { - stream = (oggz_stream_t *)oggz->streams.data[i]; + size = oggz_vector_size (oggz->streams); + for (i = 0; i < size; i++) { + stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i); #if 1 /* If this stream has delivered a non bos packet, return FALSE */ if (stream->delivered_non_b_o_s) return 0; @@ -271,13 +273,14 @@ int oggz_get_eos (OGGZ * oggz, long serialno) { oggz_stream_t * stream; - int i; + int i, size; if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; if (serialno == -1) { - for (i = 0; i < oggz->streams.nr_elements; i++) { - stream = (oggz_stream_t *)oggz->streams.data[i]; + size = oggz_vector_size (oggz->streams); + for (i = 0; i < size; i++) { + stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i); if (!stream->e_o_s) return 0; } return 1; @@ -294,13 +297,14 @@ int oggz_set_eos (OGGZ * oggz, long serialno) { oggz_stream_t * stream; - int i; + int i, size; if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; if (serialno == -1) { - for (i = 0; i < oggz->streams.nr_elements; i++) { - stream = (oggz_stream_t *)oggz->streams.data[i]; + size = oggz_vector_size (oggz->streams); + for (i = 0; i < size; i++) { + stream = (oggz_stream_t *) oggz_vector_nth_p (oggz->streams, i); stream->e_o_s = 1; } oggz->all_at_eos = 1; @@ -346,7 +350,7 @@ oggz_metric_default_linear (OGGZ * oggz, long serialno, ogg_int64_t granulepos, return (ldata->gr_n * granulepos / ldata->gr_d); } -static int +int oggz_set_metric_internal (OGGZ * oggz, long serialno, OggzMetric metric, void * user_data, int internal) { diff --git a/src/liboggz/oggz_private.h b/src/liboggz/oggz_private.h index ed778abf2cf9744f3253b0fa01f719d43dae4a40..9a5d3effff682f87dded986f884e450b9eecbcec 100644 --- a/src/liboggz/oggz_private.h +++ b/src/liboggz/oggz_private.h @@ -33,24 +33,14 @@ #ifndef __OGGZ_PRIVATE_H__ #define __OGGZ_PRIVATE_H__ +#include + #include #include +#include "oggz_macros.h" #include "oggz_vector.h" -/* Use the malloc and free used by ogg; defaults are those from stdlib */ -#define oggz_malloc _ogg_malloc -#define oggz_free _ogg_free - -#undef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) - -#undef MAX -#define MAX(a,b) ((a)>(b)?(a):(b)) - -#define OGGZ_READ 00 -#define OGGZ_WRITE 01 - typedef struct _OGGZ OGGZ; typedef struct _OggzReader OggzReader; typedef struct _OggzWriter OggzWriter; @@ -124,7 +114,7 @@ enum oggz_writer_state { struct _OggzWriter { oggz_writer_packet_t * next_zpacket; /* stashed in case of FLUSH_BEFORE */ - OggzVector packet_queue; + OggzVector * packet_queue; OggzWriteHungry hungry; void * hungry_user_data; @@ -151,7 +141,7 @@ struct _OggzWriter { struct _OGGZ { int flags; - int fd; + FILE * file; ogg_packet current_packet; ogg_page current_page; @@ -159,7 +149,7 @@ struct _OGGZ { off_t offset; /* offset of current page start */ off_t offset_data_begin; /* offset of unit 0 page start */ - OggzVector streams; + OggzVector * streams; int all_at_eos; /* all streams are at eos */ OggzMetric metric; @@ -187,4 +177,9 @@ oggz_stream_t * oggz_add_stream (OGGZ * oggz, long serialno); int oggz_get_bos (OGGZ * oggz, long serialno); ogg_int64_t oggz_get_unit (OGGZ * oggz, long serialno, ogg_int64_t granulepos); +int oggz_set_metric_internal (OGGZ * oggz, long serialno, OggzMetric metric, + void * user_data, int internal); + +int oggz_auto (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data); + #endif /* __OGGZ_PRIVATE_H__ */ diff --git a/src/liboggz/oggz_read.c b/src/liboggz/oggz_read.c index 80362db02286289e552dc3dbe98ac7f94343c370..475e2678f3824a70c4862bf85e54664f5299fc99 100644 --- a/src/liboggz/oggz_read.c +++ b/src/liboggz/oggz_read.c @@ -61,6 +61,8 @@ #define CHUNKSIZE 8500 +#define oggz_off_t long + OGGZ * oggz_read_init (OGGZ * oggz) { @@ -108,7 +110,12 @@ oggz_set_read_callback (OGGZ * oggz, long serialno, reader->read_user_data = user_data; } else { stream = oggz_get_stream (oggz, serialno); +#if 0 if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO; +#else + if (stream == NULL) + stream = oggz_add_stream (oggz, serialno); +#endif stream->read_packet = read_packet; stream->read_user_data = user_data; @@ -117,28 +124,6 @@ oggz_set_read_callback (OGGZ * oggz, long serialno, return 0; } -#if 1 - -static off_t -oggz_tell_raw_7 (OGGZ * oggz) -{ - off_t offset_at; - - offset_at = lseek (oggz->fd, 0, SEEK_CUR); - if (offset_at == -1) { -#if 0 - if (errno == ESPIPE) { - oggz_set_error (oggz, OGGZ_ENOSEEK); - } else { - oggz_set_error (oggz, OGGZ_ESYSTEM); - } -#endif - return -1; - } - - return offset_at; -} - /* * oggz_get_next_page_7 (oggz, og, do_read) * @@ -149,7 +134,7 @@ oggz_tell_raw_7 (OGGZ * oggz) * returns -1 on error * returns -2 if EOF was encountered */ -static off_t +static oggz_off_t oggz_get_next_page_7 (OGGZ * oggz, ogg_page * og) { OggzReader * reader = &oggz->x.reader; @@ -157,7 +142,7 @@ oggz_get_next_page_7 (OGGZ * oggz, ogg_page * og) char * buffer; #endif long bytes = 0, more; - off_t page_offset = 0, ret; + oggz_off_t page_offset = 0, ret; int found = 0; do { @@ -167,9 +152,11 @@ oggz_get_next_page_7 (OGGZ * oggz, ogg_page * og) page_offset = 0; #if _UMMODIFIED_ buffer = ogg_sync_buffer (&reader->ogg_sync, CHUNKSIZE); - if ((bytes = read (oggz->fd, buffer, CHUNKSIZE)) < 0) { - /*oggz_set_error (oggz, OGGZ_ESYSTEM);*/ - return -1; + if ((bytes = fread (buffer, 1, CHUNKSIZE, oggz->file)) == 0) { + if (ferror (oggz->file)) { + oggz_set_error (oggz, OGGZ_ERR_SYSTEM); + return -1; + } } if (bytes == 0) { @@ -196,7 +183,7 @@ oggz_get_next_page_7 (OGGZ * oggz, ogg_page * og) /* Calculate the byte offset of the page which was found */ if (bytes > 0) { - oggz->offset = oggz_tell_raw_7 (oggz) - bytes + page_offset; + oggz->offset = ftell (oggz->file) - bytes + page_offset; ret = oggz->offset; } else { /* didn't need to do any reading -- accumulate the page_offset */ @@ -207,7 +194,6 @@ oggz_get_next_page_7 (OGGZ * oggz, ogg_page * og) return ret; } - static long oggz_read_sync (OGGZ * oggz) { @@ -216,11 +202,8 @@ oggz_read_sync (OGGZ * oggz) oggz_stream_t * stream; ogg_stream_state * os; - ogg_sync_state * oy; ogg_packet * op; long serialno; - ogg_int64_t granulepos = -1; - int ret; ogg_packet packet; ogg_page og; @@ -263,6 +246,10 @@ oggz_read_sync (OGGZ * oggz) /* got a packet. process it */ granulepos = op->granulepos; + if (!stream->metric && (oggz->flags & OGGZ_AUTO)) { + oggz_auto (oggz, op, serialno, NULL); + } + if ((oggz->metric || stream->metric) && granulepos != -1) { reader->current_unit = oggz_get_unit (oggz, serialno, granulepos); } @@ -328,74 +315,6 @@ oggz_read_sync (OGGZ * oggz) return nread; /* XXX: nread isn't even looked at!!! */ } -#else /* 0 */ - -static long -oggz_read_sync (OGGZ * oggz) -{ - OggzReader * reader = &oggz->x.reader; - - oggz_stream_t * stream; - ogg_stream_state * os; - ogg_sync_state * oy; - ogg_packet * op; - ogg_page * og; - long serialno; - ogg_int64_t granulepos = -1; - int result; - long nread = 0; - - if (oggz == NULL) return -1; - - oy = &reader->ogg_sync; - op = &oggz->current_packet; - og = &oggz->current_page; - - while ((result = ogg_sync_pageout (oy, og)) == 1) { - - serialno = ogg_page_serialno (og); - - stream = oggz_get_stream (oggz, serialno); - - if (stream == NULL) { - /* new stream ... check bos etc. */ - if ((stream = oggz_add_stream (oggz, serialno)) == NULL) { - /* error -- could not add stream */ - break; - } - } - os = &stream->ogg_stream; - - nread += og->body_len; - - if (!ogg_page_continued (og)) { - granulepos = ogg_page_granulepos (og); - } - - ogg_stream_pagein (os, og); - - while (ogg_stream_packetout (os, op) == 1) { - - if (op->e_o_s) stream->e_o_s = 1; - - if ((oggz->metric || stream->metric) && granulepos != -1) { - reader->current_unit = oggz_get_unit (oggz, serialno, granulepos); - } - - if (stream->read_packet) { - stream->read_packet (oggz, op, serialno, stream->read_user_data); - } else if (reader->read_packet) { - reader->read_packet (oggz, op, serialno, reader->read_user_data); - } - } - - oggz->offset += og->header_len + og->body_len; - } - - return nread; -} -#endif /* 1 */ - long oggz_read (OGGZ * oggz, long n) { @@ -416,8 +335,10 @@ oggz_read (OGGZ * oggz, long n) while (bytes_read > 0 && remaining > 0) { bytes = MIN (remaining, 4096); buffer = ogg_sync_buffer (&reader->ogg_sync, bytes); - if ((bytes_read = read (oggz->fd, buffer, bytes)) == -1) { - return OGGZ_ERR_SYSTEM; + if ((bytes_read = fread (buffer, 1, bytes, oggz->file)) == 0) { + if (ferror (oggz->file)) { + return OGGZ_ERR_SYSTEM; + } } ogg_sync_wrote (&reader->ogg_sync, bytes_read); @@ -467,19 +388,13 @@ oggz_read_input (OGGZ * oggz, unsigned char * buf, long n) /* oggz_seek() related functions from here down */ -#if 0 /* - * XXX: how about this instead of data_begins_here ? - * - * Then the typical usage is: + * The typical usage is: * * oggz_set_data_start (oggz, oggz_tell (oggz)); - * - * and more general usage is possible, plus the - * overall usage is more obvious. */ int -oggz_set_data_start (OGGZ * oggz, off_t offset) +oggz_set_data_start (OGGZ * oggz, oggz_off_t offset) { if (oggz == NULL) return -1; @@ -489,36 +404,13 @@ oggz_set_data_start (OGGZ * oggz, off_t offset) return 0; } -#endif - -int -oggz_data_begins_here (OGGZ * oggz) -{ - if (oggz == NULL) return -1; - - if (oggz->offset < 0) return -1; - - oggz->offset_data_begin = oggz->offset; - - return 0; -} -static off_t +static oggz_off_t oggz_tell_raw (OGGZ * oggz) { - off_t offset_at; + oggz_off_t offset_at; - offset_at = lseek (oggz->fd, 0, SEEK_CUR); - if (offset_at == -1) { -#if 0 - if (errno == ESPIPE) { - oggz_set_error (oggz, OGGZ_ENOSEEK); - } else { - oggz_set_error (oggz, OGGZ_ESYSTEM); - } -#endif - return -1; - } + offset_at = ftell (oggz->file); return offset_at; } @@ -526,24 +418,23 @@ oggz_tell_raw (OGGZ * oggz) /* * seeks and syncs */ -static off_t -oggz_seek_raw (OGGZ * oggz, off_t offset, int whence) +static oggz_off_t +oggz_seek_raw (OGGZ * oggz, oggz_off_t offset, int whence) { OggzReader * reader = &oggz->x.reader; - off_t offset_at; + oggz_off_t offset_at; - offset_at = lseek (oggz->fd, offset, whence); - if (offset_at == -1) { -#if 0 + if (fseek (oggz->file, offset, whence) == -1) { if (errno == ESPIPE) { - oggz_set_error (oggz, OGGZ_ENOSEEK); + /*oggz_set_error (oggz, OGGZ_ERR_NOSEEK);*/ } else { - oggz_set_error (oggz, OGGZ_ESYSTEM); + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ } -#endif return -1; } + offset_at = ftell (oggz->file); + oggz->offset = offset_at; ogg_sync_reset (&reader->ogg_sync); @@ -564,11 +455,11 @@ oggz_stream_reset (void * data) } static long -oggz_reset (OGGZ * oggz, off_t offset, ogg_int64_t unit, int whence) +oggz_reset (OGGZ * oggz, oggz_off_t offset, ogg_int64_t unit, int whence) { OggzReader * reader = &oggz->x.reader; - off_t offset_at; + oggz_off_t offset_at; offset_at = oggz_seek_raw (oggz, offset, whence); if (offset_at == -1) return -1; @@ -579,7 +470,7 @@ oggz_reset (OGGZ * oggz, off_t offset, ogg_int64_t unit, int whence) printf ("reset to %ld\n", offset_at); #endif - oggz_vector_foreach (&oggz->streams, oggz_stream_reset); + oggz_vector_foreach (oggz->streams, oggz_stream_reset); #if 0 ogg_stream_reset (&oggz->ogg_stream); @@ -599,13 +490,13 @@ oggz_reset (OGGZ * oggz, off_t offset, ogg_int64_t unit, int whence) * returns -1 on error * returns -2 if EOF was encountered */ -static off_t +static oggz_off_t oggz_get_next_page (OGGZ * oggz, ogg_page * og) { OggzReader * reader = &oggz->x.reader; char * buffer; long bytes = 0, more; - off_t page_offset = 0, ret; + oggz_off_t page_offset = 0, ret; int found = 0; do { @@ -615,12 +506,15 @@ oggz_get_next_page (OGGZ * oggz, ogg_page * og) page_offset = 0; buffer = ogg_sync_buffer (&reader->ogg_sync, CHUNKSIZE); - if ((bytes = read (oggz->fd, buffer, CHUNKSIZE)) < 0) { - /*oggz_set_error (oggz, OGGZ_ESYSTEM);*/ - return -1; + if ((bytes = fread (buffer, 1, CHUNKSIZE, oggz->file)) == 0) { + if (ferror (oggz->file)) { + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ + return -1; + } } - if (bytes == 0) { + if (bytes == 0 || feof (oggz->file)) { + clearerr (oggz->file); return -2; } @@ -653,10 +547,10 @@ oggz_get_next_page (OGGZ * oggz, ogg_page * og) return ret; } -static off_t +static oggz_off_t oggz_get_next_start_page (OGGZ * oggz, ogg_page * og) { - off_t page_offset; + oggz_off_t page_offset; int found = 0; while (!found) { @@ -675,12 +569,12 @@ oggz_get_next_start_page (OGGZ * oggz, ogg_page * og) return page_offset; } -static off_t +static oggz_off_t oggz_get_prev_start_page (OGGZ * oggz, ogg_page * og, ogg_int64_t * granule, long * serialno) { - off_t offset_at, offset_start; - off_t page_offset, prev_offset = 0; + oggz_off_t offset_at, offset_start; + oggz_off_t page_offset, prev_offset = 0; long granule_at = -1; #if 0 @@ -750,12 +644,12 @@ oggz_get_prev_start_page (OGGZ * oggz, ogg_page * og, return -1; } -static off_t +static oggz_off_t oggz_scan_for_page (OGGZ * oggz, ogg_page * og, ogg_int64_t unit_target, - off_t offset_begin, off_t offset_end) + oggz_off_t offset_begin, oggz_off_t offset_end) { - off_t offset_at, offset_next; - off_t offset_prev = -1; + oggz_off_t offset_at, offset_next; + oggz_off_t offset_prev = -1; ogg_int64_t granule_at; ogg_int64_t unit_at; long serialno; @@ -852,9 +746,10 @@ static long oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target) { OggzReader * reader = &oggz->x.reader; + int fd; struct stat statbuf; - off_t offset_orig, offset_at, offset_guess; - off_t offset_begin, offset_end, offset_next; + oggz_off_t offset_orig, offset_at, offset_guess; + oggz_off_t offset_begin, offset_end, offset_next; ogg_int64_t granule_at; ogg_int64_t unit_at, unit_begin = 0, unit_end = -1; long serialno; @@ -870,20 +765,25 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target) return -1; } - if (oggz->fd == -1) { - /*oggz_set_error (oggz, OGGZ_ENOSEEK);*/ + if (oggz->file == NULL) { + /*oggz_set_error (oggz, OGGZ_ERR_NOSEEK);*/ + return -1; + } + + if ((fd = fileno (oggz->file)) == -1) { + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ return -1; } - if (fstat (oggz->fd, &statbuf) == -1) { - /*oggz_set_error (oggz, OGGZ_ESYSTEM);*/ + if (fstat (fd, &statbuf) == -1) { + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ return -1; } if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) { offset_end = statbuf.st_size; } else { - /*oggz_set_error (oggz, OGGZ_ENOSEEK);*/ + /*oggz_set_error (oggz, OGGZ_ERR_NOSEEK);*/ return -1; } @@ -941,7 +841,7 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target) #endif offset_guess = offset_begin + - (off_t)((offset_at - offset_begin) * guess_ratio); + (oggz_off_t)((offset_at - offset_begin) * guess_ratio); } } else if (unit_end <= unit_begin) { #ifdef DEBUG @@ -955,7 +855,7 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target) (double)(unit_end - unit_begin); offset_guess = offset_begin + - (off_t)((offset_end - offset_begin) * guess_ratio); + (oggz_off_t)((offset_end - offset_begin) * guess_ratio); /* if (offset_guess <= offset_begin) { @@ -1050,7 +950,7 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target) static long oggz_seek_end (OGGZ * oggz, ogg_int64_t unit_offset) { - off_t offset_orig, offset_at, offset_end; + oggz_off_t offset_orig, offset_at, offset_end; ogg_int64_t granulepos; ogg_int64_t unit_end; long serialno; @@ -1081,7 +981,7 @@ oggz_seek_end (OGGZ * oggz, ogg_int64_t unit_offset) } off_t -oggz_seek (OGGZ * oggz, off_t offset, int whence) +oggz_seek (OGGZ * oggz, oggz_off_t offset, int whence) { ogg_int64_t units = -1; @@ -1093,7 +993,7 @@ oggz_seek (OGGZ * oggz, off_t offset, int whence) if (offset == 0) units = 0; - return oggz_reset (oggz, offset, units, whence); + return (off_t)oggz_reset (oggz, offset, units, whence); } long @@ -1166,7 +1066,7 @@ oggz_read_input (OGGZ * oggz, unsigned char * buf, long n) } off_t -oggz_seek (OGGZ * oggz, off_t offset, int whence) +oggz_seek (OGGZ * oggz, oggz_off_t offset, int whence) { return OGGZ_ERR_DISABLED; } diff --git a/src/liboggz/oggz_vector.c b/src/liboggz/oggz_vector.c index ffdb1aac2e23c00c003bd9fb1288cb5d8b78c2e4..6ffc0d434653df20edd382fc14076929f0e0f9d5 100644 --- a/src/liboggz/oggz_vector.c +++ b/src/liboggz/oggz_vector.c @@ -34,10 +34,31 @@ #include #include -#include "oggz_private.h" +#include "oggz_macros.h" + +typedef int (*OggzFunc) (void * data); +typedef int (*OggzFindFunc) (void * data, long serialno); +typedef int (*OggzCmpFunc) (void * a, void * b, void * user_data); + +typedef struct _OggzVector OggzVector; + +typedef union { + void * p; + long l; +} oggz_data_t; + +struct _OggzVector { + int max_elements; + int nr_elements; + oggz_data_t * data; + OggzCmpFunc compare; + void * compare_user_data; +}; /* - * An optionally sorted vector + * A vector of void * or long; iff it's a vector of void * objects, it + * can be optionally sorted. (The sorting is used to implement the + * packet queue; the vector of longs is used to implement OggzTable) * * if you set a comparison function (oggz_vector_set_cmp()), the vector * will be sorted and new elements will be inserted in sorted order. @@ -45,22 +66,26 @@ * if you don't set a comparison function, new elements will be appended * at the tail * - * to unset the comparison function, call oggz_vector_set_cmp(NULL,NULL) + * to unset the comparison function, call oggz_vector_set_cmp (NULL,NULL) */ OggzVector * -oggz_vector_init (OggzVector * vector, size_t sizeof_element) +oggz_vector_new (void) { + OggzVector * vector; + + vector = oggz_malloc (sizeof (OggzVector)); + vector->max_elements = 0; vector->nr_elements = 0; - vector->sizeof_element = sizeof_element; vector->data = NULL; vector->compare = NULL; + vector->compare_user_data = NULL; return vector; } -void +static void oggz_vector_clear (OggzVector * vector) { oggz_free (vector->data); @@ -69,6 +94,41 @@ oggz_vector_clear (OggzVector * vector) vector->max_elements = 0; } +void +oggz_vector_delete (OggzVector * vector) +{ + oggz_vector_clear (vector); + oggz_free (vector); +} + +int +oggz_vector_size (OggzVector * vector) +{ + if (vector == NULL) return 0; + + return vector->nr_elements; +} + +void * +oggz_vector_nth_p (OggzVector * vector, int n) +{ + if (vector == NULL) return NULL; + + if (n >= vector->nr_elements) return NULL; + + return vector->data[n].p; +} + +long +oggz_vector_nth_l (OggzVector * vector, int n) +{ + if (vector == NULL) return -1L; + + if (n >= vector->nr_elements) return -1L; + + return vector->data[n].l; +} + void * oggz_vector_find (OggzVector * vector, OggzFindFunc func, long serialno) { @@ -76,7 +136,7 @@ oggz_vector_find (OggzVector * vector, OggzFindFunc func, long serialno) int i; for (i = 0; i < vector->nr_elements; i++) { - data = vector->data[i]; + data = vector->data[i].p; if (func (data, serialno)) return data; } @@ -90,24 +150,24 @@ oggz_vector_foreach (OggzVector * vector, OggzFunc func) int i; for (i = 0; i < vector->nr_elements; i++) { - func (vector->data[i]); + func (vector->data[i].p); } return 0; } static void -_array_swap (void *v[], int i, int j) +_array_swap (oggz_data_t v[], int i, int j) { void * t; - t = v[i]; - v[i] = v[j]; - v[j] = t; + t = v[i].p; + v[i].p = v[j].p; + v[j].p = t; } /** - * Helper function for oggz_vector_element_add(). Sorts the vector by + * Helper function for oggz_vector_insert (). Sorts the vector by * insertion sort, assuming the tail element has just been added and the * rest of the vector is sorted. * \param vector An OggzVector @@ -122,7 +182,7 @@ oggz_vector_tail_insertion_sort (OggzVector * vector) if (vector->compare == NULL) return; for (i = vector->nr_elements-1; i > 0; i--) { - if (vector->compare (vector->data[i-1], vector->data[i], + if (vector->compare (vector->data[i-1].p, vector->data[i].p, vector->compare_user_data) > 0) { _array_swap (vector->data, i, i-1); } else { @@ -133,8 +193,8 @@ oggz_vector_tail_insertion_sort (OggzVector * vector) return; } -void * -oggz_vector_add_element (OggzVector * vector, void * data) +static OggzVector * +oggz_vector_grow (OggzVector * vector) { void * new_elements; int new_max_elements; @@ -149,7 +209,7 @@ oggz_vector_add_element (OggzVector * vector, void * data) } new_elements = - realloc (vector->data, (size_t)new_max_elements * sizeof (void *)); + realloc (vector->data, (size_t)new_max_elements * sizeof (oggz_data_t)); if (new_elements == NULL) { vector->nr_elements--; @@ -160,25 +220,46 @@ oggz_vector_add_element (OggzVector * vector, void * data) vector->data = new_elements; } - vector->data[vector->nr_elements-1] = data; + return vector; +} + +void * +oggz_vector_insert_p (OggzVector * vector, void * data) +{ + if (oggz_vector_grow (vector) == NULL) + return NULL; + + vector->data[vector->nr_elements-1].p = data; oggz_vector_tail_insertion_sort (vector); return data; + +} + +long +oggz_vector_insert_l (OggzVector * vector, long ldata) +{ + if (oggz_vector_grow (vector) == NULL) + return -1; + + vector->data[vector->nr_elements-1].l = ldata; + + return ldata; } static void oggz_vector_qsort (OggzVector * vector, int left, int right) { int i, last; - void ** v = vector->data; + oggz_data_t * v = vector->data; if (left >= right) return; _array_swap (v, left, (left + right)/2); last = left; for (i = left+1; i <= right; i++) { - if (vector->compare (v[i], v[left], vector->compare_user_data) < 0) + if (vector->compare (v[i].p, v[left].p, vector->compare_user_data) < 0) _array_swap (v, ++last, i); } _array_swap (v, left, last); @@ -200,17 +281,83 @@ oggz_vector_set_cmp (OggzVector * vector, OggzCmpFunc compare, return 0; } + +static void * +oggz_vector_remove_nth (OggzVector * vector, int n) +{ + int i; + oggz_data_t * new_elements; + int new_max_elements; + + vector->nr_elements--; + + if (vector->nr_elements == 0) { + oggz_vector_clear (vector); + } else { + for (i = n; i < vector->nr_elements; i++) { + vector->data[i] = vector->data[i+1]; + } + + if (vector->nr_elements < vector->max_elements/2) { + new_max_elements = vector->max_elements/2; + + new_elements = + realloc (vector->data, + (size_t)new_max_elements * sizeof (oggz_data_t)); + + if (new_elements == NULL) + return NULL; + + vector->max_elements = new_max_elements; + vector->data = new_elements; + } + } + + return vector; +} + +OggzVector * +oggz_vector_remove_p (OggzVector * vector, void * data) +{ + int i; + + for (i = 0; i < vector->nr_elements; i++) { + if (vector->data[i].p == data) { + return oggz_vector_remove_nth (vector, i); + } + } + + return vector; +} + +OggzVector * +oggz_vector_remove_l (OggzVector * vector, long ldata) +{ + int i; + + for (i = 0; i < vector->nr_elements; i++) { + if (vector->data[i].l == ldata) { + return oggz_vector_remove_nth (vector, i); + } + } + + return vector; +} + void * oggz_vector_pop (OggzVector * vector) { void * data; +#if 0 void * new_elements; int new_max_elements; +#endif if (!vector || vector->data == NULL) return NULL; - data = vector->data[0]; + data = vector->data[0].p; +#if 0 vector->nr_elements--; if (vector->nr_elements == 0) { @@ -223,7 +370,7 @@ oggz_vector_pop (OggzVector * vector) { int i; for (i = 0; i < vector->nr_elements; i++) { - vector->data[i] = vector->data[i+1]; + vector->data[i].p = vector->data[i+1].p; } } #endif @@ -231,7 +378,8 @@ oggz_vector_pop (OggzVector * vector) new_max_elements = vector->max_elements/2; new_elements = - realloc (vector->data, (size_t)new_max_elements * sizeof (void *)); + realloc (vector->data, + (size_t)new_max_elements * sizeof (oggz_data_t)); if (new_elements != NULL) { vector->max_elements = new_max_elements; @@ -240,6 +388,11 @@ oggz_vector_pop (OggzVector * vector) } } +#else + + oggz_vector_remove_nth (vector, 0); + +#endif return data; diff --git a/src/liboggz/oggz_vector.h b/src/liboggz/oggz_vector.h index 8f5af8d45ef69b553b45bf04eb6597251b7f2b86..4583f4ca1e30e91ba847aa4e3aa6a37f850930e7 100644 --- a/src/liboggz/oggz_vector.h +++ b/src/liboggz/oggz_vector.h @@ -33,33 +33,33 @@ #ifndef __OGGZ_VECTOR_H__ #define __OGGZ_VECTOR_H__ -typedef struct _OggzVector OggzVector; +typedef void OggzVector; typedef int (*OggzFunc) (void * data); typedef int (*OggzFindFunc) (void * data, long serialno); typedef int (*OggzCmpFunc) (void * a, void * b, void * user_data); -struct _OggzVector { - int max_elements; - int nr_elements; - void ** data; - size_t sizeof_element; - OggzCmpFunc compare; - void * compare_user_data; -}; - OggzVector * -oggz_vector_init (OggzVector * vector, size_t sizeof_element); +oggz_vector_new (void); void -oggz_vector_clear (OggzVector * vector); +oggz_vector_delete (OggzVector * vector); void * oggz_vector_find (OggzVector * vector, OggzFindFunc func, long serialno); +void * +oggz_vector_nth_p (OggzVector * vector, int n); + +long +oggz_vector_nth_l (OggzVector * vector, int n); + int oggz_vector_foreach (OggzVector * vector, OggzFunc func); +int +oggz_vector_size (OggzVector * vector); + /** * Add an element to a vector. If the vector has a comparison function, * the new element is inserted in sorted order, otherwise it is appended @@ -70,7 +70,26 @@ oggz_vector_foreach (OggzVector * vector, OggzFunc func); * \retval NULL If adding the element failed due to a realloc() error */ void * -oggz_vector_add_element (OggzVector * vector, void * data); +oggz_vector_insert_p (OggzVector * vector, void * data); + +long +oggz_vector_insert_l (OggzVector * vector, long ldata); + +/** + * Remove a (void *) element of a vector + * \retval \a vector on success + * \retval NULL on failure (realloc error) + */ +OggzVector * +oggz_vector_remove_p (OggzVector * vector, void * data); + +/** + * Remove a (long) element of a vector + * \retval \a vector on success + * \retval NULL on failure (realloc error) + */ +OggzVector * +oggz_vector_remove_l (OggzVector * vector, long ldata); int oggz_vector_set_cmp (OggzVector * vector, OggzCmpFunc compare, diff --git a/src/liboggz/oggz_write.c b/src/liboggz/oggz_write.c index 819c3c3525b8c1aa9dee47bcf5c4879c408be298..e8dc6962f3710936e3a969eec412d848a34a49fb 100644 --- a/src/liboggz/oggz_write.c +++ b/src/liboggz/oggz_write.c @@ -48,10 +48,11 @@ #include #include "oggz_private.h" +#include "oggz_vector.h" /* #define DEBUG */ -/*#define ALWAYS_FLUSH*/ +/* #define ALWAYS_FLUSH */ static int oggz_zpacket_cmp (oggz_writer_packet_t * a, oggz_writer_packet_t * b, @@ -78,11 +79,11 @@ oggz_write_init (OGGZ * oggz) writer->next_zpacket = NULL; - oggz_vector_init (&writer->packet_queue, sizeof (oggz_writer_packet_t)); + writer->packet_queue = oggz_vector_new (); #if 0 /* XXX: comparison function should only kick in when a metric is set */ - oggz_vector_set_cmp (&writer->packet_queue, + oggz_vector_set_cmp (writer->packet_queue, (OggzCmpFunc)oggz_zpacket_cmp, oggz); #endif @@ -130,9 +131,9 @@ oggz_write_close (OGGZ * oggz) { OggzWriter * writer = &oggz->x.writer; - oggz_vector_foreach (&writer->packet_queue, + oggz_vector_foreach (writer->packet_queue, (OggzFunc)oggz_writer_packet_free); - oggz_vector_clear (&writer->packet_queue); + oggz_vector_delete (writer->packet_queue); return oggz; } @@ -271,7 +272,7 @@ oggz_write_feed (OGGZ * oggz, ogg_packet * op, long serialno, int flush, new_op->b_o_s, new_op->e_o_s, new_op->bytes, packet->flush); #endif - if (oggz_vector_add_element (&writer->packet_queue, packet) == NULL) { + if (oggz_vector_insert_p (writer->packet_queue, packet) == NULL) { oggz_free (packet); if (!guard) oggz_free (new_buf); return -1; @@ -433,13 +434,28 @@ oggz_page_writeout (OGGZ * oggz, long n) long h, b, nwritten; ogg_page * og; +#ifdef OGGZ_WRITE_DIRECT + int fd; +#endif + if (oggz == NULL) return -1L; og = &oggz->current_page; +#ifdef OGGZ_WRITE_DIRECT + fd = fileno (oggz->file); +#endif + h = MIN (n, og->header_len - writer->page_offset); if (h > 0) { - nwritten = write (oggz->fd, og->header + writer->page_offset, h); +#ifdef OGGZ_WRITE_DIRECT + nwritten = write (fd, og->header + writer->page_offset, h); +#else + nwritten = fwrite (og->header + writer->page_offset, 1, h, oggz->file); +#endif + if (nwritten < h) { + printf ("oggz_page_writeout: %ld < %ld\n", nwritten, h); + } writer->page_offset += h; n -= h; } else { @@ -448,8 +464,16 @@ oggz_page_writeout (OGGZ * oggz, long n) b = MIN (n, og->header_len + og->body_len - writer->page_offset); if (b > 0) { - nwritten = write (oggz->fd, +#ifdef OGGZ_WRITE_DIRECT + nwritten = write (fd, og->body + (writer->page_offset - og->header_len), b); +#else + nwritten = fwrite (og->body + (writer->page_offset - og->header_len), + 1, b, oggz->file); +#endif + if (nwritten < b) { + printf ("oggz_page_writeout: %ld < %ld\n", nwritten, b); + } writer->page_offset += b; n -= b; } else { @@ -469,12 +493,12 @@ oggz_dequeue_packet (OGGZ * oggz) next_zpacket = writer->next_zpacket; writer->next_zpacket = NULL; } else { - next_zpacket = oggz_vector_pop (&writer->packet_queue); + next_zpacket = oggz_vector_pop (writer->packet_queue); if (next_zpacket == NULL) { if (writer->hungry) { writer->hungry (oggz, 1, writer->hungry_user_data); - next_zpacket = oggz_vector_pop (&writer->packet_queue); + next_zpacket = oggz_vector_pop (writer->packet_queue); } } } @@ -502,7 +526,7 @@ oggz_writer_make_packet (OGGZ * oggz) * it to them, marking emptiness appropriately */ if (writer->hungry && !writer->hungry_only_when_empty) { - int empty = (writer->packet_queue.nr_elements == 0); + int empty = (oggz_vector_size (writer->packet_queue) == 0); cb_ret = writer->hungry (oggz, empty, writer->hungry_user_data); } diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 53240d83ec9849a4c2c64260c8ece16a692e21d2..cd8a2c81545ece6df65698b9706a5ae92aa04913 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -2,6 +2,10 @@ AM_CFLAGS = -Wall -pedantic -g +EXTRA_DIST = oggzdiff + +bin_SCRIPTS = oggzdiff + INCLUDES = -I$(top_srcdir)/include OGGZDIR = ../liboggz diff --git a/src/tools/oggzdump.c b/src/tools/oggzdump.c index 89ac7937f9501ac3645f1bf4a2b56e2a2d29c690..f8f05c1f363e162f60bfdb31217d89735800b316 100644 --- a/src/tools/oggzdump.c +++ b/src/tools/oggzdump.c @@ -34,15 +34,54 @@ #include #include +#include #include #include +#include +#include #include +#define DEBUG + #undef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) -static void hex_dump (unsigned char * buf, long n) { +static char * progname; +static FILE * outfile = NULL; +static int dump_bits = 0; +static int dump_char = 1; +static int dump_all_serialnos = 1; +static int truth = 1; + +static int hide_offset = 0; +static int hide_serialno = 0; +static int hide_granulepos = 0; +static int hide_packetno = 0; + +static void +usage (char * progname) +{ + printf ("Usage: %s [options] filename\n", progname); +} + +static void +dump_char_line (unsigned char * buf, long n) +{ + int i; + + fprintf (outfile, " "); + + for (i = 0; i < n; i++) { + if (isgraph(buf[i])) fprintf (outfile, "%c", buf[i]); + else if (isspace(buf[i])) fprintf (outfile, " "); + else fprintf (outfile, "."); + } +} + +static void +hex_dump (unsigned char * buf, long n) +{ int i; long remaining = n, count = 0; long rowlen; @@ -51,31 +90,26 @@ static void hex_dump (unsigned char * buf, long n) { rowlen = MIN (remaining, 16); if (n > 0xffffff) - printf ("%08lx:", count); + fprintf (outfile, "%08lx:", count); else if (n > 0xffff) - printf (" %06lx:", count); + fprintf (outfile, " %06lx:", count); else - printf (" %04lx:", count); + fprintf (outfile, " %04lx:", count); for (i = 0; i < rowlen; i++) { - if (!(i%2)) printf (" "); - printf ("%02x", buf[i]); + if (!(i%2)) fprintf (outfile, " "); + fprintf (outfile, "%02x", buf[i]); } for (; i < 16; i++) { - if (!(i%2)) printf (" "); - printf (" "); + if (!(i%2)) fprintf (outfile, " "); + fprintf (outfile, " "); } - printf (" "); - - for (i = 0; i < rowlen; i++) { - if (isgraph(buf[i])) printf ("%c", buf[i]); - else if (isspace(buf[i])) printf (" "); - else printf ("."); - } + if (dump_char) + dump_char_line (buf, rowlen); - printf("\n"); + fprintf(outfile, "\n"); remaining -= rowlen; buf += rowlen; @@ -83,7 +117,9 @@ static void hex_dump (unsigned char * buf, long n) { } } -static void bin_dump (unsigned char * buf, long n) { +static void +bin_dump (unsigned char * buf, long n) +{ int i, j; long remaining = n, count = 0; long rowlen; @@ -92,34 +128,29 @@ static void bin_dump (unsigned char * buf, long n) { rowlen = MIN (remaining, 6); if (n > 0xffffff) - printf ("%08lx:", count); + fprintf (outfile, "%08lx:", count); else if (n > 0xffff) - printf (" %06lx:", count); + fprintf (outfile, " %06lx:", count); else - printf (" %04lx:", count); + fprintf (outfile, " %04lx:", count); for (i = 0; i < rowlen; i++) { - printf (" "); + fprintf (outfile, " "); #ifdef WORDS_BIGENDIAN for (j = 0; j < 8; j++) #else for (j = 7; j >= 0; j--) #endif - printf ("%c", (buf[i]&(1<granulepos, op->packetno); + fprintf (outfile, "%08lx: serialno %010ld, " + "granulepos %" PRId64 ", packetno %" PRId64, + hide_offset ? -1 : oggz_tell (oggz), + hide_serialno ? -1 : serialno, + hide_granulepos ? -1 : op->granulepos, + hide_packetno ? -1 : op->packetno); if (op->b_o_s) { - printf (" *** bos"); + fprintf (outfile, " *** bos"); } if (op->e_o_s) { - printf (" *** eos"); + fprintf (outfile, " *** eos"); } - printf (":\n"); + fprintf (outfile, ":\n"); - hex_dump (op->packet, op->bytes); + if (dump_bits) { + bin_dump (op->packet, op->bytes); + } else { + hex_dump (op->packet, op->bytes); + } - printf ("\n"); + fprintf (outfile, "\n"); return 0; } static int -read_bos_packet (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +ignore_packet (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +{ + return -1; +} + +static int +read_new_packet (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +{ + oggz_set_read_callback (oggz, serialno, ignore_packet, NULL); + read_packet (oggz, op, serialno, user_data); + + return -1; +} + +static void +revert_file (char * infilename) { - if (!op->b_o_s) { - oggz_set_read_callback (oggz, -1, NULL, NULL); - return 1; + OGGZ * oggz; + FILE * infile; + char line[80]; + unsigned int offset; + long current_serialno = -1, serialno; + ogg_int64_t granulepos, packetno; + int bos = 0, eos = 0; + + int line_offset = 0, consumed = 0; + + unsigned char * packet = NULL; + long max_bytes = 0; + + unsigned char buf[1024]; + ogg_packet op; + int flush = 1; + long n; + char c; + + if (strcmp (infilename, "-") == 0) { + infile = stdin; + } else { + infile = fopen (infilename, "r"); + } + + oggz = oggz_new (OGGZ_WRITE|OGGZ_NONSTRICT); + + while (fgets (line, 80, infile)) { + if (sscanf (line, "%x: serialno %ld, granulepos %lld, packetno %lld%n", + &offset, &serialno, &granulepos, &packetno, + &line_offset) >= 4) { + + /* flush any existing packets */ + if (current_serialno != -1) { + int ret; + +#ifdef DEBUG + printf ("feeding packet (%010ld) %ld bytes\n", + current_serialno, op.bytes); +#endif + if ((ret = oggz_write_feed (oggz, &op, current_serialno, flush, NULL)) != 0) { + fprintf (stderr, "%s: oggz_write_feed error %d\n", progname, ret); + } + + while ((n = oggz_write_output (oggz, buf, 1024)) > 0) { + fwrite (buf, 1, 1024, outfile); + } + } + + /* Start new packet */ + bos = 0; eos = 0; + if (sscanf (&line[line_offset], " *** %[b]%[o]%[s]%n", &c, &c, &c, + &consumed) >= 3) { + bos = 1; + line_offset += consumed; + } + if (sscanf (&line[line_offset], " *** %[e]%[o]%[s]%n", &c, &c, &c, + &consumed) >= 3) { + eos = 1; + line_offset += consumed; + } + + current_serialno = serialno; + + op.packet = packet; + op.bytes = 0; + op.b_o_s = bos; + op.e_o_s = eos; + op.granulepos = granulepos; + op.packetno = packetno; + + } else { + int nread = 0; + unsigned int val = 0; + unsigned int offset; + + if (current_serialno != -1 && + sscanf (line, "%x:%n", &offset, &line_offset) >= 1) { + while (nread < 16 && + (sscanf (&line[line_offset], "%2x%n", &val, &consumed) > 0)) { + op.bytes++; + if (op.bytes > max_bytes) { + unsigned char * new_packet; + size_t new_size; + + if (max_bytes == 0) { + new_size = 128; + } else { + new_size = max_bytes * 2; + } + + new_packet = + (unsigned char *) realloc ((void *)packet, new_size); + if (new_packet == NULL) { + fprintf (stderr, + "%s: error allocating memory for packet data\n", + progname); + exit (1); + } else { + max_bytes = new_size; + packet = new_packet; + op.packet = packet; + } + } + + packet[op.bytes-1] = (unsigned char) val; + + line_offset += consumed; + nread++; + } + } + } } - return read_packet (oggz, op, serialno, user_data); + fclose (infile); } int main (int argc, char ** argv) { OGGZ * oggz; + char * infilename = NULL, * outfilename = NULL; + int revert = 0; + OggzTable * table = NULL; + long serialno; + OggzReadPacket my_read_packet = read_packet; + int i, size; long n; + progname = argv[0]; + if (argc < 2) { - printf ("usage: %s filename\n", argv[0]); + printf ("usage: %s filename\n", progname); + } + + table = oggz_table_new(); + + while (1) { + char * optstring = "hbxnro:s:OSGP"; + +#ifdef HAVE_GETOPT_LONG + static struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"binary", no_argument, 0, 'b'}, + {"hexadecimal", no_argument, 0, 'x'}, + {"new", no_argument, 0, 'n'}, + {"revert", no_argument, 0, 'r'}, + {"output", required_argument, 0, 'o'}, + {"serialno", required_argument, 0, 's'}, + {"hide-offset", no_argument, 0, 'O'}, + {"hide-serialno", no_argument, 0, 'S'}, + {"hide-granulepos", no_argument, 0, 'G'}, + {"hide-packetno", no_argument, 0, 'P'}, + {0,0,0,0} + }; + + i = getopt_long(argc, argv, optstring, long_options, NULL); +#else + i = getopt (argc, argv, optstring); +#endif + if (i == -1) break; + if (i == ':') { + usage (progname); + exit (1); + } + + switch (i) { + case 'h': /* help */ + usage (progname); + exit (0); + break; + case 'b': /* binary */ + dump_bits = 1; + break; + case 'n': /* new */ + my_read_packet = read_new_packet; + break; + case 'o': /* output */ + outfilename = optarg; + break; + case 'r': /* revert */ + revert = 1; + break; + case 's': /* serialno */ + dump_all_serialnos = 0; + serialno = atol (optarg); + oggz_table_insert (table, serialno, &truth); + break; + case 'O': /* hide offset */ + hide_offset = 1; + break; + case 'S': /* hide serialno */ + hide_serialno = 1; + break; + case 'G': /* hide granulepos */ + hide_granulepos = 1; + break; + case 'P': /* hide packetno */ + hide_packetno = 1; + break; + default: + break; + } } - if ((oggz = oggz_open ((char *)argv[1], OGGZ_READ)) == NULL) { - printf ("unable to open file %s\n", argv[1]); + if (optind >= argc) { + usage (progname); exit (1); } - oggz_set_read_callback (oggz, -1, read_packet, NULL); - while ((n = oggz_read (oggz, 1024)) > 0); + infilename = argv[optind++]; + + if (outfilename == NULL) { + outfile = stdout; + } else { + outfile = fopen (outfilename, "w"); + if (outfile == NULL) { + fprintf (stderr, "%s: unable to open output file %s\n", + progname, outfilename); + exit (1); + } + } + + if (revert) { + if (dump_bits) { + fprintf (stderr, "%s: Revert of binary dump not supported\n", progname); + exit (1); + } + + revert_file (infilename); + } else { + errno = 0; + + if (strcmp (infilename, "-") == 0) { + oggz = oggz_open_stdio (stdin, OGGZ_READ); + } else { + oggz = oggz_open (infilename, OGGZ_READ); + } - oggz_close (oggz); + if (oggz == NULL) { + if (errno == 0) { + fprintf (stderr, "%s: %s: OGGZ error opening input file\n", + progname, infilename); + } else { + fprintf (stderr, "%s: %s: %s\n", + progname, infilename, strerror (errno)); + } + exit (1); + } + + if (dump_all_serialnos) { + oggz_set_read_callback (oggz, -1, my_read_packet, NULL); + } else { + size = oggz_table_size (table); + for (i = 0; i < size; i++) { + oggz_table_nth (table, i, &serialno); + oggz_set_read_callback (oggz, serialno, my_read_packet, NULL); + } + } + + while ((n = oggz_read (oggz, 1024)) > 0); + + oggz_close (oggz); + } exit (0); }