diff --git a/src/add-ons/media/plugins/ogg/liboggz/AUTHORS b/src/add-ons/media/plugins/ogg/liboggz/AUTHORS new file mode 100644 index 0000000000..0b1f6b403f --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/AUTHORS @@ -0,0 +1,11 @@ +Conrad Parker + - Design, general implementation + +Silvia Pfeiffer + - Design, MS Windows porting + +Andre Pang + - Design, Mac OS X porting + +Zentaro Kavanagh + - MS Windows Porting and packaging. Windows specific bugfixes. diff --git a/src/add-ons/media/plugins/ogg/liboggz/COPYING b/src/add-ons/media/plugins/ogg/liboggz/COPYING new file mode 100644 index 0000000000..0be71ec661 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/COPYING @@ -0,0 +1,29 @@ + Copyright (C) 2003 CSIRO Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the CSIRO nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/src/add-ons/media/plugins/ogg/liboggz/ChangeLog b/src/add-ons/media/plugins/ogg/liboggz/ChangeLog new file mode 100644 index 0000000000..e8ba589cbf --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/ChangeLog @@ -0,0 +1,17 @@ +Fri May 21 16:48:34 EST 2004 Conrad Parker + + * Version 0.8.3 + * fixes for win32 build procedure + * improved API documentation for seeking, OGGZ_AUTO and OggzIO + +Thu Mar 11 11:00:00 EST 2004 Silvia Pfeiffer + * Version 0.8.2 + * fixed up the Makefiles to not include the CVS subdirectories + +Sun Mar 07 16:00:00 EST 2004 Silvia Pfeiffer + * Version 0.8.1 + * includes Windows port with Makefile & VC6 workspace & .NET solution + +Thu Oct 16 21:55:07 EST 2003 Conrad Parker + + * split liboggz out from libannodex sources, started ChangeLog diff --git a/src/add-ons/media/plugins/ogg/liboggz/Jamfile b/src/add-ons/media/plugins/ogg/liboggz/Jamfile new file mode 100644 index 0000000000..348c9173d5 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/Jamfile @@ -0,0 +1,14 @@ +SubDir OBOS_TOP src add-ons media plugins ogg liboggz ; + +SubDirHdrs [ FDirName $(SUBDIR) .. libogg ] ; +SubDirCcFlags -Wno-missing-prototypes ; + +StaticLibrary oggz : + oggz.c + oggz_auto.c + oggz_io.c + oggz_read.c + oggz_table.c + oggz_vector.c + oggz_write.c +; diff --git a/src/add-ons/media/plugins/ogg/liboggz/config.h b/src/add-ons/media/plugins/ogg/liboggz/config.h new file mode 100644 index 0000000000..e81dc759a1 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/config.h @@ -0,0 +1,97 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the 'getopt_long' function */ +#define HAVE_GETOPT_LONG + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `random' function. */ +#define HAVE_RANDOM 1 + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to build experimental code */ +/* #undef OGGZ_CONFIG_EXPERIMENTAL */ + +/* Do not build reading support */ +#define OGGZ_CONFIG_READ 1 + +/* Do not build writing support */ +#define OGGZ_CONFIG_WRITE 1 + +/* Name of package */ +#define PACKAGE "liboggz" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.8.3" + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `long' if does not define. */ +/* #undef off_t */ + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz.c b/src/add-ons/media/plugins/ogg/liboggz/oggz.c new file mode 100644 index 0000000000..4bef0a7daf --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz.c @@ -0,0 +1,502 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "oggz_compat.h" +#include "oggz_private.h" +#include "oggz_vector.h" + +/*#define DEBUG*/ + +static int +oggz_flags_disabled (int flags) +{ + if (flags & OGGZ_WRITE) { + if (!OGGZ_CONFIG_WRITE) return OGGZ_ERR_DISABLED; + } else { + if (!OGGZ_CONFIG_READ) return OGGZ_ERR_DISABLED; + } + + return 0; +} + +OGGZ * +oggz_new (int flags) +{ + OGGZ * oggz; + + if (oggz_flags_disabled (flags)) return NULL; + + oggz = (OGGZ *) oggz_malloc (sizeof (OGGZ)); + if (oggz == NULL) return NULL; + + oggz->flags = flags; + oggz->file = NULL; + oggz->io = NULL; + + oggz->offset = 0; + oggz->offset_data_begin = 0; + + oggz->streams = oggz_vector_new (); + oggz->all_at_eos = 0; + + oggz->metric = NULL; + oggz->metric_user_data = NULL; + oggz->metric_internal = 0; + + oggz->order = NULL; + oggz->order_user_data = NULL; + + if (OGGZ_CONFIG_WRITE && (oggz->flags & OGGZ_WRITE)) { + oggz_write_init (oggz); + } else if (OGGZ_CONFIG_READ) { + oggz_read_init (oggz); + } + + return oggz; +} + +OGGZ * +oggz_open (char * filename, int flags) +{ + OGGZ * oggz = NULL; + FILE * file = NULL; + + if (oggz_flags_disabled (flags)) return NULL; + + if (flags & OGGZ_WRITE) { + file = fopen (filename, "wb"); + } else { + file = fopen (filename, "rb"); + } + if (file == NULL) return NULL; + + oggz = oggz_new (flags); + oggz->file = file; + + return oggz; +} + +OGGZ * +oggz_open_stdio (FILE * file, int flags) +{ + OGGZ * oggz = NULL; + + if (oggz_flags_disabled (flags)) return NULL; + + oggz = oggz_new (flags); + oggz->file = file; + + return oggz; +} + +int +oggz_flush (OGGZ * oggz) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + return oggz_io_flush (oggz); +} + +static int +oggz_stream_clear (void * data) +{ + oggz_stream_t * stream = (oggz_stream_t *) data; + + if (stream->ogg_stream.serialno != -1) + ogg_stream_clear (&stream->ogg_stream); + + if (stream->metric_internal) + oggz_free (stream->metric_user_data); + + return 0; +} + +int +oggz_close (OGGZ * oggz) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + 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); + } else if (OGGZ_CONFIG_READ) { + oggz_read_close (oggz); + } + + if (oggz->file != NULL) { + if (fclose (oggz->file) == EOF) { + return OGGZ_ERR_SYSTEM; + } + } + + oggz_free (oggz); + + return 0; +} + +off_t +oggz_tell (OGGZ * oggz) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + return oggz->offset; +} + +ogg_int64_t +oggz_tell_units (OGGZ * oggz) +{ + OggzReader * reader; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (oggz->flags & OGGZ_WRITE) { + return OGGZ_ERR_INVALID; + } + + reader = &oggz->x.reader; + + if (OGGZ_CONFIG_READ) { + return reader->current_unit; + } else { + return OGGZ_ERR_DISABLED; + } +} + +/******** oggz_stream management ********/ + +static int +oggz_find_stream (void * data, long serialno) +{ + oggz_stream_t * stream = (oggz_stream_t *) data; + + return (stream->ogg_stream.serialno == serialno); +} + +oggz_stream_t * +oggz_get_stream (OGGZ * oggz, long serialno) +{ + if (serialno < 0) return NULL; + + return oggz_vector_find (oggz->streams, oggz_find_stream, serialno); +} + +oggz_stream_t * +oggz_add_stream (OGGZ * oggz, long serialno) +{ + oggz_stream_t * stream; + + stream = oggz_malloc (sizeof (oggz_stream_t)); + if (stream == NULL) return NULL; + + ogg_stream_init (&stream->ogg_stream, (int)serialno); + + stream->delivered_non_b_o_s = 0; + stream->b_o_s = 1; + stream->e_o_s = 0; + stream->granulepos = 0; + stream->packetno = -1; /* will be incremented on first write */ + + stream->metric = NULL; + stream->metric_user_data = NULL; + stream->metric_internal = 0; + stream->order = NULL; + stream->order_user_data = NULL; + stream->read_packet = NULL; + stream->read_user_data = NULL; + + oggz_vector_insert_p (oggz->streams, stream); + + return stream; +} + +int +oggz_get_bos (OGGZ * oggz, long serialno) +{ + oggz_stream_t * stream; + int i, size; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (serialno == -1) { + 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; +#else + /* If this stream has delivered its bos packet, return FALSE */ + if (!stream->b_o_s) return 0; +#endif + } + return 1; + } else { + stream = oggz_get_stream (oggz, serialno); + if (stream == NULL) + return OGGZ_ERR_BAD_SERIALNO; + + return stream->b_o_s; + } +} + +int +oggz_get_eos (OGGZ * oggz, long serialno) +{ + oggz_stream_t * stream; + int i, size; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (serialno == -1) { + 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; + } else { + stream = oggz_get_stream (oggz, serialno); + if (stream == NULL) + return OGGZ_ERR_BAD_SERIALNO; + + return stream->e_o_s; + } +} + +int +oggz_set_eos (OGGZ * oggz, long serialno) +{ + oggz_stream_t * stream; + int i, size; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (serialno == -1) { + 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; + } else { + stream = oggz_get_stream (oggz, serialno); + if (stream == NULL) + return OGGZ_ERR_BAD_SERIALNO; + + stream->e_o_s = 1; + + if (oggz_get_eos (oggz, -1)) + oggz->all_at_eos = 1; + } + + return 0; +} + +long +oggz_serialno_new (OGGZ * oggz) +{ + long serialno; + + do { + serialno = oggz_random(); + } while (oggz_get_stream (oggz, serialno) != NULL); + + return serialno; +} + +/******** OggzMetric management ********/ + +typedef struct { + ogg_int64_t gr_n; + ogg_int64_t gr_d; +} oggz_metric_linear_t; + +static ogg_int64_t +oggz_metric_default_linear (OGGZ * oggz, long serialno, ogg_int64_t granulepos, + void * user_data) +{ + oggz_metric_linear_t * ldata = (oggz_metric_linear_t *)user_data; + + return (ldata->gr_d * granulepos / ldata->gr_n); +} + +int +oggz_set_metric_internal (OGGZ * oggz, long serialno, + OggzMetric metric, void * user_data, int internal) +{ + oggz_stream_t * stream; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (serialno == -1) { + if (oggz->metric_internal) + oggz_free (oggz->metric_user_data); + oggz->metric = metric; + oggz->metric_user_data = user_data; + oggz->metric_internal = internal; + } else { + stream = oggz_get_stream (oggz, serialno); + if (stream == NULL) return OGGZ_ERR_BAD_SERIALNO; + + if (stream->metric_internal) + oggz_free (stream->metric_user_data); + stream->metric = metric; + stream->metric_user_data = user_data; + stream->metric_internal = internal; + } + + return 0; +} + +int +oggz_set_metric (OGGZ * oggz, long serialno, + OggzMetric metric, void * user_data) +{ + return oggz_set_metric_internal (oggz, serialno, metric, user_data, 0); +} + +int +oggz_set_metric_linear (OGGZ * oggz, long serialno, + ogg_int64_t granule_rate_numerator, + ogg_int64_t granule_rate_denominator) +{ + oggz_metric_linear_t * linear_data; + + /* we divide by the granulerate, ie. mult by gr_d/gr_n, so ensure + * numerator is non-zero */ + if (granule_rate_numerator == 0) { + granule_rate_numerator = 1; + granule_rate_denominator = 0; + } + + linear_data = oggz_malloc (sizeof (oggz_metric_linear_t)); + linear_data->gr_n = granule_rate_numerator; + linear_data->gr_d = granule_rate_denominator; + + return oggz_set_metric_internal (oggz, serialno, oggz_metric_default_linear, + linear_data, 1); +} + +/* + * Check if an oggz has metrics for all streams + */ +int +oggz_has_metrics (OGGZ * oggz) +{ + int i, size; + oggz_stream_t * stream; + + if (oggz->metric != NULL) return 1; + + 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->metric == NULL) return 0; + } + + return 1; +} + +ogg_int64_t +oggz_get_unit (OGGZ * oggz, long serialno, ogg_int64_t granulepos) +{ + oggz_stream_t * stream; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (granulepos == -1) return -1; + + if (serialno == -1) { + if (oggz->metric) + return oggz->metric (oggz, serialno, granulepos, + oggz->metric_user_data); + } else { + stream = oggz_get_stream (oggz, serialno); + if (!stream) return -1; + + if (stream->metric) { + return stream->metric (oggz, serialno, granulepos, + stream->metric_user_data); + } else if (oggz->metric) { + return oggz->metric (oggz, serialno, granulepos, + oggz->metric_user_data); + } + } + + return -1; +} + +int +oggz_set_order (OGGZ * oggz, long serialno, + OggzOrder order, void * user_data) +{ + oggz_stream_t * stream; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (oggz->flags & OGGZ_WRITE) { + return OGGZ_ERR_INVALID; + } + + if (serialno == -1) { + oggz->order = order; + oggz->order_user_data = user_data; + } else { + stream = oggz_get_stream (oggz, serialno); + stream->order = order; + stream->order_user_data = user_data; + } + + return 0; +} + diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz.h b/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz.h new file mode 100644 index 0000000000..0cf42beda8 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz.h @@ -0,0 +1,1153 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __OGGZ_H__ +#define __OGGZ_H__ + +#include +#include + +#include +#include +#include + +/** \mainpage + * + * \section intro Oggz makes programming with Ogg easy! + * + * This is the documentation for the Oggz C API. Oggz provides a simple + * programming interface for reading and writing Ogg files and streams. + * Ogg is an interleaving data container developed by Monty + * at Xiph.Org, originally to + * support the Ogg Vorbis audio format. + * + * 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 means of overriding the \link oggz_io.h IO functions \endlink used by + * Oggz, for easier integration with media frameworks and similar systems. + * - A handy \link oggz_table.h table \endlink structure for storing + * information on each logical bitstream + * + * \subsection contents Contents + * + * - \link basics Basics \endlink: + * Information about Ogg required to understand liboggz + * + * - \link oggz.h oggz.h \endlink: + * Documentation of the Oggz C API + * + * - \link configuration Configuration \endlink: + * Customizing liboggz to only read or write. + * + * - \link building Building \endlink: + * Information related to building software that uses liboggz. + * + * \section Licensing + * + * liboggz is provided under the following BSD-style open source license: + * + * \include COPYING + * + */ + +/** \defgroup basics Ogg basics + * + * \section Scope + * + * This section provides a minimal introduction to Ogg concepts, covering + * only that which is required to use liboggz. + * + * For more detailed information, see the + * Ogg homepage + * or IETF RFC 3533 + * The Ogg File Format version 0. + * + * \section Terminology + * + * The monospace text below is quoted directly from RFC 3533. + * For each concept introduced, tips related to liboggz are provided + * in bullet points. + * + * \subsection bitstreams Physical and Logical Bitstreams + * + * 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 + * + * 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.
+
+ * + */ + +/** \defgroup configuration Configuration + * \section ./configure ./configure + * + * It is possible to customize the functionality of liboggz + * by using various ./configure flags when + * building it from source. You can build a smaller + * version of liboggz to only read or write. + * By default, both reading and writing support is built. + * + * For general information about using ./configure, see the file + * \link install INSTALL \endlink + * + * \subsection no_encode Removing writing support + * + * 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_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() + * will return OGGZ_ERR_DISABLED + * + * \subsection no_decode Removing reading support + * + * 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_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 + * OGGZ_ERR_DISABLED + * + */ + +/** \defgroup install Installation + * \section install INSTALL + * + * \include INSTALL + */ + +/** \defgroup building Building against liboggz + * + * + * \section autoconf Using GNU autoconf + * + * If you are using GNU autoconf, you do not need to call pkg-config + * directly. Use the following macro to determine if liboggz is + * available: + * +
+ PKG_CHECK_MODULES(OGGZ, oggz >= 0.6.0,
+                   HAVE_OGGZ="yes", HAVE_OGGZ="no")
+ if test "x$HAVE_OGGZ" = "xyes" ; then
+   AC_SUBST(OGGZ_CFLAGS)
+   AC_SUBST(OGGZ_LIBS)
+ fi
+ 
+ * + * If liboggz is found, HAVE_OGGZ will be set to "yes", and + * the autoconf variables OGGZ_CFLAGS and OGGZ_LIBS will + * be set appropriately. + * + * \section pkg-config Determining compiler options with pkg-config + * + * If you are not using GNU autoconf in your project, you can use the + * pkg-config tool directly to determine the correct compiler options. + * +
+ OGGZ_CFLAGS=`pkg-config --cflags oggz`
+
+ OGGZ_LIBS=`pkg-config --libs oggz`
+ 
+ * + */ + +/** \file + * The liboggz C API. + * + * \section general Generic semantics + * + * All access is managed via an OGGZ handle. This can be instantiated + * in one of three ways: + * + * - oggz_open() - Open a full pathname + * - oggz_open_stdio() - Use an already opened FILE * + * - oggz_new() - Create an anonymous OGGZ object, which you can later + * handle via memory buffers + * + * To finish using an OGGZ handle, it should be closed with oggz_close(). + * + * \section reading Reading Ogg data + * + * To read from Ogg files or streams you must instantiate an OGGZ handle + * with flags set to OGGZ_READ, and provide an OggzReadPacket + * callback with oggz_set_read_callback(). + * See the \ref read_api section for details. + * + * \section writing Writing Ogg data + * + * To write to Ogg files or streams you must instantiate an OGGZ handle + * with flags set to OGGZ_WRITE, and provide an OggzWritePacket + * 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 io Overriding the IO methods + * + * When an OGGZ handle is instantiated by oggz_open() or oggz_open_stdio(), + * Oggz uses stdio functions internally to access the raw data. However for + * some applications, the raw data cannot be accessed via stdio -- this + * commonly occurs when integrating with media frameworks. For such + * applications, you can provide Oggz with custom IO methods that it should + * use to access the raw data. Oggz will then use these custom methods, + * rather than using stdio methods, to access the raw data internally. + * + * For details, see \link oggz_io.h \endlink. + * + * \section headers Headers + * + * oggz.h provides direct access to libogg types such as ogg_packet, defined + * in . + */ + +/** + * 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; + +/** + * Create a new OGGZ object + * \param flags OGGZ_READ or OGGZ_WRITE + * \returns A new OGGZ object + * \retval NULL on system error; check errno for details + */ +OGGZ * oggz_new (int flags); + +/** + * Open an Ogg file, creating an OGGZ handle for it + * \param filename The file to open + * \param flags OGGZ_READ or OGGZ_WRITE + * \return A new OGGZ handle + * \retval NULL System error; check errno for details + */ +OGGZ * oggz_open (char * filename, int flags); + +/** + * 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_open_stdio (FILE * file, int flags); + +/** + * Ensure any associated io 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 + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + * \retval OGGZ_ERR_SYSTEM System error; check errno for details + */ +int oggz_flush (OGGZ * oggz); + +/** + * Close an OGGZ handle + * \param oggz An OGGZ handle + * \retval 0 Success + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_SYSTEM System error; check errno for details + */ +int oggz_close (OGGZ * oggz); + +/** + * Determine if a given logical bitstream is at bos (beginning of stream). + * \param oggz An OGGZ handle + * \param serialno Identify a logical bitstream within \a oggz, or -1 to + * query if all logical bitstreams in \a oggz are at bos + * \retval 1 The given stream is at bos + * \retval 0 The given stream is not at bos + * \retval OGGZ_ERR_BAD_SERIALNO \a serialno does not identify an existing + * logical bitstream in \a oggz. + */ +int oggz_get_bos (OGGZ * oggz, long serialno); + +/** + * Determine if a given logical bitstream is at eos (end of stream). + * \param oggz An OGGZ handle + * \param serialno Identify a logical bitstream within \a oggz, or -1 to + * query if all logical bitstreams in \a oggz are at eos + * \retval 1 The given stream is at eos + * \retval 0 The given stream is not at eos + * \retval OGGZ_ERR_BAD_SERIALNO \a serialno does not identify an existing + * logical bitstream in \a oggz. + */ +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. + * + * \param oggz The OGGZ handle + * \param op The full ogg_packet (see ) + * \param serialno Identify the logical bistream in \a oggz that contains + * \a op + * \param user_data A generic pointer you have provided earlier + * \returns 0 to continue, non-zero to instruct OGGZ to stop. + * + * \note It is possible to provide different callbacks per logical + * bitstream -- see oggz_set_read_callback() for more information. + */ +typedef int (*OggzReadPacket) (OGGZ * oggz, ogg_packet * op, long serialno, + void * user_data); + +/** + * Set a callback for Oggz to call when a new Ogg packet is found in the + * stream. + * + * \param oggz An OGGZ handle previously opened for reading + * \param serialno Identify the logical bitstream in \a oggz to attach + * this callback to, or -1 to attach this callback to all unattached + * logical bitstreams in \a oggz. + * \param read_packet Your callback function + * \param user_data Arbitrary data you wish to pass to your callback + * \retval 0 Success + * \retval OGGZ_ERR_BAD_SERIALNO \a serialno does not identify an existing + * logical bitstream in \a oggz. + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + * + * \note Values of \a serialno other than -1 allows you to specify different + * callback functions for each logical bitstream. + * + * \note It is safe to call this callback from within an OggzReadPacket + * function, in order to specify that subsequent packets should be handled + * by a different OggzReadPacket function. + */ +int oggz_set_read_callback (OGGZ * oggz, long serialno, + OggzReadPacket read_packet, void * user_data); + +/** + * Read n bytes into \a oggz, calling any read callbacks on the fly. + * \param oggz An OGGZ handle previously opened for reading + * \param n A count of bytes to ingest + * \retval "> 0" The number of bytes successfully ingested. + * \retval 0 End of file + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + * \retval OGGZ_ERR_SYSTEM System error; check errno for details + */ +long oggz_read (OGGZ * oggz, long n); + +/** + * Input data into \a oggz. + * \param oggz An OGGZ handle previously opened for reading + * \param buf A memory buffer + * \param n A count of bytes to input + * \retval "> 0" The number of bytes successfully ingested. + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + */ +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. + * + * \{ + */ + +/** + * This is the signature of a callback which Oggz will call when \a oggz + * is \link hungry hungry \endlink. + * + * \param oggz The OGGZ handle + * \param empty A value of 1 indicates that the packet queue is currently + * empty. A value of 0 indicates that the packet queue is not empty. + * \param user_data A generic pointer you have provided earlier + * \retval 0 Continue + * \retval non-zero Instruct OGGZ to stop. + */ +typedef int (*OggzWriteHungry) (OGGZ * oggz, int empty, void * user_data); + +/** + * Set a callback for Oggz to call when \a oggz + * is \link hungry hungry \endlink. + * + * \param oggz An OGGZ handle previously opened for writing + * \param hungry Your callback function + * \param only_when_empty When to call: a value of 0 indicates that + * OGGZ should call \a hungry() after each and every packet is written; + * a value of 1 indicates that OGGZ should call \a hungry() only when + * its packet queue is empty + * \param user_data Arbitrary data you wish to pass to your callback + * \retval 0 Success + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + * \note Passing a value of 0 for \a only_when_empty allows you to feed + * new packets into \a oggz's packet queue on the fly. + */ +int oggz_write_set_hungry_callback (OGGZ * oggz, + OggzWriteHungry hungry, + int only_when_empty, + void * user_data); +/** + * Add a packet to \a oggz's packet queue. + * \param oggz An OGGZ handle previously opened for writing + * \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 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 + * \retval OGGZ_ERR_BOS Packet would be bos packet of a new logical bitstream, + * but oggz has already written one or more non-bos packets in + * other logical bitstreams, + * and \a oggz is not flagged OGGZ_NONSTRICT + * \retval OGGZ_ERR_EOS The logical bitstream identified by \a serialno is + * already at eos, + * and \a oggz is not flagged OGGZ_NONSTRICT + * \retval OGGZ_ERR_BAD_BYTES \a op->bytes is invalid, + * and \a oggz is not flagged OGGZ_NONSTRICT + * \retval OGGZ_ERR_BAD_B_O_S \a op->b_o_s is invalid, + * and \a oggz is not flagged OGGZ_NONSTRICT + * \retval OGGZ_ERR_BAD_GRANULEPOS \a op->granulepos is less than that of + * an earlier packet within this logical bitstream, + * and \a oggz is not flagged OGGZ_NONSTRICT + * \retval OGGZ_ERR_BAD_PACKETNO \a op->packetno is less than that of an + * earlier packet within this logical bitstream, + * and \a oggz is not flagged OGGZ_NONSTRICT + * \retval OGGZ_ERR_BAD_SERIALNO \a serialno does not identify an existing + * logical bitstream in \a oggz, + * and \a oggz is not flagged OGGZ_NONSTRICT + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + * + * \note If \a op->b_o_s is initialized to \a -1 before calling + * oggz_write_feed(), Oggz will fill it in with the appropriate + * value; ie. 1 for the first packet of a new stream, and 0 otherwise. + */ +int oggz_write_feed (OGGZ * oggz, ogg_packet * op, long serialno, int flush, + int * guard); + +/** + * Output data from an OGGZ handle. Oggz will call your write callback + * as needed. + * + * \param oggz An OGGZ handle previously opened for writing + * \param buf A memory buffer + * \param n A count of bytes to output + * \retval "> 0" The number of bytes successfully output + * \retval 0 End of stream + * \retval OGGZ_ERR_RECURSIVE_WRITE Attempt to initiate writing from + * within an OggzHungry callback + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + */ +long oggz_write_output (OGGZ * oggz, unsigned char * buf, long n); + +/** + * Write n bytes from an OGGZ handle. Oggz will call your write callback + * as needed. + * + * \param oggz An OGGZ handle previously opened for writing + * \param n A count of bytes to be written + * \retval "> 0" The number of bytes successfully output + * \retval 0 End of stream + * \retval OGGZ_ERR_RECURSIVE_WRITE Attempt to initiate writing from + * within an OggzHungry callback + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + */ +long oggz_write (OGGZ * oggz, long n); + +/** + * Query the number of bytes in the next page to be written. + * + * \param oggz An OGGZ handle previously opened for writing + * \retval ">= 0" The number of bytes in the next page + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + */ +long oggz_write_get_next_page_size (OGGZ * oggz); + +/** \} + */ + +/** \defgroup auto OGGZ_AUTO + * + * \section auto Automatic Metrics + * + * Oggz can automatically provide \link metric metrics \endlink 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. + */ + +/** \defgroup metric Using OggzMetric + * + * \section metric_intro Introduction + * + * 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 + * + * You don't need to set metrics for Vorbis, Speex, Theora or Annodex. + * These can be handled \link auto automatically \endlink 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 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 streams with non-linear granulepos, you need to set a custom metric: + * + * - Implement an OggzMetric callback + * - Set the OggzMetric callback using oggz_set_metric() + * + * \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 + * + * Many data streams begin with headers describing such things as codec + * setup parameters. One of the assumptions Monty makes is: + * + * - Given pre-cached decode headers, a player may seek into a stream at + * any point and begin decode. + * + * Thus, the first action taken by applications dealing with such data is + * to read in and cache the decode headers; thereafter the application can + * safely seek to arbitrary points in the data. + * + * 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_set_data_start(). + * + */ + +/** \defgroup seek_semantics Semantics of seeking in Ogg bitstreams + * + * \section seek_semantics_intro Introduction + * + * 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(). + * + */ + +/** \defgroup seek_api OGGZ Seek API + * + * Oggz can seek on multitrack, multicodec bitstreams. + * + * - If you are expecting to only handle + * Annodex bitstreams, + * or any combination of Vorbis, + * Speex and + * Theora logical bitstreams, seeking + * is built-in to Oggz. See \link auto OGGZ_AUTO \endlink for details. + * + * - For other data streams, you will need to provide a metric function; + * 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. + * + * For a full description of the seeking methods possible in Ogg, see + * \link seek_semantics Semantics of seeking in Ogg bitstreams \endlink. + * + * \{ + */ + +/** + * Specify that a logical bitstream has a linear metric + * \param oggz An OGGZ handle + * \param serialno Identify the logical bitstream in \a oggz to attach + * this linear metric to. A value of -1 indicates that the metric should + * be attached to all unattached logical bitstreams in \a oggz. + * \param granule_rate_numerator The numerator of the granule rate + * \param granule_rate_denominator The denominator of the granule rate + * \returns 0 Success + * \retval OGGZ_ERR_BAD_SERIALNO \a serialno does not identify an existing + * logical bitstream in \a oggz. + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + */ +int oggz_set_metric_linear (OGGZ * oggz, long serialno, + ogg_int64_t granule_rate_numerator, + ogg_int64_t granule_rate_denominator); + +/** + * This is the signature of a function to correlate Ogg streams. + * 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. + * + * \param oggz An OGGZ handle + * \param serialno Identifies a logical bitstream within \a oggz + * \param granulepos A granulepos within the logical bitstream identified + * by \a serialno + * \param user_data Arbitrary data you wish to pass to your callback + * \returns A conversion of the (serialno, granulepos) pair into a measure + * in units which is consistent across all logical bitstreams within \a oggz + */ +typedef ogg_int64_t (*OggzMetric) (OGGZ * oggz, long serialno, + ogg_int64_t granulepos, void * user_data); + +/** + * Set the OggzMetric to use for an OGGZ handle + * + * \param oggz An OGGZ handle + * \param serialno Identify the logical bitstream in \a oggz to attach + * this metric to. A value of -1 indicates that this metric + * should be attached to all unattached logical bitstreams + * in \a oggz. + * \param metric An OggzMetric callback + * \param user_data arbitrary data to pass to the metric callback + * + * \returns 0 Success + * \retval OGGZ_ERR_BAD_SERIALNO \a serialno does not identify an existing + * logical bitstream in \a oggz, and is not -1 + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * + * \note Specifying values of \a serialno other than -1 allows you to pass + * logical bitstream specific user_data to the same metric. + * \note Alternatively, you may use a different \a metric for each + * \a serialno, but all metrics used must return mutually consistent + * unit measurements. + */ +int oggz_set_metric (OGGZ * oggz, long serialno, OggzMetric metric, + void * user_data); + +/** + * Query the current offset in units corresponding to the Metric function + * \param oggz An OGGZ handle + * \returns the offset in units + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + */ +ogg_int64_t oggz_tell_units (OGGZ * oggz); + +/** + * Seek to a number of units corresponding to the Metric function + * \param oggz An OGGZ handle + * \param units A number of units + * \param whence As defined in : SEEK_SET, SEEK_CUR or SEEK_END + * \returns the new file offset, or -1 on failure. + */ +ogg_int64_t oggz_seek_units (OGGZ * oggz, ogg_int64_t units, int whence); + +#ifdef _UNIMPLEMENTED +/** \defgroup order OggzOrder + * + * \subsection OggzOrder + * + * Suppose there is a partial order < and a corresponding equivalence + * relation = defined on the space of packets in the Ogg stream of 'OGGZ'. + * An OggzOrder simply provides a comparison in terms of '<' and '=' for + * ogg_packets against a target. + * + * To use OggzOrder: + * + * - Implement an OggzOrder callback + * - Set the OggzOrder callback for an OGGZ handle with oggz_set_order() + * - To seek, use oggz_seek_byorder(). Oggz will use a combination bisection + * search and scan of the Ogg bitstream, using the OggzOrder callback to + * match against the desired 'target'. + * + * Otherwise, for more general ogg streams for which a partial order can be + * defined, define a function matching this specification. + * + * Parameters: + * + * OGGZ: the OGGZ object + * op: an ogg packet in the stream + * target: a user defined object + * + * Return values: + * + * -1 , if 'op' would occur before the position represented by 'target' + * 0 , if the position of 'op' is equivalent to that of 'target' + * 1 , if 'op' would occur after the position represented by 'target' + * 2 , if the relationship between 'op' and 'target' is undefined. + * + * Symbolically: + * + * Suppose there is a partial order < and a corresponding equivalence + * relation = defined on the space of packets in the Ogg stream of 'OGGZ'. + * Let p represent the position of the packet 'op', and t be the position + * represented by 'target'. + * + * Then a function implementing OggzPacketOrder should return as follows: + * + * -1 , p < t + * 0 , p = t + * 1 , t < p + * 2 , otherwise + * + * 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); +/** + * \retval 0 Success + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + */ +int oggz_set_order (OGGZ * oggz, long serialno, OggzOrder order, + void * user_data); + +long oggz_seek_byorder (OGGZ * oggz, void * target); + +#endif /* _UNIMPLEMENTED */ + +/** + * Erase any input buffered in OGGZ. This discards any input read from the + * underlying IO system but not yet delivered as ogg_packets. + * + * \param oggz An OGGZ handle + * \retval 0 Success + * \retval OGGZ_ERR_SYSTEM Error seeking on underlying IO. + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + */ +int oggz_purge (OGGZ * oggz); + +/** + * 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 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_set_data_start (OGGZ * oggz, off_t offset); + +/** + * Provide the file offset in bytes corresponding to the data read. + * \param oggz An OGGZ handle + * \returns The current offset of oggz. + * + * \note When reading, the value returned by oggz_tell() reflects the + * data offset of the start of the most recent packet processed, so that + * when called from an OggzReadPacket callback it reflects the byte + * offset of the start of the packet. As OGGZ may have internally read + * ahead, this may differ from the current offset of the associated file + * descriptor. + */ +off_t oggz_tell (OGGZ * oggz); + +/** + * 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 + * \returns the new file offset, or -1 on failure. + */ +off_t oggz_seek (OGGZ * oggz, off_t offset, int whence); + +#ifdef _UNIMPLEMENTED +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 + * is not yet used for any other streams managed by this OGGZ. + * \param oggz An OGGZ handle + * \returns A new serialno, not already occuring in any logical bitstreams + * in \a oggz. + */ +long oggz_serialno_new (OGGZ * oggz); + +#include + +#endif /* __OGGZ_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_constants.h b/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_constants.h new file mode 100644 index 0000000000..2449b762b4 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_constants.h @@ -0,0 +1,161 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __OGGZ_CONSTANTS_H__ +#define __OGGZ_CONSTANTS_H__ + +/** \file + * General constants used by liboggz. + */ + +/** + * 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 */ + OGGZ_READ = 0x00, + + /** Write only */ + OGGZ_WRITE = 0x01, + + /** Disable strict adherence to mapping constraints, eg for + * handling an incomplete stream */ + 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 +}; + +enum OggzStatus { + OGGZ_STATUS_INITIALIZED = 0, + OGGZ_STATUS_INTERNAL_ERROR = 1, + + OGGZ_STATUS_READ_ACTIVE = 10, + OGGZ_STATUS_READ_STOP_OK = 11, + OGGZ_STATUS_READ_STOP_ERR = 12, + OGGZ_STATUS_READ_EOF = 13 +}; + +enum OggzStopCtl { + /** Continue calling read callbacks */ + OGGZ_CONTINUE = 0, + + /** Stop calling callbacks, but retain buffered packet data */ + OGGZ_STOP_OK = 1, + + /** Stop calling callbacks, and purge buffered packet data */ + OGGZ_STOP_ERR = -1 +}; + +/** + * Flush options for oggz_write_feed; can be or'ed together + */ +enum OggzFlushOpts { + /** Flush all streams before beginning this packet */ + OGGZ_FLUSH_BEFORE = 0x01, + + /** Flush after this packet */ + OGGZ_FLUSH_AFTER = 0x02 +}; + +/** + * Definitions of error return values + */ +enum OggzError { + /** No error */ + OGGZ_ERR_OK = 0, + + /** generic error */ + OGGZ_ERR_GENERIC = -1, + + /** oggz is not a valid OGGZ */ + OGGZ_ERR_BAD_OGGZ = -2, + + /** The requested operation is not suitable for this OGGZ */ + OGGZ_ERR_INVALID = -3, + + /** oggz contains no logical bitstreams */ + OGGZ_ERR_NO_STREAMS = -4, + + /** Operation is inappropriate for oggz in current bos state */ + OGGZ_ERR_BOS = -5, + + /** 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, + + /** Packet disallowed due to invalid byte length */ + OGGZ_ERR_BAD_BYTES = -21, + + /** Packet disallowed due to invalid b_o_s (beginning of stream) flag */ + OGGZ_ERR_BAD_B_O_S = -22, + + /** Packet disallowed due to invalid e_o_s (end of stream) flag */ + OGGZ_ERR_BAD_E_O_S = -23, + + /** Packet disallowed due to invalid granulepos */ + OGGZ_ERR_BAD_GRANULEPOS = -24, + + /** Packet disallowed due to invalid packetno */ + OGGZ_ERR_BAD_PACKETNO = -25, + + /** Guard provided by user has non-zero value */ + OGGZ_ERR_BAD_GUARD = -210, + + /** Attempt to call oggz_write() or oggz_write_output() from within + * a hungry() callback */ + OGGZ_ERR_RECURSIVE_WRITE = -266 +}; + +#endif /* __OGGZ_CONSTANTS_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_io.h b/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_io.h new file mode 100644 index 0000000000..0bc80d3f8c --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_io.h @@ -0,0 +1,231 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __OGGZ_IO_H__ +#define __OGGZ_IO_H__ + +/** \file + * Overriding the functions used for input and output of raw data. + * + * OggzIO provides a way of overriding the functions Oggz uses to access + * its raw input or output data. This is required in many situations where + * the raw stream cannot be accessed via stdio, but can be accessed by + * other means. This is typically useful within media frameworks, where + * accessing and moving around in the data is possible only using methods + * provided by the framework. + * + * The functions you provide for overriding IO will be used by Oggz + * whenever you call oggz_read() or oggz_write(). They will also be + * used repeatedly by Oggz when you call oggz_seek(). + * + * \note Opening a file with oggz_open() or oggz_open_stdio() is equivalent + * to calling oggz_new() and setting stdio based functions for data IO. + */ + +/** + * This is the signature of a function which you provide for Oggz + * to call when it needs to acquire raw input data. + * + * \param user_handle A generic pointer you have provided earlier + * \param n The length in bytes that Oggz wants to read + * \param buf The buffer that you read data into + * \retval "> 0" The number of bytes successfully read into the buffer + * \retval 0 to indicate that there is no more data to read (End of file) + * \retval "< 0" An error condition + */ +typedef size_t (*OggzIORead) (void * user_handle, void * buf, size_t n); + +/** + * This is the signature of a function which you provide for Oggz + * to call when it needs to output raw data. + * + * \param user_handle A generic pointer you have provided earlier + * \param n The length in bytes of the data + * \param buf A buffer containing data to write + * \retval ">= 0" The number of bytes successfully written (may be less than + * \a n if a write error has occurred) + * \retval "< 0" An error condition + */ +typedef size_t (*OggzIOWrite) (void * user_handle, void * buf, size_t n); + +/** + * This is the signature of a function which you provide for Oggz + * to call when it needs to seek on the raw input or output data. + * + * \param user_handle A generic pointer you have provided earlier + * \param offset The offset in bytes to seek to + * \param whence SEEK_SET, SEEK_CUR or SEEK_END (as for stdio.h) + * \retval ">= 0" The offset seeked to + * \retval "< 0" An error condition + * + * \note If you provide an OggzIOSeek function, you MUST also provide + * an OggzIOTell function, or else all your seeks will fail. + */ +typedef int (*OggzIOSeek) (void * user_handle, long offset, int whence); + +/** + * This is the signature of a function which you provide for Oggz + * to call when it needs to determine the current offset of the raw + * input or output data. + * + * \param user_handle A generic pointer you have provided earlier + * \retval ">= 0" The offset + * \retval "< 0" An error condition + */ +typedef long (*OggzIOTell) (void * user_handle); + +/** + * This is the signature of a function which you provide for Oggz + * to call when it needs to flush the output data. The behaviour + * of this function is similar to that of fflush() in stdio. + * + * \param user_handle A generic pointer you have provided earlier + * \retval 0 Success + * \retval "< 0" An error condition + */ +typedef int (*OggzIOFlush) (void * user_handle); + + +/** + * Set a function for Oggz to call when it needs to read input data. + * + * \param oggz An OGGZ handle + * \param read Your reading function + * \param user_handle Any arbitrary data you wish to pass to the function + * \retval 0 Success + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ; \a oggz not + * open for reading. + */ +int oggz_io_set_read (OGGZ * oggz, OggzIORead read, void * user_handle); + +/** + * Retrieve the user_handle associated with the function you have provided + * for reading input data. + * + * \param oggz An OGGZ handle + * \returns the associated user_handle + */ +void * oggz_io_get_read_user_handle (OGGZ * oggz); + +/** + * Set a function for Oggz to call when it needs to write output data. + * + * \param oggz An OGGZ handle + * \param write Your writing function + * \param user_handle Any arbitrary data you wish to pass to the function + * \retval 0 Success + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ; \a oggz not + * open for writing. + */ +int oggz_io_set_write (OGGZ * oggz, OggzIOWrite write, void * user_handle); + +/** + * Retrieve the user_handle associated with the function you have provided + * for writing output data. + * + * \param oggz An OGGZ handle + * \returns the associated user_handle + */ +void * oggz_io_get_write_user_handle (OGGZ * oggz); + +/** + * Set a function for Oggz to call when it needs to seek on its raw data. + * + * \param oggz An OGGZ handle + * \param seek Your seeking function + * \param user_handle Any arbitrary data you wish to pass to the function + * \retval 0 Success + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + * + * \note If you provide an OggzIOSeek function, you MUST also provide + * an OggzIOTell function, or else all your seeks will fail. + */ +int oggz_io_set_seek (OGGZ * oggz, OggzIOSeek seek, void * user_handle); + +/** + * Retrieve the user_handle associated with the function you have provided + * for seeking on input or output data. + * + * \param oggz An OGGZ handle + * \returns the associated user_handle + */ +void * oggz_io_get_seek_user_handle (OGGZ * oggz); + +/** + * Set a function for Oggz to call when it needs to determine the offset + * within its input data (if OGGZ_READ) or output data (if OGGZ_WRITE). + * + * \param oggz An OGGZ handle + * \param tell Your tell function + * \param user_handle Any arbitrary data you wish to pass to the function + * \retval 0 Success + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ + */ +int oggz_io_set_tell (OGGZ * oggz, OggzIOTell tell, void * user_handle); + +/** + * Retrieve the user_handle associated with the function you have provided + * for determining the current offset in input or output data. + * + * \param oggz An OGGZ handle + * \returns the associated user_handle + */ +void * oggz_io_get_tell_user_handle (OGGZ * oggz); + +/** + * Set a function for Oggz to call when it needs to flush its output. The + * meaning of this is similar to that of fflush() in stdio. + * + * \param oggz An OGGZ handle + * \param flush Your flushing function + * \param user_handle Any arbitrary data you wish to pass to the function + * \retval 0 Success + * \retval OGGZ_ERR_BAD_OGGZ \a oggz does not refer to an existing OGGZ + * \retval OGGZ_ERR_INVALID Operation not suitable for this OGGZ; \a oggz not + * open for writing. + */ +int oggz_io_set_flush (OGGZ * oggz, OggzIOFlush flush, void * user_handle); + +/** + * Retrieve the user_handle associated with the function you have provided + * for flushing output. + * + * \param oggz An OGGZ handle + * \returns the associated user_handle + */ +void * oggz_io_get_flush_user_handle (OGGZ * oggz); + +#endif /* __OGGZ_IO_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_table.h b/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_table.h new file mode 100644 index 0000000000..68901ba5f9 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz/oggz_table.h @@ -0,0 +1,103 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __OGGZ_TABLE_H__ +#define __OGGZ_TABLE_H__ + +/** \file + * A lookup table. + * + * OggzTable is provided for convenience to allow the storage of + * serialno-specific data. + */ + +/** + * A table of key-value pairs. + */ +typedef void OggzTable; + +/** + * Instantiate a new OggzTable + * \returns A new OggzTable + */ +OggzTable * +oggz_table_new (void); + +/** + * Delete an OggzTable + * \param table An OggzTable + */ +void +oggz_table_delete (OggzTable * table); + +/** + * Insert an element into a table. + * \param table An OggzTable + * \param key Key to access this data element + * \param data The new element to add + * \retval data If the element was successfully added + * \retval NULL If adding the element failed due to a realloc() error + */ +void * +oggz_table_insert (OggzTable * table, long key, void * data); + +/** + * Retrieve the element of an OggzTable indexed by a given key + * \param table An OggzTable + * \param key a key + * \returns The element indexed by \a key + * \retval NULL \a table is undefined, or no element is indexed by \a key + */ +void * +oggz_table_lookup (OggzTable * table, long key); + +/** + * Query the number of elements in an OggzTable + * \param table An OggzTable + * \returns the number of elements in \a table + */ +int +oggz_table_size (OggzTable * table); + +/** + * Retrieve the nth element of an OggzTable + * \param table An OggzTable + * \param n An index into the \a table + * \param key Return value for key corresponding to nth data element + * of \a table + * \returns The nth data element of \a table + * \retval NULL \a table is undefined, or \a n is out of range + */ +void * +oggz_table_nth (OggzTable * table, int n, long * key); + +#endif /* __OGGZ_TABLE_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_auto.c b/src/add-ons/media/plugins/ogg/liboggz/oggz_auto.c new file mode 100644 index 0000000000..74560b0f18 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_auto.c @@ -0,0 +1,238 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * oggz_auto.c + * + * Conrad Parker + */ + +#include "config.h" + +#if OGGZ_CONFIG_READ +#include +#include + +#include +#include "oggz_auto.h" +#include "oggz_byteorder.h" +#include "oggz_macros.h" + +#define INT32_LE_AT(x) _le_32((*(ogg_int32_t *)(x))) +#define INT32_BE_AT(x) _be_32((*(ogg_int32_t *)(x))) +#define INT64_LE_AT(x) _le_64((*(ogg_int64_t *)(x))) + +/*#define DEBUG*/ + +#define OGGZ_AUTO_MULT 1000 + +static int +auto_speex (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +{ + unsigned char * header = op->packet; + ogg_int64_t granule_rate = 0; + + if (op->bytes < 68) return 0; + + if (strncmp ((char *)header, "Speex ", 8)) return 0; + if (!op->b_o_s) return 0; + + granule_rate = (ogg_int64_t) INT32_LE_AT(&header[36]); +#ifdef DEBUG + printf ("Got speex rate %d\n", (int)granule_rate); +#endif + + oggz_set_metric_linear (oggz, serialno, granule_rate, OGGZ_AUTO_MULT); + + return 1; +} + +static int +auto_vorbis (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +{ + unsigned char * header = op->packet; + ogg_int64_t granule_rate = 0; + + if (op->bytes < 30) return 0; + + if (header[0] != 0x01) return 0; + if (strncmp ((char *)&header[1], "vorbis", 6)) return 0; + if (!op->b_o_s) return 0; + + granule_rate = (ogg_int64_t) INT32_LE_AT(&header[12]); +#ifdef DEBUG + printf ("Got vorbis rate %d\n", (int)granule_rate); +#endif + + oggz_set_metric_linear (oggz, serialno, granule_rate, OGGZ_AUTO_MULT); + + return 1; +} + +typedef struct { + ogg_int32_t fps_numerator; + ogg_int32_t fps_denominator; + int keyframe_shift; +} oggz_theora_metric_t; + +static int intlog(int num) { + int ret=0; + while(num>0){ + num=num/2; + ret=ret+1; + } + return(ret); +} + +static ogg_int64_t +auto_theora_metric (OGGZ * oggz, long serialno, ogg_int64_t granulepos, + void * user_data) +{ + oggz_theora_metric_t * tdata = (oggz_theora_metric_t *)user_data; + ogg_int64_t iframe, pframe; + ogg_int64_t units; + + iframe = granulepos >> tdata->keyframe_shift; + pframe = granulepos - (iframe << tdata->keyframe_shift); + granulepos = (iframe + pframe); + + units = OGGZ_AUTO_MULT * granulepos * tdata->fps_denominator / + tdata->fps_numerator; + + return units; +} + +static int +auto_theora (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +{ + unsigned char * header = op->packet; + char keyframe_granule_shift = 0; + oggz_theora_metric_t * tdata; + + if (op->bytes < 41) return 0; + + if (header[0] != 0x80) return 0; + if (strncmp ((char *)&header[1], "theora", 6)) return 0; + if (!op->b_o_s) return 0; + + + tdata = oggz_malloc (sizeof (oggz_theora_metric_t)); + + tdata->fps_numerator = INT32_BE_AT(&header[22]); + tdata->fps_denominator = INT32_BE_AT(&header[26]); + +#if USE_THEORA_PRE_ALPHA_3_FORMAT + /* old header format, used by Theora alpha2 and earlier */ + keyframe_granule_shift = (header[36] & 0xf8) >> 3; +#else + keyframe_granule_shift = (header[40] & 0x03) << 3; + keyframe_granule_shift |= (header[41] & 0xe0) >> 5; +#endif + tdata->keyframe_shift = intlog (keyframe_granule_shift - 1); + +#ifdef DEBUG + printf ("Got theora fps %d/%d, keyframe_granule_shift %d\n", + tdata->fps_numerator, tdata->fps_denominator, + keyframe_granule_shift); +#endif + + /*oggz_set_metric_internal (oggz, serialno, auto_theora_metric, tdata, 1);*/ + oggz_set_metric (oggz, serialno, auto_theora_metric, tdata); + + return 1; +} + +static int +auto_annodex (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +{ + unsigned char * header = op->packet; + + if (op->bytes < 8) return 0; + + if (strncmp ((char *)header, "Annodex", 8)) return 0; + if (!op->b_o_s) return 0; + + /* Yeah ... set it up with a "linear" metric with numerator 0 :) */ + oggz_set_metric_linear (oggz, serialno, 0, 1); + + return 1; +} + +static int +auto_anxdata (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +{ + unsigned char * header = op->packet; + ogg_int64_t granule_rate_numerator = 0, granule_rate_denominator = 0; + + if (op->bytes < 28) return 0; + + if (strncmp ((char *)header, "AnxData", 8)) return 0; + if (!op->b_o_s) return 0; + + granule_rate_numerator = INT64_LE_AT(&header[8]); + granule_rate_denominator = INT64_LE_AT(&header[16]); +#ifdef DEBUG + printf ("Got AnxData rate %lld/%lld\n", granule_rate_numerator, + granule_rate_denominator); +#endif + + oggz_set_metric_linear (oggz, serialno, + granule_rate_numerator, + OGGZ_AUTO_MULT * granule_rate_denominator); + + return 1; +} + +static OggzReadPacket auto_readers[] = { + auto_speex, + auto_vorbis, + auto_theora, + auto_annodex, + auto_anxdata, + NULL +}; + +int +oggz_auto (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data) +{ + OggzReadPacket read_packet; + int i = 0; + + for (read_packet = auto_readers[0]; read_packet; + read_packet = auto_readers[++i]) { + if (read_packet (oggz, op, serialno, user_data)) return 0; + } + + return 0; +} + +#endif /* OGGZ_CONFIG_READ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_auto.h b/src/add-ons/media/plugins/ogg/liboggz/oggz_auto.h new file mode 100644 index 0000000000..bb37a75eaa --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_auto.h @@ -0,0 +1,255 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * oggz_auto.h + * + * Conrad Parker + */ + +#ifndef __OGGZ_AUTO_H__ +#define __OGGZ_AUTO_H__ + +#include + +/** + * Speex + * + * Default field type: LITTLE ENDIAN unsigned integer + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| speex_string: Identifier char[8]: 'Speex ' | 0-3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 4-7 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| speex_version: char[20] | 8-11 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 12-15 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 16-19 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 20-23 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 24-27 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| speex_version_id | 28-31 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| header_size | 32-35 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| rate | 36-39 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| mode | 40-43 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| mode_bitstream_version | 44-47 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| nb_channels | 48-51 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| bitrate | 52-55 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| frame_size | 56-59 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| vbr | 60-63 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| frames_per_packet | 64-67 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| extra_headers | 68-71 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| reserved1 | 72-75 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| reserved2 | 76-79 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + */ + +/** + * Vorbis + * + * Default field type: LITTLE ENDIAN unsigned integer + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| packtype | Identifier char[6]: 'vorbis' | 0-3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | version | 4-7 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | channels | 8-11 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| rate | 12-15 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| bitrate_upper | 16-19 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| bitrate_nominal | 20-23 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| bitrate_lower | 24-27 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| l2bs0 | l2bs1 |e| 28-31 ++-+-+-+-+-+-+-+-+-+ + + */ + +/** + * Theora + * + * Default field type: BIG ENDIAN unsigned integer + * + +Field names in full caps refer to fields described in the Theora I +specification. Lowercase refers to theora_info struct members from +libtheora. + +This is the Theora header for theora-alpha3: + +(VMAJ=3, VMIN=2, VREV=0) + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| packtype | Identifier char[6]: 'theora' | 0-3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | VMAJ | 4-7 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| VMIN | VREV | FMBW: width >> 4 | 8-11 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| FMBH: height >> 4 | PICW: frame_width | 12-15 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | PICH: frame_height | 16-19 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| PICX: offset_x| PICY: offset_y| FRN: fps_numerator | 20-23 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | FRD: fps_denominator | 24-27 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | PARN: aspect_numerator | 28-31 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | PARD: aspect_denominator | 32-35 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| CS: colorspace| NOMBR: target_bitrate | 36-39 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| QUAL | KFGSHIFT| PF| resv| 40-43 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +This was the Theora header for theora-alpha2: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| packtype | Identifier char[6]: 'theora' | 0-3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | Version major | 4-7 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Version minor | Version sub | width >> 4 | 8-11 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| height >> 4 | frame_width | 12-15 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | frame_height | 16-19 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| offset_x | offset_y | fps_numerator | 20-23 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | fps_denominator | 24-27 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | aspect_numerator | 28-31 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | aspect_denominator | 32-35 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| kgshift | colorspace | target_bitrate | 36-39 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | quality | 40-43 ++-+-+-+-+-+-+-+-+-+-+-+ + + */ + +/** + * Annodex + * + * Default field type: LITTLE ENDIAN unsigned integer + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Identifier char[8]: 'Annodex\0' | 0-3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 4-7 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Version major | Version minor | 8-11 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Timebase numerator | 12-15 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 16-19 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Timebase denominator | 20-23 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 24-27 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| UTC | 28-31 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 32-35 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 36-39 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 40-43 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 44-47 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + */ + +/** + * AnxData + * + * Default field type: LITTLE ENDIAN unsigned integer + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Identifier char[8]: 'AnxData\0' | 0-3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 4-7 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Granule rate numerator | 8-11 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 12-15 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Granule rate denominator | 16-19 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | 20-23 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Number of secondary header pages | 24-27 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Headers ... | 28- ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + */ + +#endif /* __OGGZ_AUTO_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_byteorder.h b/src/add-ons/media/plugins/ogg/liboggz/oggz_byteorder.h new file mode 100644 index 0000000000..c4a7ac5772 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_byteorder.h @@ -0,0 +1,103 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __OGGZ_BYTEORDER_H__ +#define __OGGZ_BYTEORDER_H__ + +#ifdef _UNUSED_ +static unsigned short +_le_16 (unsigned short s) +{ + unsigned short ret=s; +#ifdef WORDS_BIGENDIAN + ret = (s>>8) & 0x00ffU; + ret += (s<<8) & 0xff00U; +#endif + return ret; +} +#endif + +static ogg_uint32_t +_le_32 (ogg_uint32_t i) +{ + ogg_uint32_t ret=i; +#ifdef WORDS_BIGENDIAN + ret = (i>>24); + ret += (i>>8) & 0x0000ff00; + ret += (i<<8) & 0x00ff0000; + ret += (i<<24); +#endif + return ret; +} + +static ogg_uint32_t +_be_32 (ogg_uint32_t i) +{ + ogg_uint32_t ret=i; +#ifndef WORDS_BIGENDIAN + ret = (i>>24); + ret += (i>>8) & 0x0000ff00; + ret += (i<<8) & 0x00ff0000; + ret += (i<<24); +#endif + return ret; +} + +static ogg_int64_t +_le_64 (ogg_int64_t l) +{ + ogg_int64_t ret=l; + unsigned char *ucptr = (unsigned char *)&ret; +#ifdef WORDS_BIGENDIAN + unsigned char temp; + + temp = ucptr [0] ; + ucptr [0] = ucptr [7] ; + ucptr [7] = temp ; + + temp = ucptr [1] ; + ucptr [1] = ucptr [6] ; + ucptr [6] = temp ; + + temp = ucptr [2] ; + ucptr [2] = ucptr [5] ; + ucptr [5] = temp ; + + temp = ucptr [3] ; + ucptr [3] = ucptr [4] ; + ucptr [4] = temp ; + +#endif + return (*(ogg_int64_t *)ucptr); +} + +#endif /* __OGGZ_BYTEORDER_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_compat.h b/src/add-ons/media/plugins/ogg/liboggz/oggz_compat.h new file mode 100644 index 0000000000..d8671bdcda --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_compat.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" + +#ifdef HAVE_RANDOM +# define oggz_random random +#else +# define oggz_random rand +#endif + +#ifndef WIN32 +# define oggz_stat_regular(mode) (S_ISREG((mode)) || S_ISLNK((mode))) +#else +# define oggz_stat_regular(mode) ((mode) & S_IFREG) +#endif diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_io.c b/src/add-ons/media/plugins/ogg/liboggz/oggz_io.c new file mode 100644 index 0000000000..6069efc3d8 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_io.c @@ -0,0 +1,307 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * oggz_io.c + * + * Conrad Parker + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "oggz_compat.h" +#include "oggz_private.h" + +/*#define DEBUG*/ + +size_t +oggz_io_read (OGGZ * oggz, void * buf, size_t n) +{ + OggzIO * io; + size_t bytes; + + if (oggz->file != NULL) { + if ((bytes = fread (buf, 1, n, oggz->file)) == 0) { + if (ferror (oggz->file)) { + return OGGZ_ERR_SYSTEM; + } + } + } + + else if ((io = oggz->io) != NULL) { + if (io->read == NULL) return -1; + bytes = io->read (io->read_user_handle, buf, n); + } + + else return OGGZ_ERR_INVALID; + + return bytes; +} + +size_t +oggz_io_write (OGGZ * oggz, void * buf, size_t n) +{ + OggzIO * io; + size_t bytes; + + if (oggz->file != NULL) { + bytes = fwrite (buf, 1, n, oggz->file); + } + + else if ((io = oggz->io) != NULL) { + if (io->write == NULL) return -1; + bytes = io->write (io->write_user_handle, buf, n); + } + + else return OGGZ_ERR_INVALID; + + return bytes; +} + +int +oggz_io_seek (OGGZ * oggz, long offset, int whence) +{ + OggzIO * io; + + if (oggz->file != NULL) { + if (fseek (oggz->file, offset, whence) == -1) { + if (errno == ESPIPE) { + /*oggz_set_error (oggz, OGGZ_ERR_NOSEEK);*/ + } else { + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ + } + return OGGZ_ERR_SYSTEM; + } + } + + else if ((io = oggz->io) != NULL) { + if (io->seek == NULL) return -1; + if (io->seek (io->seek_user_handle, offset, whence) == -1) + return -1; + } + + else return OGGZ_ERR_INVALID; + + return 0; +} + +long +oggz_io_tell (OGGZ * oggz) +{ + OggzIO * io; + long offset; + + if (oggz->file != NULL) { + if ((offset = ftell (oggz->file)) == -1) { + if (errno == ESPIPE) { + /*oggz_set_error (oggz, OGGZ_ERR_NOSEEK);*/ + } else { + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ + } + return -1; + } + } + + else if ((io = oggz->io) != NULL) { + if (io->tell == NULL) return -1; + if ((offset = io->tell (io->tell_user_handle)) == -1) + return -1; + } + + else return OGGZ_ERR_INVALID; + + return offset; +} + +int +oggz_io_flush (OGGZ * oggz) +{ + OggzIO * io; + + if (oggz->file != NULL) { + if (fflush (oggz->file) == EOF) { + /* check errno for write-related errors */ + return OGGZ_ERR_SYSTEM; + } + } + + else if ((io = oggz->io) != NULL) { + if (io->flush == NULL) return OGGZ_ERR_INVALID; + + if (io->flush (io->flush_user_handle) == -1) { + return -1; + } + } + + else return OGGZ_ERR_INVALID; + + return 0; +} + +/* get/set functions */ + +static void +oggz_io_init (OGGZ * oggz) +{ + oggz->io = (OggzIO *) oggz_malloc (sizeof (OggzIO)); + memset (oggz->io, 0, sizeof (OggzIO)); +} + +int +oggz_io_set_read (OGGZ * oggz, OggzIORead read, void * user_handle) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + if (oggz->file != NULL) return OGGZ_ERR_INVALID; + + if (oggz->io == NULL) oggz_io_init (oggz); + + oggz->io->read = read; + oggz->io->read_user_handle = user_handle; + + return 0; +} + +void * +oggz_io_get_read_user_handle (OGGZ * oggz) +{ + if (oggz == NULL) return NULL; + if (oggz->file != NULL) return NULL; + + if (oggz->io == NULL) return NULL; + + return oggz->io->read_user_handle; +} + +int +oggz_io_set_write (OGGZ * oggz, OggzIOWrite write, void * user_handle) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + if (oggz->file != NULL) return OGGZ_ERR_INVALID; + + if (oggz->io == NULL) oggz_io_init (oggz); + + oggz->io->write = write; + oggz->io->write_user_handle = user_handle; + + return 0; +} + +void * +oggz_io_get_write_user_handle (OGGZ * oggz) +{ + if (oggz == NULL) return NULL; + if (oggz->file != NULL) return NULL; + + if (oggz->io == NULL) return NULL; + + return oggz->io->write_user_handle; +} + +int +oggz_io_set_seek (OGGZ * oggz, OggzIOSeek seek, void * user_handle) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + if (oggz->file != NULL) return OGGZ_ERR_INVALID; + + if (oggz->io == NULL) oggz_io_init (oggz); + + oggz->io->seek = seek; + oggz->io->seek_user_handle = user_handle; + + return 0; +} + +void * +oggz_io_get_seek_user_handle (OGGZ * oggz) +{ + if (oggz == NULL) return NULL; + if (oggz->file != NULL) return NULL; + + if (oggz->io == NULL) return NULL; + + return oggz->io->seek_user_handle; +} + +int +oggz_io_set_tell (OGGZ * oggz, OggzIOTell tell, void * user_handle) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + if (oggz->file != NULL) return OGGZ_ERR_INVALID; + + if (oggz->io == NULL) oggz_io_init (oggz); + + oggz->io->tell = tell; + oggz->io->tell_user_handle = user_handle; + + return 0; +} + +void * +oggz_io_get_tell_user_handle (OGGZ * oggz) +{ + if (oggz == NULL) return NULL; + if (oggz->file != NULL) return NULL; + + if (oggz->io == NULL) return NULL; + + return oggz->io->tell_user_handle; +} + +int +oggz_io_set_flush (OGGZ * oggz, OggzIOFlush flush, void * user_handle) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + if (oggz->file != NULL) return OGGZ_ERR_INVALID; + + if (oggz->io == NULL) oggz_io_init (oggz); + + oggz->io->flush = flush; + oggz->io->flush_user_handle = user_handle; + + return 0; +} + +void * +oggz_io_get_flush_user_handle (OGGZ * oggz) +{ + if (oggz == NULL) return NULL; + if (oggz->file != NULL) return NULL; + + if (oggz->io == NULL) return NULL; + + return oggz->io->flush_user_handle; +} diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_macros.h b/src/add-ons/media/plugins/ogg/liboggz/oggz_macros.h new file mode 100644 index 0000000000..edfc2578bc --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_macros.h @@ -0,0 +1,48 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __OGGZ_MACROS_H__ +#define __OGGZ_MACROS_H__ + +#include + +/* 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)) + +#endif /* __OGGZ_MACROS_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_private.h b/src/add-ons/media/plugins/ogg/liboggz/oggz_private.h new file mode 100644 index 0000000000..e5416f0095 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_private.h @@ -0,0 +1,222 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __OGGZ_PRIVATE_H__ +#define __OGGZ_PRIVATE_H__ + +#include +#include + +#include +#include + +#include "oggz_macros.h" +#include "oggz_vector.h" + +typedef struct _OGGZ OGGZ; +typedef struct _OggzIO OggzIO; +typedef struct _OggzReader OggzReader; +typedef struct _OggzWriter OggzWriter; + +typedef int (*OggzReadPacket) (OGGZ * oggz, ogg_packet * op, long serialno, + void * user_data); + +typedef ogg_int64_t (*OggzMetric) (OGGZ * oggz, long serialno, + ogg_int64_t granulepos, + void * user_data); + +typedef int (*OggzOrder) (OGGZ * oggz, ogg_packet * op, void * target, + void * user_data); + +typedef int (*OggzWriteHungry) (OGGZ * oggz, int empty, void * user_data); + +/* oggz_io */ +typedef size_t (*OggzIORead) (void * user_handle, void * buf, size_t n); +typedef size_t (*OggzIOWrite) (void * user_handle, void * buf, size_t n); +typedef int (*OggzIOSeek) (void * user_handle, long offset, int whence); +typedef long (*OggzIOTell) (void * user_handle); +typedef int (*OggzIOFlush) (void * user_handle); + +typedef struct { + ogg_stream_state ogg_stream; + + /* non b_o_s packet has been written (not just queued) */ + int delivered_non_b_o_s; + + int b_o_s; /* beginning of stream */ + int e_o_s; /* end of stream */ + ogg_int64_t granulepos; + ogg_int64_t packetno; + + OggzMetric metric; + void * metric_user_data; + int metric_internal; + + OggzOrder order; + void * order_user_data; + + OggzReadPacket read_packet; + void * read_user_data; +} oggz_stream_t; + +struct _OggzReader { + ogg_sync_state ogg_sync; + + /* XXX: these two can prolly be removed again :) */ + ogg_stream_state ogg_stream; + long current_serialno; + + OggzReadPacket read_packet; + void * read_user_data; + + ogg_int64_t current_unit; + +#if 0 + off_t offset_page_end; /* offset of end of current page */ +#endif +}; + +/** + * Bundle a packet with the stream it is being queued for; used in + * the packet_queue vector + */ +typedef struct { + ogg_packet op; + oggz_stream_t * stream; + int flush; + int * guard; +} oggz_writer_packet_t; + +enum oggz_writer_state { + OGGZ_MAKING_PACKETS = 0, + OGGZ_WRITING_PAGES = 1 +}; + +struct _OggzWriter { + oggz_writer_packet_t * next_zpacket; /* stashed in case of FLUSH_BEFORE */ + OggzVector * packet_queue; + + OggzWriteHungry hungry; + void * hungry_user_data; + int hungry_only_when_empty; + + int writing; /* already mid-write; check for recursive writes */ + int state; /* OGGZ_MAKING_PACKETS or OGGZ_WRITING_PAGES */ + + int flushing; /* whether current packet is being flushed or just paged out */ + + int eog; /* end of page */ +#if 0 + int eop; /* end of packet */ +#endif + int eos; /* end of stream */ + + oggz_writer_packet_t * current_zpacket; + + int packet_offset; /* n bytes already copied out of current packet */ + int page_offset; /* n bytes already copied out of current page */ + + ogg_stream_state * current_stream; +}; + +struct _OggzIO { + OggzIORead read; + void * read_user_handle; + + OggzIOWrite write; + void * write_user_handle; + + OggzIOSeek seek; + void * seek_user_handle; + + OggzIOTell tell; + void * tell_user_handle; + + OggzIOFlush flush; + void * flush_user_handle; +}; + +struct _OGGZ { + int flags; + FILE * file; + OggzIO * io; + + ogg_packet current_packet; + ogg_page current_page; + + off_t offset; /* offset of current page start */ + off_t offset_data_begin; /* offset of unit 0 page start */ + + OggzVector * streams; + int all_at_eos; /* all streams are at eos */ + + OggzMetric metric; + void * metric_user_data; + int metric_internal; + + OggzOrder order; + void * order_user_data; + + union { + OggzReader reader; + OggzWriter writer; + } x; +}; + +OGGZ * oggz_read_init (OGGZ * oggz); +OGGZ * oggz_read_close (OGGZ * oggz); + +OGGZ * oggz_write_init (OGGZ * oggz); +OGGZ * oggz_write_close (OGGZ * oggz); + +oggz_stream_t * oggz_get_stream (OGGZ * oggz, long serialno); +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_has_metrics (OGGZ * oggz); + +int oggz_purge (OGGZ * oggz); + +int oggz_auto (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data); + +/* oggz_io */ +size_t oggz_io_read (OGGZ * oggz, void * buf, size_t n); +size_t oggz_io_write (OGGZ * oggz, void * buf, size_t n); +int oggz_io_seek (OGGZ * oggz, long offset, int whence); +long oggz_io_tell (OGGZ * oggz); +int oggz_io_flush (OGGZ * oggz); + +#endif /* __OGGZ_PRIVATE_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_read.c b/src/add-ons/media/plugins/ogg/liboggz/oggz_read.c new file mode 100644 index 0000000000..471957a12e --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_read.c @@ -0,0 +1,1154 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * oggz_read.c + * + * Conrad Parker + */ + +#include "config.h" + +#if OGGZ_CONFIG_READ + +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "oggz_compat.h" +#include "oggz_private.h" + +/*#define DEBUG*/ + +/*#define DEBUG_BY_READING_PAGES*/ + +#define CHUNKSIZE 8500 + +#define oggz_off_t long + +OGGZ * +oggz_read_init (OGGZ * oggz) +{ + OggzReader * reader = &oggz->x.reader; + + ogg_sync_init (&reader->ogg_sync); + ogg_stream_init (&reader->ogg_stream, (int)-1); + reader->current_serialno = -1; + + reader->read_packet = NULL; + reader->read_user_data = NULL; + + reader->current_unit = 0; + + return oggz; +} + +OGGZ * +oggz_read_close (OGGZ * oggz) +{ + OggzReader * reader = &oggz->x.reader; + + ogg_sync_clear (&reader->ogg_sync); + + return oggz; +} + +int +oggz_set_read_callback (OGGZ * oggz, long serialno, + OggzReadPacket read_packet, void * user_data) +{ + OggzReader * reader; + oggz_stream_t * stream; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + reader = &oggz->x.reader; + + if (oggz->flags & OGGZ_WRITE) { + return OGGZ_ERR_INVALID; + } + + if (serialno == -1) { + reader->read_packet = read_packet; + 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; + } + + return 0; +} + +/* + * oggz_get_next_page_7 (oggz, og, do_read) + * + * MODIFIED COPY OF CODE FROM BELOW SEEKING STUFF + * + * retrieves the next page. + * returns >= 0 if found; return value is offset of page start + * returns -1 on error + * returns -2 if EOF was encountered + */ +static oggz_off_t +oggz_get_next_page_7 (OGGZ * oggz, ogg_page * og) +{ + OggzReader * reader = &oggz->x.reader; +#if _UNMODIFIED + char * buffer; +#endif + long bytes = 0, more; + oggz_off_t page_offset = 0, ret; + int found = 0; + + do { + more = ogg_sync_pageseek (&reader->ogg_sync, og); + + if (more == 0) { + page_offset = 0; +#if _UMMODIFIED_ + buffer = ogg_sync_buffer (&reader->ogg_sync, CHUNKSIZE); + if ((bytes = oggz_io_read (oggz, buffer, CHUNKSIZE)) == 0) { +#if 0 + if (ferror (oggz->file)) { + oggz_set_error (oggz, OGGZ_ERR_SYSTEM); + return -1; + } +#endif + } + + if (bytes == 0) { + return -2; + } + + ogg_sync_wrote(&reader->ogg_sync, bytes); +#else + return -2; +#endif + } else if (more < 0) { +#ifdef DEBUG + printf ("get_next_page: skipped %ld bytes\n", -more); +#endif + page_offset -= more; + } else { +#ifdef DEBUG + printf ("get_next_page: page has %ld bytes\n", more); +#endif + found = 1; + } + + } while (!found); + + /* Calculate the byte offset of the page which was found */ + if (bytes > 0) { + oggz->offset = oggz_io_tell (oggz) - bytes + page_offset; + ret = oggz->offset; + } else { + /* didn't need to do any reading -- accumulate the page_offset */ + ret = oggz->offset + page_offset; + oggz->offset += page_offset + more; + } + + return ret; +} + +static int +oggz_read_sync (OGGZ * oggz) +{ + OggzReader * reader = &oggz->x.reader; + + oggz_stream_t * stream; + ogg_stream_state * os; + ogg_packet * op; + long serialno; + + ogg_packet packet; + ogg_page og; + + int cb_ret = 0; + + /*os = &reader->ogg_stream;*/ + op = &packet; + + /* handle one packet. Try to fetch it from current stream state */ + /* extract packets from page */ + while(cb_ret == 0){ + + if (reader->current_serialno != -1) { + /* process a packet if we can. If the machine isn't loaded, + neither is a page */ + while(cb_ret == 0) { + ogg_int64_t granulepos; + int result; + + serialno = reader->current_serialno; + + 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 */ + return -7; + } + } + os = &stream->ogg_stream; + + result = ogg_stream_packetout(os, op); + + if(result == -1) { + /* hole in the data. */ + return -7; + } + + if(result > 0){ + /* 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); + } +#ifndef DEBUG_BY_READING_PAGES + if (stream->read_packet) { + cb_ret = + stream->read_packet (oggz, op, serialno, stream->read_user_data); + } else if (reader->read_packet) { + cb_ret = + reader->read_packet (oggz, op, serialno, reader->read_user_data); + } +#endif /* DEBUG_BY_READING_PAGES */ + } + else + break; + } + } + + /* If we've got a stop already, don't read more data in */ + if (cb_ret != 0) return cb_ret; + + if(oggz_get_next_page_7 (oggz, &og) < 0) + return -404; /* eof. leave unitialized */ + + serialno = ogg_page_serialno (&og); + reader->current_serialno = serialno; /* XXX: maybe not necessary */ + + 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 */ + return -7; + } + } + os = &stream->ogg_stream; + +#ifdef DEBUG_BY_READING_PAGES + { + ogg_packet op_debug; + op_debug.packet = og.body; + op_debug.bytes = og.body_len; + op_debug.b_o_s = ogg_page_bos (&og); + op_debug.e_o_s = ogg_page_eos (&og); + op_debug.granulepos = ogg_page_granulepos (&og); + op_debug.packetno = ogg_page_packets (&og); + + if (stream->read_packet) { + cb_ret = stream->read_packet (oggz, &op_debug, serialno, + stream->read_user_data); + } else if (reader->read_packet) { + cb_ret = reader->read_packet (oggz, &op_debug, serialno, + reader->read_user_data); + } + } +#endif + +#if 0 + /* bitrate tracking; add the header's bytes here, the body bytes + are done by packet above */ + vf->bittrack+=og.header_len*8; +#endif + + ogg_stream_pagein(os, &og); + } + + return cb_ret; +} + +long +oggz_read (OGGZ * oggz, long n) +{ + OggzReader * reader; + char * buffer; + long bytes, bytes_read = 1, remaining = n, nread = 0; + int cb_ret = 0; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (oggz->flags & OGGZ_WRITE) { + return OGGZ_ERR_INVALID; + } + + reader = &oggz->x.reader; + + cb_ret = oggz_read_sync (oggz); + + /* If there's nothing to read yet, don't flag an error */ + if (reader->current_unit == 0 && cb_ret == -404) cb_ret = 0; + + while (cb_ret != -1 && cb_ret != 1 && bytes_read > 0 && remaining > 0) { + bytes = MIN (remaining, 4096); + buffer = ogg_sync_buffer (&reader->ogg_sync, bytes); + if ((bytes_read = (long) oggz_io_read (oggz, buffer, bytes)) == 0) { + /* schyeah! */ + } + if (bytes_read == OGGZ_ERR_SYSTEM) { + return OGGZ_ERR_SYSTEM; + } + + ogg_sync_wrote (&reader->ogg_sync, bytes_read); + + remaining -= bytes_read; + nread += bytes_read; + + cb_ret = oggz_read_sync (oggz); + } + + if (cb_ret == -1) oggz_purge (oggz); + + return nread; +} + +/* generic */ +long +oggz_read_input (OGGZ * oggz, unsigned char * buf, long n) +{ + OggzReader * reader; + char * buffer; + long bytes, remaining = n, nread = 0; + int cb_ret = 0; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (oggz->flags & OGGZ_WRITE) { + return OGGZ_ERR_INVALID; + } + + reader = &oggz->x.reader; + + cb_ret = oggz_read_sync (oggz); + + /* If there's nothing to read yet, don't flag an error */ + if (reader->current_unit == 0 && cb_ret == -404) cb_ret = 0; + + while (cb_ret != -1 && cb_ret != 1 && /* !oggz->eos && */ remaining > 0) { + bytes = MIN (remaining, 4096); + buffer = ogg_sync_buffer (&reader->ogg_sync, bytes); + memcpy (buffer, buf, bytes); + ogg_sync_wrote (&reader->ogg_sync, bytes); + + buf += bytes; + remaining -= bytes; + nread += bytes; + + cb_ret = oggz_read_sync (oggz); + } + + if (cb_ret == -1) oggz_purge (oggz); + + return nread; +} + +/* oggz_seek() (and oggz_purge()) related functions from here down */ + +/* + * The typical usage is: + * + * oggz_set_data_start (oggz, oggz_tell (oggz)); + */ +int +oggz_set_data_start (OGGZ * oggz, oggz_off_t offset) +{ + if (oggz == NULL) return -1; + + if (offset < 0) return -1; + + oggz->offset_data_begin = offset; + + return 0; +} + +static oggz_off_t +oggz_tell_raw (OGGZ * oggz) +{ + oggz_off_t offset_at; + + offset_at = oggz_io_tell (oggz); + + return offset_at; +} + +/* + * seeks and syncs + */ +static oggz_off_t +oggz_seek_raw (OGGZ * oggz, oggz_off_t offset, int whence) +{ + OggzReader * reader = &oggz->x.reader; + oggz_off_t offset_at; + + if (oggz_io_seek (oggz, offset, whence) == -1) { + return -1; + } + + offset_at = oggz_io_tell (oggz); + + oggz->offset = offset_at; + + ogg_sync_reset (&reader->ogg_sync); + + return offset_at; +} + +static int +oggz_stream_reset (void * data) +{ + oggz_stream_t * stream = (oggz_stream_t *) data; + + if (stream->ogg_stream.serialno != -1) { + ogg_stream_reset (&stream->ogg_stream); + } + + return 0; +} + +static void +oggz_reset_streams (OGGZ * oggz) +{ + oggz_vector_foreach (oggz->streams, oggz_stream_reset); +} + +static long +oggz_reset_seek (OGGZ * oggz, oggz_off_t offset, ogg_int64_t unit, int whence) +{ + OggzReader * reader = &oggz->x.reader; + + oggz_off_t offset_at; + + offset_at = oggz_seek_raw (oggz, offset, whence); + if (offset_at == -1) return -1; + + oggz->offset = offset_at; + +#ifdef DEBUG + printf ("reset to %ld\n", offset_at); +#endif + + if (unit != -1) reader->current_unit = unit; + + return offset_at; +} + +static long +oggz_reset (OGGZ * oggz, oggz_off_t offset, ogg_int64_t unit, int whence) +{ + oggz_reset_streams (oggz); + return oggz_reset_seek (oggz, offset, unit, whence); +} + +int +oggz_purge (OGGZ * oggz) +{ + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (oggz->flags & OGGZ_WRITE) { + return OGGZ_ERR_INVALID; + } + + oggz_reset_streams (oggz); + + if (oggz->file && oggz_reset (oggz, oggz->offset, -1, SEEK_SET) < 0) { + return OGGZ_ERR_SYSTEM; + } + + return 0; +} + +/* + * oggz_get_next_page (oggz, og, do_read) + * + * retrieves the next page. + * returns >= 0 if found; return value is offset of page start + * returns -1 on error + * returns -2 if EOF was encountered + */ +static oggz_off_t +oggz_get_next_page (OGGZ * oggz, ogg_page * og) +{ + OggzReader * reader = &oggz->x.reader; + char * buffer; + long bytes = 0, more; + oggz_off_t page_offset = 0, ret; + int found = 0; + + do { + more = ogg_sync_pageseek (&reader->ogg_sync, og); + + if (more == 0) { + page_offset = 0; + + buffer = ogg_sync_buffer (&reader->ogg_sync, CHUNKSIZE); + if ((bytes = (long) oggz_io_read (oggz, buffer, CHUNKSIZE)) == 0) { + /* schyeah! */ + } + if (bytes == OGGZ_ERR_SYSTEM) { + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ + return -1; + } + + if (bytes == 0) { + return -2; + } else if (oggz->file && feof (oggz->file)) { + clearerr (oggz->file); + return -2; + } + + ogg_sync_wrote(&reader->ogg_sync, bytes); + + } else if (more < 0) { +#ifdef DEBUG + printf ("get_next_page: skipped %ld bytes\n", -more); +#endif + page_offset -= more; + } else { +#ifdef DEBUG + printf ("get_next_page: page has %ld bytes\n", more); +#endif + found = 1; + } + + } while (!found); + + /* Calculate the byte offset of the page which was found */ + if (bytes > 0) { + oggz->offset = oggz_tell_raw (oggz) - bytes + page_offset; + ret = oggz->offset; + } else { + /* didn't need to do any reading -- accumulate the page_offset */ + ret = oggz->offset + page_offset; + oggz->offset += page_offset + more; + } + + return ret; +} + +static oggz_off_t +oggz_get_next_start_page (OGGZ * oggz, ogg_page * og) +{ + oggz_off_t page_offset; + int found = 0; + + while (!found) { + page_offset = oggz_get_next_page (oggz, og); + + /* Return this value if one of the following conditions is met: + * + * page_offset < 0 : error or EOF + * page_offset == 0 : start of stream + * !ogg_page_continued : start of page + */ + if (page_offset <= 0 || !ogg_page_continued (og)) + found = 1; + } + + return page_offset; +} + +static oggz_off_t +oggz_get_prev_start_page (OGGZ * oggz, ogg_page * og, + ogg_int64_t * granule, long * serialno) +{ + oggz_off_t offset_at, offset_start; + oggz_off_t page_offset, prev_offset = 0; + long granule_at = -1; + +#if 0 + offset_at = oggz_tell_raw (oggz); + if (offset_at == -1) return -1; +#else + offset_at = oggz->offset; +#endif + + offset_start = offset_at; + + do { + + offset_start = offset_at - CHUNKSIZE; + if (offset_start < 0) offset_start = 0; + + offset_start = oggz_seek_raw (oggz, offset_start, SEEK_SET); + if (offset_start == -1) return -1; + +#ifdef DEBUG + + printf ("[A] offset_at: @%ld\toffset_start: @%ld\n", + offset_at, offset_start); + + printf ("*** get_prev_start_page: seeked to %ld\n", offset_start); +#endif + + page_offset = 0; + + do { + prev_offset = page_offset; + + page_offset = oggz_get_next_start_page (oggz, og); + if (page_offset == -1) return -1; + if (page_offset == -2) break; + + granule_at = (long)ogg_page_granulepos (og); + +#ifdef DEBUG + printf ("\tGOT page (%ld) @%ld\tat @%ld\n", granule_at, + page_offset, offset_at); +#endif + + /* Need to stash the granule and serialno of this page because og + * will be overwritten by the time we realise this was the desired + * prev page */ + if (page_offset >= 0 && page_offset < offset_at) { + *granule = granule_at; + *serialno = ogg_page_serialno (og); + } + + } while (page_offset >= 0 && page_offset < offset_at); + +#ifdef DEBUG + printf ("[B] offset_at: @%ld\toffset_start: @%ld\n" + "prev_offset: @%ld\tpage_offset: @%ld\n", + offset_at, offset_start, prev_offset, page_offset); +#endif + /* reset the file offset */ + offset_at = offset_start; + + } while (offset_at > 0 && prev_offset == 0); + + if (offset_at > 0) + return prev_offset; + else + return -1; +} + +static oggz_off_t +oggz_scan_for_page (OGGZ * oggz, ogg_page * og, ogg_int64_t unit_target, + oggz_off_t offset_begin, oggz_off_t offset_end) +{ + 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; + +#ifdef DEBUG + printf (" SCANNING from %ld...", offset_begin); +#endif + + while (1) { + offset_at = oggz_seek_raw (oggz, offset_begin, SEEK_SET); + if (offset_at == -1) return -1; + +#ifdef DEBUG + printf (" scan @%ld\n", offset_at); +#endif + + offset_next = oggz_get_next_start_page (oggz, og); + + if (offset_next < 0) { + return offset_next; + } + + if (offset_next == 0 && offset_begin != 0) { +#ifdef DEBUG + printf (" ... scanned past EOF\n"); +#endif + return -1; + } + if (offset_next > offset_end) { +#ifdef DEBUG + printf (" ... scanned to page %ld\n", (long)ogg_page_granulepos (og)); +#endif + if (offset_prev != -1) { + offset_at = oggz_seek_raw (oggz, offset_prev, SEEK_SET); + if (offset_at == -1) return -1; + + offset_next = oggz_get_next_start_page (oggz, og); + if (offset_next < 0) return offset_next; + + serialno = ogg_page_serialno (og); + granule_at = ogg_page_granulepos (og); + unit_at = oggz_get_unit (oggz, serialno, granule_at); + + return offset_at; + } else { + return -1; + } + } + + offset_at = offset_next; + + serialno = ogg_page_serialno (og); + granule_at = ogg_page_granulepos (og); + unit_at = oggz_get_unit (oggz, serialno, granule_at); + + if (unit_at < unit_target) { +#ifdef DEBUG + printf (" scan: (%ld) < (%ld)\n", unit_at, unit_target); +#endif + offset_prev = offset_next; + offset_begin = offset_next+1; + } else if (unit_at > unit_target) { +#ifdef DEBUG + printf (" scan: (%ld) > (%ld)\n", unit_at, unit_target); +#endif +#if 0 + /* hole ? */ + offset_at = oggz_seek_raw (oggz, offset_begin, SEEK_SET); + if (offset_at == -1) return -1; + + offset_next = oggz_get_next_start_page (oggz, og); + if (offset_next < 0) return offset_next; + + serialno = ogg_page_serialno (og); + granule_at = ogg_page_granulepos (og); + unit_at = oggz_get_unit (oggz, serialno, granule_at); + + break; +#else + return offset_at; +#endif + } else if (unit_at == unit_target) { +#ifdef DEBUG + printf (" scan: (%ld) == (%ld)\n", unit_at, unit_target); +#endif + break; + } + } + + return offset_at; +} + +static long +oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target) +{ + OggzReader * reader = &oggz->x.reader; + int fd; + struct stat statbuf; + oggz_off_t offset_orig, offset_at, offset_guess; + oggz_off_t offset_begin, offset_end = -1, offset_next; + ogg_int64_t granule_at; + ogg_int64_t unit_at, unit_begin = 0, unit_end = -1; + long serialno; + double guess_ratio; + ogg_page * og; + + if (oggz == NULL) { + return -1; + } + + if (unit_target > 0 && !oggz_has_metrics (oggz)) { + /* No metric defined */ + return -1; + } + + if (oggz->file != NULL) { + if ((fd = fileno (oggz->file)) == -1) { + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ + return -1; + } + + if (fstat (fd, &statbuf) == -1) { + /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/ + return -1; + } + + if (oggz_stat_regular (statbuf.st_mode)) { + offset_end = statbuf.st_size; + } else { + /*oggz_set_error (oggz, OGGZ_ERR_NOSEEK);*/ + + /* XXX: should be able to just carry on and guess, as per io */ + /*return -1;*/ + } + } else { + oggz_off_t offset_save; + + if (oggz->io == NULL || oggz->io->seek == NULL) { + /* No file, and no io seek method */ + return -1; + } + + /* Get the offset of the end by querying the io seek method */ + offset_save = oggz_io_tell (oggz); + if (oggz_io_seek (oggz, 0, SEEK_END) == -1) { + return -1; + } + offset_end = oggz_io_tell (oggz); + if (oggz_io_seek (oggz, offset_save, SEEK_SET) == -1) { + return -1; /* fubar */ + } + } + + if (unit_target == reader->current_unit) { + return (long)reader->current_unit; + } + + if (unit_target == 0) { + offset_at = oggz_reset (oggz, oggz->offset_data_begin, 0, SEEK_SET); + if (offset_at == -1) return -1; + return 0; + } + + offset_at = oggz_tell_raw (oggz); + if (offset_at == -1) return -1; + +#if 0 + offset_orig = offset_at; +#else + offset_orig = oggz->offset; +#endif + + offset_begin = 0; + + unit_at = reader->current_unit; + unit_begin = 0; + unit_end = -1; + + og = &oggz->current_page; + + while (1) { + +#ifdef DEBUG + printf ("oggz_read_seek (%ld): (%ld - %ld) [%ld - %ld]\t", + unit_target, unit_begin, unit_end, offset_begin, offset_end); +#endif + + if (unit_end == -1) { + if (unit_at == unit_begin) { +#ifdef DEBUG + printf ("*G1*"); +#endif + offset_guess = offset_begin + (offset_end - offset_begin)/2; + } else { +#ifdef DEBUG + printf ("*G2*"); +#endif + guess_ratio = + (double)(unit_target - unit_begin) / + (double)(unit_at - unit_begin); + +#ifdef DEBUG + printf ("\nguess_ration %f = (%ld - %ld) / (%ld - %ld)\n", + guess_ratio, unit_target, unit_begin, unit_at, unit_begin); +#endif + + offset_guess = offset_begin + + (oggz_off_t)((offset_at - offset_begin) * guess_ratio); + } + } else if (unit_end <= unit_begin) { +#ifdef DEBUG + printf ("unit_end <= unit_begin\n"); +#endif + break; + } else { +#if 1 + guess_ratio = + (double)(unit_target - unit_begin) / + (double)(unit_end - unit_begin); + + offset_guess = offset_begin + + (oggz_off_t)((offset_end - offset_begin) * guess_ratio); + + /* + if (offset_guess <= offset_begin) { + offset_guess = offset_begin + 1; + } + */ +#else + offset_guess = offset_begin + (offset_end - offset_begin)/2; +#endif + } + +#ifdef DEBUG + printf ("%ld ->", offset_guess); +#endif + + offset_at = oggz_seek_raw (oggz, offset_guess, SEEK_SET); + if (offset_at == -1) { + goto notfound; + } + + offset_next = oggz_get_next_start_page (oggz, og); + +#ifdef DEBUG + printf ("\n"); +#endif + + if (offset_next < 0) { + goto notfound; + } + + if (offset_next > offset_end) { + offset_next = oggz_scan_for_page (oggz, og, unit_target, + offset_begin, offset_end); + + if (offset_next < 0) { + goto notfound; + } + + offset_at = offset_next; + serialno = ogg_page_serialno (og); + granule_at = ogg_page_granulepos (og); + + unit_at = oggz_get_unit (oggz, serialno, granule_at); + + goto found; + } + + offset_at = offset_next; + serialno = ogg_page_serialno (og); + granule_at = ogg_page_granulepos (og); + + unit_at = oggz_get_unit (oggz, serialno, granule_at); + +#ifdef DEBUG + printf ("oggz_read_seek (%ld): got page (%ld) @%ld\n", unit_target, + unit_at, offset_at); +#endif + + if (unit_at < unit_target) { + offset_begin = offset_at; + unit_begin = unit_at; + } else if (unit_at > unit_target) { + offset_end = offset_at-1; + unit_end = unit_at; + } else { + break; + } + } + + found: +#ifdef DEBUG + printf ("FOUND (%ld)\n", unit_at); +#endif + + offset_at = oggz_reset (oggz, offset_at, unit_at, SEEK_SET); + if (offset_at == -1) return -1; + + return (long)reader->current_unit; + + notfound: +#ifdef DEBUG + printf ("NOT FOUND\n"); +#endif + + oggz_reset (oggz, offset_orig, -1, SEEK_SET); + + return -1; +} + +static long +oggz_seek_end (OGGZ * oggz, ogg_int64_t unit_offset) +{ + oggz_off_t offset_orig, offset_at, offset_end; + ogg_int64_t granulepos; + ogg_int64_t unit_end; + long serialno; + ogg_page * og; + + og = &oggz->current_page; + + offset_orig = oggz->offset; + + offset_at = oggz_seek_raw (oggz, 0, SEEK_END); + if (offset_at == -1) return -1; + + offset_end = oggz_get_prev_start_page (oggz, og, &granulepos, &serialno); + + unit_end = oggz_get_unit (oggz, serialno, granulepos); + + if (offset_end < 0) { + oggz_reset (oggz, offset_orig, -1, SEEK_SET); + return -1; + } + +#ifdef DEBUG + printf ("*** oggz_seek_end: found packet (%ld) at @%ld\n", + unit_end, offset_end); +#endif + + return oggz_seek_set (oggz, unit_end + unit_offset); +} + +off_t +oggz_seek (OGGZ * oggz, oggz_off_t offset, int whence) +{ + ogg_int64_t units = -1; + + if (oggz == NULL) return -1; + + if (oggz->flags & OGGZ_WRITE) { + return -1; + } + + if (offset == 0) units = 0; + + return (off_t)oggz_reset (oggz, offset, units, whence); +} + +long +oggz_seek_units (OGGZ * oggz, ogg_int64_t units, int whence) +{ + OggzReader * reader = &oggz->x.reader; + + if (oggz == NULL) return -1; + + if (oggz->flags & OGGZ_WRITE) { + return -1; + } + + if (!oggz_has_metrics (oggz)) { + return -1; + } + + switch (whence) { + case SEEK_SET: + return oggz_seek_set (oggz, units); + break; + case SEEK_CUR: + units += reader->current_unit; + return oggz_seek_set (oggz, units); + break; + case SEEK_END: + return oggz_seek_end (oggz, units); + break; + default: + /*oggz_set_error (oggz, OGGZ_EINVALID);*/ + return -1; + break; + } +} + +long +oggz_seek_byorder (OGGZ * oggz, void * target) +{ + return -1; +} + +long +oggz_seek_packets (OGGZ * oggz, long serialno, long packets, int whence) +{ + return -1; +} + +#else /* OGGZ_CONFIG_READ */ + +#include +#include "oggz_private.h" + +int +oggz_set_read_callback (OGGZ * oggz, long serialno, + OggzReadPacket read_packet, void * user_data) +{ + return OGGZ_ERR_DISABLED; +} + +long +oggz_read (OGGZ * oggz, long n) +{ + return OGGZ_ERR_DISABLED; +} + +long +oggz_read_input (OGGZ * oggz, unsigned char * buf, long n) +{ + return OGGZ_ERR_DISABLED; +} + +off_t +oggz_seek (OGGZ * oggz, oggz_off_t offset, int whence) +{ + return OGGZ_ERR_DISABLED; +} + +long +oggz_seek_units (OGGZ * oggz, ogg_int64_t units, int whence) +{ + return OGGZ_ERR_DISABLED; +} + +long +oggz_seek_byorder (OGGZ * oggz, void * target) +{ + return OGGZ_ERR_DISABLED; +} + +long +oggz_seek_packets (OGGZ * oggz, long serialno, long packets, int whence) +{ + return OGGZ_ERR_DISABLED; +} + +#endif diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_table.c b/src/add-ons/media/plugins/ogg/liboggz/oggz_table.c new file mode 100644 index 0000000000..2ea54b3254 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_table.c @@ -0,0 +1,122 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "oggz_macros.h" +#include "oggz_vector.h" + +typedef struct _OggzTable OggzTable; + +struct _OggzTable { + OggzVector * keys; + OggzVector * data; +}; + +OggzTable * +oggz_table_new (void) +{ + OggzTable * table; + + table = oggz_malloc (sizeof (OggzTable)); + table->keys = oggz_vector_new (); + table->data = oggz_vector_new (); + + return table; +} + +void +oggz_table_delete (OggzTable * table) +{ + if (table == NULL) return; + + oggz_vector_delete (table->keys); + oggz_vector_delete (table->data); + oggz_free (table); +} + +void * +oggz_table_lookup (OggzTable * table, long key) +{ + int i, size; + + size = oggz_vector_size (table->keys); + for (i = 0; i < size; i++) { + if (oggz_vector_nth_l (table->keys, i) == key) { + return oggz_vector_nth_p (table->data, i); + } + } + + return NULL; +} + +void * +oggz_table_insert (OggzTable * table, long key, void * data) +{ + void * old_data; + + if ((old_data = oggz_table_lookup (table, key)) != NULL) { + if (oggz_vector_remove_l (table->keys, key) == NULL) + return NULL; + + if (oggz_vector_remove_p (table->data, old_data) == NULL) { + /* XXX: This error condition can only happen if the previous + * removal succeeded, and this removal failed, ie. there was + * an error reallocing table->data->data downwards. */ + return NULL; + } + } + + if (oggz_vector_insert_l (table->keys, key) == -1) + return NULL; + + if (oggz_vector_insert_p (table->data, data) == NULL) { + oggz_vector_remove_l (table->keys, key); + return NULL; + } + + return data; +} + +int +oggz_table_size (OggzTable * table) +{ + if (table == NULL) return 0; + return oggz_vector_size (table->data); +} + +void * +oggz_table_nth (OggzTable * table, int n, long * key) +{ + if (table == NULL) return NULL; + if (key) *key = oggz_vector_nth_l (table->keys, n); + return oggz_vector_nth_p (table->data, n); +} diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_vector.c b/src/add-ons/media/plugins/ogg/liboggz/oggz_vector.c new file mode 100644 index 0000000000..6ffc0d4346 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_vector.c @@ -0,0 +1,399 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#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; +}; + +/* + * 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. + * + * 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) + */ + +OggzVector * +oggz_vector_new (void) +{ + OggzVector * vector; + + vector = oggz_malloc (sizeof (OggzVector)); + + vector->max_elements = 0; + vector->nr_elements = 0; + vector->data = NULL; + vector->compare = NULL; + vector->compare_user_data = NULL; + + return vector; +} + +static void +oggz_vector_clear (OggzVector * vector) +{ + oggz_free (vector->data); + vector->data = NULL; + vector->nr_elements = 0; + 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) +{ + void * data; + int i; + + for (i = 0; i < vector->nr_elements; i++) { + data = vector->data[i].p; + if (func (data, serialno)) + return data; + } + + return NULL; +} + +int +oggz_vector_foreach (OggzVector * vector, OggzFunc func) +{ + int i; + + for (i = 0; i < vector->nr_elements; i++) { + func (vector->data[i].p); + } + + return 0; +} + +static void +_array_swap (oggz_data_t v[], int i, int j) +{ + void * t; + + t = v[i].p; + v[i].p = v[j].p; + v[j].p = t; +} + +/** + * 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 + * \pre The vector has just had a new element added to its tail + * \pre All elements other than the tail element are already sorted. + */ +static void +oggz_vector_tail_insertion_sort (OggzVector * vector) +{ + int i; + + if (vector->compare == NULL) return; + + for (i = vector->nr_elements-1; i > 0; 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 { + break; + } + } + + return; +} + +static OggzVector * +oggz_vector_grow (OggzVector * vector) +{ + void * new_elements; + int new_max_elements; + + vector->nr_elements++; + + if (vector->nr_elements > vector->max_elements) { + if (vector->max_elements == 0) { + new_max_elements = 1; + } else { + 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) { + vector->nr_elements--; + return NULL; + } + + vector->max_elements = new_max_elements; + vector->data = new_elements; + } + + 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; + 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].p, v[left].p, vector->compare_user_data) < 0) + _array_swap (v, ++last, i); + } + _array_swap (v, left, last); + oggz_vector_qsort (vector, left, last-1); + oggz_vector_qsort (vector, last+1, right); +} + +int +oggz_vector_set_cmp (OggzVector * vector, OggzCmpFunc compare, + void * user_data) +{ + vector->compare = compare; + vector->compare_user_data = user_data; + + if (compare) { + oggz_vector_qsort (vector, 0, vector->nr_elements-1); + } + + 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].p; + +#if 0 + vector->nr_elements--; + + if (vector->nr_elements == 0) { + oggz_vector_clear (vector); + } else { +#if 0 + memmove (vector->data, &vector->data[1], + vector->nr_elements * sizeof (void *)); +#else + { + int i; + for (i = 0; i < vector->nr_elements; i++) { + vector->data[i].p = vector->data[i+1].p; + } + } +#endif + 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) { + vector->max_elements = new_max_elements; + vector->data = new_elements; + } + } + + } +#else + + oggz_vector_remove_nth (vector, 0); + +#endif + + return data; + +} diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_vector.h b/src/add-ons/media/plugins/ogg/liboggz/oggz_vector.h new file mode 100644 index 0000000000..4583f4ca1e --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_vector.h @@ -0,0 +1,101 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __OGGZ_VECTOR_H__ +#define __OGGZ_VECTOR_H__ + +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); + +OggzVector * +oggz_vector_new (void); + +void +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 + * to the tail. + * \param vector An OggzVector + * \param data The new element to add + * \retval data If the element was successfully added + * \retval NULL If adding the element failed due to a realloc() error + */ +void * +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, + void * user_data); + +void * +oggz_vector_pop (OggzVector * vector); + +#endif /* __OGGZ_VECTOR_H__ */ diff --git a/src/add-ons/media/plugins/ogg/liboggz/oggz_write.c b/src/add-ons/media/plugins/ogg/liboggz/oggz_write.c new file mode 100644 index 0000000000..f8e5d7f80d --- /dev/null +++ b/src/add-ons/media/plugins/ogg/liboggz/oggz_write.c @@ -0,0 +1,749 @@ +/* + Copyright (C) 2003 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) Australia + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of CSIRO Australia nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" + +#if OGGZ_CONFIG_WRITE + +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "oggz_private.h" +#include "oggz_vector.h" + +/* #define DEBUG */ + +/* #define ALWAYS_FLUSH */ + +/* #define ZPACKET_CMP */ + +#ifdef ZPACKET_CMP +static int +oggz_zpacket_cmp (oggz_writer_packet_t * a, oggz_writer_packet_t * b, + void * user_data) +{ + OGGZ * oggz = (OGGZ *)user_data; + long serialno_a, serialno_b; + ogg_int64_t unit_a, unit_b; + + serialno_a = a->stream->ogg_stream.serialno; + serialno_b = b->stream->ogg_stream.serialno; + + unit_a = oggz_get_unit (oggz, serialno_a, a->op.granulepos); + unit_b = oggz_get_unit (oggz, serialno_b, b->op.granulepos); + + if (unit_a < unit_b) return -1; + else return (unit_a > unit_b); +} +#endif + +OGGZ * +oggz_write_init (OGGZ * oggz) +{ + OggzWriter * writer = &oggz->x.writer; + + writer->next_zpacket = NULL; + + writer->packet_queue = oggz_vector_new (); + +#ifdef ZPACKET_CMP + /* XXX: comparison function should only kick in when a metric is set */ + oggz_vector_set_cmp (writer->packet_queue, + (OggzCmpFunc)oggz_zpacket_cmp, oggz); +#endif + + writer->hungry = NULL; + writer->hungry_user_data = NULL; + writer->hungry_only_when_empty = 0; + + writer->writing = 0; + writer->state = OGGZ_MAKING_PACKETS; + + writer->flushing = 0; +#if 0 + writer->eog = 1; + writer->eop = 1; /* init ready to start next packet */ +#endif + writer->eos = 0; + + writer->current_zpacket = NULL; + + writer->packet_offset = 0; + writer->page_offset = 0; + + return oggz; +} + +static int +oggz_writer_packet_free (oggz_writer_packet_t * zpacket) +{ + if (!zpacket) return 0; + + if (zpacket->guard) { + /* managed by user; flag guard */ + *zpacket->guard = 1; + } else { + /* managed by oggz; free copied data */ + oggz_free (zpacket->op.packet); + } + oggz_free (zpacket); + + return 0; +} + +OGGZ * +oggz_write_close (OGGZ * oggz) +{ + OggzWriter * writer = &oggz->x.writer; + + oggz_vector_foreach (writer->packet_queue, + (OggzFunc)oggz_writer_packet_free); + oggz_vector_delete (writer->packet_queue); + + return oggz; +} + +/******** Packet queueing ********/ + +int +oggz_write_set_hungry_callback (OGGZ * oggz, OggzWriteHungry hungry, + int only_when_empty, void * user_data) +{ + OggzWriter * writer; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (!(oggz->flags & OGGZ_WRITE)) { + return OGGZ_ERR_INVALID; + } + + writer = &oggz->x.writer; + + writer->hungry = hungry; + writer->hungry_user_data = user_data; + writer->hungry_only_when_empty = only_when_empty; + + return 0; +} + +int +oggz_write_feed (OGGZ * oggz, ogg_packet * op, long serialno, int flush, + int * guard) +{ + OggzWriter * writer; + oggz_stream_t * stream; + oggz_writer_packet_t * packet; + ogg_packet * new_op; + unsigned char * new_buf; + int b_o_s, e_o_s, bos_auto; + int strict; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (!(oggz->flags & OGGZ_WRITE)) { + return OGGZ_ERR_INVALID; + } + + writer = &oggz->x.writer; + + if (guard && *guard != 0) return OGGZ_ERR_BAD_GUARD; + +#ifdef DEBUG + printf ("oggz_write_feed: (%010ld) FLUSH: %d\n", serialno, flush); +#endif + + /* Cache strict */ + strict = !(oggz->flags & OGGZ_NONSTRICT); + + /* Set bos, eos to canonical values */ + bos_auto = (op->b_o_s == -1) ? 1 : 0; + b_o_s = op->b_o_s ? 1 : 0; + e_o_s = op->e_o_s ? 1 : 0; + + stream = oggz_get_stream (oggz, serialno); + if (stream == NULL) { + if (bos_auto) b_o_s = 1; + + if (strict && b_o_s && !oggz_get_bos (oggz, -1)) { + return OGGZ_ERR_BOS; + } + + if (b_o_s || !strict) { + stream = oggz_add_stream (oggz, serialno); + } else { + return OGGZ_ERR_BAD_SERIALNO; + } + } else { + if (bos_auto) b_o_s = 0; + + if (strict && stream->e_o_s) + return OGGZ_ERR_EOS; + } + + /* Check packet against mapping restrictions */ + if (strict) { + if (op->bytes < 0) return OGGZ_ERR_BAD_BYTES; + if (b_o_s != stream->b_o_s) return OGGZ_ERR_BAD_B_O_S; + + if (op->granulepos != -1 && op->granulepos < stream->granulepos) + return OGGZ_ERR_BAD_GRANULEPOS; + + /* Allow packetno == -1 to indicate oggz should fill it in; otherwise: + * if op is the first packet in the stream, initialize the stream's + * packetno to the given one, otherwise check it for strictness */ + if (op->packetno != -1) { + if (b_o_s) { + stream->packetno = op->packetno; + } else if (op->packetno <= stream->packetno) { + return OGGZ_ERR_BAD_PACKETNO; + } + } + } + + /* OK -- Update stream's memory of packet details */ + + stream->b_o_s = 0; /* The stream is henceforth no longer at bos */ + stream->e_o_s = e_o_s; /* We believe the eos value */ + stream->granulepos = op->granulepos; /* and the granulepos */ + + /* If the user specified a packetno of -1, fill it in automatically; + * otherwise, use the user-specified value */ + stream->packetno = (op->packetno != -1) ? op->packetno : stream->packetno+1; + + /* Now set up the packet and add it to the queue */ + if (guard == NULL) { + new_buf = oggz_malloc ((size_t)op->bytes); + memcpy (new_buf, op->packet, (size_t)op->bytes); + } else { + new_buf = op->packet; + } + + packet = oggz_malloc (sizeof (oggz_writer_packet_t)); + + new_op = &packet->op; + new_op->packet = new_buf; + new_op->bytes = op->bytes; + new_op->b_o_s = op->b_o_s; + new_op->e_o_s = op->e_o_s; + new_op->granulepos = op->granulepos; + new_op->packetno = stream->packetno; + + packet->stream = stream; + packet->flush = flush; + packet->guard = guard; + +#ifdef DEBUG + printf ("oggz_write_feed: made packet bos %ld eos %ld (%ld bytes) FLUSH: %d\n", + new_op->b_o_s, new_op->e_o_s, new_op->bytes, packet->flush); +#endif + + if (oggz_vector_insert_p (writer->packet_queue, packet) == NULL) { + oggz_free (packet); + if (!guard) oggz_free (new_buf); + return -1; + } + + return 0; +} + +/******** Page creation ********/ + +/* + * + + ____ __________________\___ ____ + / \ / Page ready / \ / \ + | \ /=========\ /=========\ / | + Page | || Make || || Write || | Page + !ready V || packet || || page || V ready + | / \=========/ \=========/ \ | + \____/ \___/__________________/ \____/ + \ Page !ready + * + */ + + +/* + * oggz_page_init (oggz) + * + * Initialises the next page of the current packet. + * + * If this returns 0, the page is not ready for writing. + */ +static long +oggz_page_init (OGGZ * oggz) +{ + OggzWriter * writer = &oggz->x.writer; + ogg_stream_state * os; + ogg_page * og; + int ret; + + if (oggz == NULL) return -1; + + os = writer->current_stream; + og = &oggz->current_page; + +#ifdef ALWAYS_FLUSH + ret = ogg_stream_flush (os, og); +#else + if (writer->flushing) { +#ifdef DEBUG + printf ("*** ATTEMPT FLUSH: "); +#endif + ret = ogg_stream_flush (os, og); + } else { +#ifdef DEBUG + printf ("*** ATTEMPT pageout: "); +#endif + ret = ogg_stream_pageout (os, og); + } +#endif + + if (ret) { + writer->page_offset = 0; + } + +#ifdef DEBUG + printf ("%s\n", ret ? "OK" : "NO"); +#endif + + return ret; +} + +/* + * oggz_packet_init (oggz, buf, n) + * + * Initialises the next packet with data from buf, length n + */ +static long +oggz_packet_init (OGGZ * oggz, oggz_writer_packet_t * next_zpacket) +{ + OggzWriter * writer = &oggz->x.writer; + oggz_stream_t * stream; + ogg_stream_state * os; + ogg_packet * op; + + if (oggz == NULL) return -1L; + + writer->current_zpacket = next_zpacket; + op = &next_zpacket->op; + +#ifdef DEBUG + printf ("oggz_packet_init: %ld bytes\n", (long)op->bytes); +#endif + + stream = next_zpacket->stream; + + /* Mark this stream as having delivered a non b_o_s packet if so */ + if (!op->b_o_s) stream->delivered_non_b_o_s = 1; + + os = &stream->ogg_stream; + ogg_stream_packetin (os, op); + + writer->flushing = (next_zpacket->flush & OGGZ_FLUSH_AFTER); +#ifdef DEBUG + printf ("oggz_packet_init: set flush to %d\n", writer->flushing); +#endif + writer->current_stream = os; + writer->packet_offset = 0; + + return 0; +} + +static long +oggz_page_copyout (OGGZ * oggz, unsigned char * buf, long n) +{ + OggzWriter * writer = &oggz->x.writer; + long h, b; + ogg_page * og; + + if (oggz == NULL) return -1L; + + og = &oggz->current_page; + + h = MIN (n, og->header_len - writer->page_offset); + if (h > 0) { + memcpy (buf, og->header + writer->page_offset, h); + writer->page_offset += h; + n -= h; + buf += h; + } else { + h = 0; + } + + b = MIN (n, og->header_len + og->body_len - writer->page_offset); + if (b > 0) { +#ifdef DEBUG + { + unsigned char * c = &og->body[writer->page_offset - og->header_len]; + printf ("oggz_page_copyout [%d] (@%ld): %c%c%c%c ...\n", + ogg_page_serialno (og), (long) ogg_page_granulepos (og), + c[0], c[1], c[2], c[3]); + } +#endif + memcpy (buf, og->body + (writer->page_offset - og->header_len), b); + writer->page_offset += b; + n -= b; + buf += b; + } else { + b = 0; + } + + return h + b; +} + +static long +oggz_page_writeout (OGGZ * oggz, long n) +{ + OggzWriter * writer = &oggz->x.writer; + 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) { +#ifdef OGGZ_WRITE_DIRECT + nwritten = write (fd, og->header + writer->page_offset, h); +#else + nwritten = (long)oggz_io_write (oggz, og->header + writer->page_offset, h); +#endif + +#ifdef DEBUG + if (nwritten < h) { + printf ("oggz_page_writeout: %ld < %ld\n", nwritten, h); + } +#endif + writer->page_offset += h; + n -= h; + } else { + h = 0; + } + + b = MIN (n, og->header_len + og->body_len - writer->page_offset); + if (b > 0) { +#ifdef OGGZ_WRITE_DIRECT + nwritten = write (fd, + og->body + (writer->page_offset - og->header_len), b); +#else + nwritten = (long)oggz_io_write (oggz, og->body + (writer->page_offset - og->header_len), b); +#endif +#ifdef DEBUG + if (nwritten < b) { + printf ("oggz_page_writeout: %ld < %ld\n", nwritten, b); + } +#endif + writer->page_offset += b; + n -= b; + } else { + b = 0; + } + + return h + b; +} + +static oggz_writer_packet_t * +oggz_dequeue_packet (OGGZ * oggz) +{ + OggzWriter * writer = &oggz->x.writer; + oggz_writer_packet_t * next_zpacket; + + if (writer->next_zpacket != NULL) { + next_zpacket = writer->next_zpacket; + writer->next_zpacket = NULL; + } else { + 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); + } + } + } + + return next_zpacket; +} + +static long +oggz_writer_make_packet (OGGZ * oggz) +{ + OggzWriter * writer = &oggz->x.writer; + oggz_writer_packet_t * zpacket, * next_zpacket; + int cb_ret = 0, ret = 0; + +#ifdef DEBUG + printf ("make packet ...\n"); +#endif + + /* finished with current packet; unguard */ + zpacket = writer->current_zpacket; + oggz_writer_packet_free (zpacket); + writer->current_zpacket = NULL; + + /* if the user wants the hungry callback after every packet, give + * it to them, marking emptiness appropriately + */ + if (writer->hungry && !writer->hungry_only_when_empty) { + int empty = (oggz_vector_size (writer->packet_queue) == 0); + cb_ret = writer->hungry (oggz, empty, writer->hungry_user_data); + } + + if (cb_ret == 0) { + /* dequeue and init the next packet */ + if ((next_zpacket = oggz_dequeue_packet (oggz)) == NULL) { + /*writer->eos = 1;*/ + ret = 0; + } else { + if ((writer->current_stream != NULL) && + (next_zpacket->flush & OGGZ_FLUSH_BEFORE)) { + writer->flushing = 1; +#ifdef DEBUG + printf ("oggz_writer_make_packet: set flush to %d\n", + writer->flushing); +#endif + next_zpacket->flush &= OGGZ_FLUSH_AFTER; + writer->next_zpacket = next_zpacket; + } else { + oggz_packet_init (oggz, next_zpacket); + } + ret = 1; + } + } else { + ret = 0; + } + + return ret; +} + +long +oggz_write_output (OGGZ * oggz, unsigned char * buf, long n) +{ + OggzWriter * writer = &oggz->x.writer; + long bytes, bytes_written = 1, remaining = n, nwritten = 0; + int active = 1; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (!(oggz->flags & OGGZ_WRITE)) { + return OGGZ_ERR_INVALID; + } + + if (writer->writing) return OGGZ_ERR_RECURSIVE_WRITE; + writer->writing = 1; + + while (active && remaining > 0) { + bytes = MIN (remaining, 1024); + + while (writer->state == OGGZ_MAKING_PACKETS) { + if (!oggz_writer_make_packet (oggz)) { + active = 0; + break; + } + if (oggz_page_init (oggz)) { + writer->state = OGGZ_WRITING_PAGES; + } + } + + if (writer->state == OGGZ_WRITING_PAGES) { + bytes_written = oggz_page_copyout (oggz, buf, bytes); + + if (bytes_written == -1) { + active = 0; + return OGGZ_ERR_SYSTEM; /* XXX: catch next */ + } else if (bytes_written == 0) { + if (!oggz_page_init (oggz)) { + writer->state = OGGZ_MAKING_PACKETS; + } + } + + buf += bytes_written; + + remaining -= bytes_written; + nwritten += bytes_written; + } + } + + writer->writing = 0; + + return nwritten; +} + +long +oggz_write (OGGZ * oggz, long n) +{ + OggzWriter * writer = &oggz->x.writer; + long bytes, bytes_written = 1, remaining = n, nwritten = 0; + int active = 1; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (!(oggz->flags & OGGZ_WRITE)) { + return OGGZ_ERR_INVALID; + } + + if (writer->writing) return OGGZ_ERR_RECURSIVE_WRITE; + writer->writing = 1; + +#ifdef DEBUG + printf ("oggz_write: IN\n"); +#endif + + while (active && remaining > 0) { + bytes = MIN (remaining, 1024); + +#ifdef DEBUG + printf ("oggz_write: write loop (%ld , %ld remain) ...\n", bytes, + remaining); +#endif + + while (writer->state == OGGZ_MAKING_PACKETS) { + if (!oggz_writer_make_packet (oggz)) { + active = 0; +#ifdef DEBUG + printf ("oggz_write: no packets\n"); +#endif + break; + } + if (oggz_page_init (oggz)) { + writer->state = OGGZ_WRITING_PAGES; + } + } + + if (writer->state == OGGZ_WRITING_PAGES) { + bytes_written = oggz_page_writeout (oggz, bytes); + + if (bytes_written == -1) { + active = 0; + return OGGZ_ERR_SYSTEM; /* XXX: catch next */ + } else if (bytes_written == 0) { + if (!oggz_page_init (oggz)) { +#ifdef DEBUG + printf ("oggz_write: bytes_written == 0, DONE\n"); +#endif + writer->state = OGGZ_MAKING_PACKETS; + } + } + + remaining -= bytes_written; + nwritten += bytes_written; + } + } + +#ifdef DEBUG + printf ("oggz_write: OUT %ld\n", nwritten); +#endif + + writer->writing = 0; + + return nwritten; +} + +long +oggz_write_get_next_page_size (OGGZ * oggz) +{ + OggzWriter * writer = &oggz->x.writer; + ogg_page * og; + + if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ; + + if (!(oggz->flags & OGGZ_WRITE)) { + return OGGZ_ERR_INVALID; + } + + og = &oggz->current_page; + + return (og->header_len + og->body_len - (long)writer->page_offset); +} + +#else /* OGGZ_CONFIG_WRITE */ + +#include +#include "oggz_private.h" + +int +oggz_write_set_hungry_callback (OGGZ * oggz, OggzWriteHungry hungry, + int only_when_empty, void * user_data) +{ + return OGGZ_ERR_DISABLED; +} + +int +oggz_write_feed (OGGZ * oggz, ogg_packet * op, long serialno, int flush, + int * guard) +{ + return OGGZ_ERR_DISABLED; +} + +long +oggz_write_output (OGGZ * oggz, unsigned char * buf, long n) +{ + return OGGZ_ERR_DISABLED; +} + +long +oggz_write (OGGZ * oggz, long n) +{ + return OGGZ_ERR_DISABLED; +} + +long +oggz_write_get_next_page_size (OGGZ * oggz) +{ + return OGGZ_ERR_DISABLED; +} + +#endif