diff options
| author | /dev/humancontroller <devhc@example.com> | 2015-03-28 01:21:52 +0100 | 
|---|---|---|
| committer | /dev/humancontroller <devhc@example.com> | 2017-01-26 07:16:57 +0100 | 
| commit | e9d273f7b0544b03e4d73e1078b2755fef0e05f7 (patch) | |
| tree | 4fa9863c0a41a444a0afc145411480ced8e4351d /src/opusfile-0.5 | |
| parent | f45fbef604e05144057dec8d1dbfc5d4f5a2a822 (diff) | |
remove most libraries from the source tree
Diffstat (limited to 'src/opusfile-0.5')
| -rw-r--r-- | src/opusfile-0.5/include/opusfile.h | 2089 | ||||
| -rw-r--r-- | src/opusfile-0.5/src/http.c | 3391 | ||||
| -rw-r--r-- | src/opusfile-0.5/src/info.c | 683 | ||||
| -rw-r--r-- | src/opusfile-0.5/src/internal.c | 42 | ||||
| -rw-r--r-- | src/opusfile-0.5/src/internal.h | 249 | ||||
| -rw-r--r-- | src/opusfile-0.5/src/opusfile.c | 3163 | ||||
| -rw-r--r-- | src/opusfile-0.5/src/stream.c | 366 | ||||
| -rw-r--r-- | src/opusfile-0.5/src/wincerts.c | 171 | ||||
| -rw-r--r-- | src/opusfile-0.5/src/winerrno.h | 90 | 
9 files changed, 0 insertions, 10244 deletions
diff --git a/src/opusfile-0.5/include/opusfile.h b/src/opusfile-0.5/include/opusfile.h deleted file mode 100644 index 850cd6b9..00000000 --- a/src/opusfile-0.5/include/opusfile.h +++ /dev/null @@ -1,2089 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012           * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ******************************************************************** - - function: stdio-based convenience library for opening/seeking/decoding - last mod: $Id: vorbisfile.h 17182 2010-04-29 03:48:32Z xiphmont $ - - ********************************************************************/ -#if !defined(_opusfile_h) -# define _opusfile_h (1) - -/**\mainpage -   \section Introduction - -   This is the documentation for the <tt>libopusfile</tt> C API. - -   The <tt>libopusfile</tt> package provides a convenient high-level API for -    decoding and basic manipulation of all Ogg Opus audio streams. -   <tt>libopusfile</tt> is implemented as a layer on top of Xiph.Org's -    reference -    <tt><a href="https://www.xiph.org/ogg/doc/libogg/reference.html">libogg</a></tt> -    and -    <tt><a href="https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/index.html">libopus</a></tt> -    libraries. - -   <tt>libopusfile</tt> provides several sets of built-in routines for -    file/stream access, and may also use custom stream I/O routines provided by -    the embedded environment. -   There are built-in I/O routines provided for ANSI-compliant -    <code>stdio</code> (<code>FILE *</code>), memory buffers, and URLs -    (including <file:> URLs, plus optionally <http:> and <https:> URLs). - -   \section Organization - -   The main API is divided into several sections: -   - \ref stream_open_close -   - \ref stream_info -   - \ref stream_decoding -   - \ref stream_seeking - -   Several additional sections are not tied to the main API. -   - \ref stream_callbacks -   - \ref header_info -   - \ref error_codes - -   \section Overview - -   The <tt>libopusfile</tt> API always decodes files to 48 kHz. -   The original sample rate is not preserved by the lossy compression, though -    it is stored in the header to allow you to resample to it after decoding -    (the <tt>libopusfile</tt> API does not currently provide a resampler, -    but the -    <a href="http://www.speex.org/docs/manual/speex-manual/node7.html#SECTION00760000000000000000">the -    Speex resampler</a> is a good choice if you need one). -   In general, if you are playing back the audio, you should leave it at -    48 kHz, provided your audio hardware supports it. -   When decoding to a file, it may be worth resampling back to the original -    sample rate, so as not to surprise users who might not expect the sample -    rate to change after encoding to Opus and decoding. - -   Opus files can contain anywhere from 1 to 255 channels of audio. -   The channel mappings for up to 8 channels are the same as the -    <a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis -    mappings</a>. -   A special stereo API can convert everything to 2 channels, making it simple -    to support multichannel files in an application which only has stereo -    output. -   Although the <tt>libopusfile</tt> ABI provides support for the theoretical -    maximum number of channels, the current implementation does not support -    files with more than 8 channels, as they do not have well-defined channel -    mappings. - -   Like all Ogg files, Opus files may be "chained". -   That is, multiple Opus files may be combined into a single, longer file just -    by concatenating the original files. -   This is commonly done in internet radio streaming, as it allows the title -    and artist to be updated each time the song changes, since each link in the -    chain includes its own set of metadata. - -   <tt>libopusfile</tt> fully supports chained files. -   It will decode the first Opus stream found in each link of a chained file -    (ignoring any other streams that might be concurrently multiplexed with it, -    such as a video stream). - -   The channel count can also change between links. -   If your application is not prepared to deal with this, it can use the stereo -    API to ensure the audio from all links will always get decoded into a -    common format. -   Since <tt>libopusfile</tt> always decodes to 48 kHz, you do not have to -    worry about the sample rate changing between links (as was possible with -    Vorbis). -   This makes application support for chained files with <tt>libopusfile</tt> -    very easy.*/ - -# if defined(__cplusplus) -extern "C" { -# endif - -# include <stdarg.h> -# include <stdio.h> -# include <ogg/ogg.h> -# include <opus_multistream.h> - -/**@cond PRIVATE*/ - -/*Enable special features for gcc and gcc-compatible compilers.*/ -# if !defined(OP_GNUC_PREREQ) -#  if defined(__GNUC__)&&defined(__GNUC_MINOR__) -#   define OP_GNUC_PREREQ(_maj,_min) \ - ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) -#  else -#   define OP_GNUC_PREREQ(_maj,_min) 0 -#  endif -# endif - -# if OP_GNUC_PREREQ(4,0) -#  pragma GCC visibility push(default) -# endif - -typedef struct OpusHead          OpusHead; -typedef struct OpusTags          OpusTags; -typedef struct OpusPictureTag    OpusPictureTag; -typedef struct OpusServerInfo    OpusServerInfo; -typedef struct OpusFileCallbacks OpusFileCallbacks; -typedef struct OggOpusFile       OggOpusFile; - -/*Warning attributes for libopusfile functions.*/ -# if OP_GNUC_PREREQ(3,4) -#  define OP_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) -# else -#  define OP_WARN_UNUSED_RESULT -# endif -# if OP_GNUC_PREREQ(3,4) -#  define OP_ARG_NONNULL(_x) __attribute__((__nonnull__(_x))) -# else -#  define OP_ARG_NONNULL(_x) -# endif - -/**@endcond*/ - -/**\defgroup error_codes Error Codes*/ -/*@{*/ -/**\name List of possible error codes -   Many of the functions in this library return a negative error code when a -    function fails. -   This list provides a brief explanation of the common errors. -   See each individual function for more details on what a specific error code -    means in that context.*/ -/*@{*/ - -/**A request did not succeed.*/ -#define OP_FALSE         (-1) -/*Currently not used externally.*/ -#define OP_EOF           (-2) -/**There was a hole in the page sequence numbers (e.g., a page was corrupt or -    missing).*/ -#define OP_HOLE          (-3) -/**An underlying read, seek, or tell operation failed when it should have -    succeeded.*/ -#define OP_EREAD         (-128) -/**A <code>NULL</code> pointer was passed where one was unexpected, or an -    internal memory allocation failed, or an internal library error was -    encountered.*/ -#define OP_EFAULT        (-129) -/**The stream used a feature that is not implemented, such as an unsupported -    channel family.*/ -#define OP_EIMPL         (-130) -/**One or more parameters to a function were invalid.*/ -#define OP_EINVAL        (-131) -/**A purported Ogg Opus stream did not begin with an Ogg page, a purported -    header packet did not start with one of the required strings, "OpusHead" or -    "OpusTags", or a link in a chained file was encountered that did not -    contain any logical Opus streams.*/ -#define OP_ENOTFORMAT    (-132) -/**A required header packet was not properly formatted, contained illegal -    values, or was missing altogether.*/ -#define OP_EBADHEADER    (-133) -/**The ID header contained an unrecognized version number.*/ -#define OP_EVERSION      (-134) -/*Currently not used at all.*/ -#define OP_ENOTAUDIO     (-135) -/**An audio packet failed to decode properly. -   This is usually caused by a multistream Ogg packet where the durations of -    the individual Opus packets contained in it are not all the same.*/ -#define OP_EBADPACKET    (-136) -/**We failed to find data we had seen before, or the bitstream structure was -    sufficiently malformed that seeking to the target destination was -    impossible.*/ -#define OP_EBADLINK      (-137) -/**An operation that requires seeking was requested on an unseekable stream.*/ -#define OP_ENOSEEK       (-138) -/**The first or last granule position of a link failed basic validity checks.*/ -#define OP_EBADTIMESTAMP (-139) - -/*@}*/ -/*@}*/ - -/**\defgroup header_info Header Information*/ -/*@{*/ - -/**The maximum number of channels in an Ogg Opus stream.*/ -#define OPUS_CHANNEL_COUNT_MAX (255) - -/**Ogg Opus bitstream information. -   This contains the basic playback parameters for a stream, and corresponds to -    the initial ID header packet of an Ogg Opus stream.*/ -struct OpusHead{ -  /**The Ogg Opus format version, in the range 0...255. -     The top 4 bits represent a "major" version, and the bottom four bits -      represent backwards-compatible "minor" revisions. -     The current specification describes version 1. -     This library will recognize versions up through 15 as backwards compatible -      with the current specification. -     An earlier draft of the specification described a version 0, but the only -      difference between version 1 and version 0 is that version 0 did -      not specify the semantics for handling the version field.*/ -  int           version; -  /**The number of channels, in the range 1...255.*/ -  int           channel_count; -  /**The number of samples that should be discarded from the beginning of the -      stream.*/ -  unsigned      pre_skip; -  /**The sampling rate of the original input. -     All Opus audio is coded at 48 kHz, and should also be decoded at 48 kHz -      for playback (unless the target hardware does not support this sampling -      rate). -     However, this field may be used to resample the audio back to the original -      sampling rate, for example, when saving the output to a file.*/ -  opus_uint32   input_sample_rate; -  /**The gain to apply to the decoded output, in dB, as a Q8 value in the range -      -32768...32767. -     The <tt>libopusfile</tt> API will automatically apply this gain to the -      decoded output before returning it, scaling it by -      <code>pow(10,output_gain/(20.0*256))</code>.*/ -  int           output_gain; -  /**The channel mapping family, in the range 0...255. -     Channel mapping family 0 covers mono or stereo in a single stream. -     Channel mapping family 1 covers 1 to 8 channels in one or more streams, -      using the Vorbis speaker assignments. -     Channel mapping family 255 covers 1 to 255 channels in one or more -      streams, but without any defined speaker assignment.*/ -  int           mapping_family; -  /**The number of Opus streams in each Ogg packet, in the range 1...255.*/ -  int           stream_count; -  /**The number of coupled Opus streams in each Ogg packet, in the range -      0...127. -     This must satisfy <code>0 <= coupled_count <= stream_count</code> and -      <code>coupled_count + stream_count <= 255</code>. -     The coupled streams appear first, before all uncoupled streams, in an Ogg -      Opus packet.*/ -  int           coupled_count; -  /**The mapping from coded stream channels to output channels. -     Let <code>index=mapping[k]</code> be the value for channel <code>k</code>. -     If <code>index<2*coupled_count</code>, then it refers to the left channel -      from stream <code>(index/2)</code> if even, and the right channel from -      stream <code>(index/2)</code> if odd. -     Otherwise, it refers to the output of the uncoupled stream -      <code>(index-coupled_count)</code>.*/ -  unsigned char mapping[OPUS_CHANNEL_COUNT_MAX]; -}; - -/**The metadata from an Ogg Opus stream. - -   This structure holds the in-stream metadata corresponding to the 'comment' -    header packet of an Ogg Opus stream. -   The comment header is meant to be used much like someone jotting a quick -    note on the label of a CD. -   It should be a short, to the point text note that can be more than a couple -    words, but not more than a short paragraph. - -   The metadata is stored as a series of (tag, value) pairs, in length-encoded -    string vectors, using the same format as Vorbis (without the final "framing -    bit"), Theora, and Speex, except for the packet header. -   The first occurrence of the '=' character delimits the tag and value. -   A particular tag may occur more than once, and order is significant. -   The character set encoding for the strings is always UTF-8, but the tag -    names are limited to ASCII, and treated as case-insensitive. -   See <a href="http://www.xiph.org/vorbis/doc/v-comment.html">the Vorbis -    comment header specification</a> for details. - -   In filling in this structure, <tt>libopusfile</tt> will null-terminate the -    #user_comments strings for safety. -   However, the bitstream format itself treats them as 8-bit clean vectors, -    possibly containing NUL characters, so the #comment_lengths array should be -    treated as their authoritative length. - -   This structure is binary and source-compatible with a -    <code>vorbis_comment</code>, and pointers to it may be freely cast to -    <code>vorbis_comment</code> pointers, and vice versa. -   It is provided as a separate type to avoid introducing a compile-time -    dependency on the libvorbis headers.*/ -struct OpusTags{ -  /**The array of comment string vectors.*/ -  char **user_comments; -  /**An array of the corresponding length of each vector, in bytes.*/ -  int   *comment_lengths; -  /**The total number of comment streams.*/ -  int    comments; -  /**The null-terminated vendor string. -     This identifies the software used to encode the stream.*/ -  char  *vendor; -}; - -/**\name Picture tag image formats*/ -/*@{*/ - -/**The MIME type was not recognized, or the image data did not match the -    declared MIME type.*/ -#define OP_PIC_FORMAT_UNKNOWN (-1) -/**The MIME type indicates the image data is really a URL.*/ -#define OP_PIC_FORMAT_URL     (0) -/**The image is a JPEG.*/ -#define OP_PIC_FORMAT_JPEG    (1) -/**The image is a PNG.*/ -#define OP_PIC_FORMAT_PNG     (2) -/**The image is a GIF.*/ -#define OP_PIC_FORMAT_GIF     (3) - -/*@}*/ - -/**The contents of a METADATA_BLOCK_PICTURE tag.*/ -struct OpusPictureTag{ -  /**The picture type according to the ID3v2 APIC frame: -     <ol start="0"> -     <li>Other</li> -     <li>32x32 pixels 'file icon' (PNG only)</li> -     <li>Other file icon</li> -     <li>Cover (front)</li> -     <li>Cover (back)</li> -     <li>Leaflet page</li> -     <li>Media (e.g. label side of CD)</li> -     <li>Lead artist/lead performer/soloist</li> -     <li>Artist/performer</li> -     <li>Conductor</li> -     <li>Band/Orchestra</li> -     <li>Composer</li> -     <li>Lyricist/text writer</li> -     <li>Recording Location</li> -     <li>During recording</li> -     <li>During performance</li> -     <li>Movie/video screen capture</li> -     <li>A bright colored fish</li> -     <li>Illustration</li> -     <li>Band/artist logotype</li> -     <li>Publisher/Studio logotype</li> -     </ol> -     Others are reserved and should not be used. -     There may only be one each of picture type 1 and 2 in a file.*/ -  opus_int32     type; -  /**The MIME type of the picture, in printable ASCII characters 0x20-0x7E. -     The MIME type may also be <code>"-->"</code> to signify that the data part -      is a URL pointing to the picture instead of the picture data itself. -     In this case, a terminating NUL is appended to the URL string in #data, -      but #data_length is set to the length of the string excluding that -      terminating NUL.*/ -  char          *mime_type; -  /**The description of the picture, in UTF-8.*/ -  char          *description; -  /**The width of the picture in pixels.*/ -  opus_uint32    width; -  /**The height of the picture in pixels.*/ -  opus_uint32    height; -  /**The color depth of the picture in bits-per-pixel (<em>not</em> -      bits-per-channel).*/ -  opus_uint32    depth; -  /**For indexed-color pictures (e.g., GIF), the number of colors used, or 0 -      for non-indexed pictures.*/ -  opus_uint32    colors; -  /**The length of the picture data in bytes.*/ -  opus_uint32    data_length; -  /**The binary picture data.*/ -  unsigned char *data; -  /**The format of the picture data, if known. -     One of -     <ul> -     <li>#OP_PIC_FORMAT_UNKNOWN,</li> -     <li>#OP_PIC_FORMAT_URL,</li> -     <li>#OP_PIC_FORMAT_JPEG,</li> -     <li>#OP_PIC_FORMAT_PNG, or</li> -     <li>#OP_PIC_FORMAT_GIF.</li> -     </ul>*/ -  int            format; -}; - -/**\name Functions for manipulating header data - -   These functions manipulate the #OpusHead and #OpusTags structures, -    which describe the audio parameters and tag-value metadata, respectively. -   These can be used to query the headers returned by <tt>libopusfile</tt>, or -    to parse Opus headers from sources other than an Ogg Opus stream, provided -    they use the same format.*/ -/*@{*/ - -/**Parses the contents of the ID header packet of an Ogg Opus stream. -   \param[out] _head Returns the contents of the parsed packet. -                     The contents of this structure are untouched on error. -                     This may be <code>NULL</code> to merely test the header -                      for validity. -   \param[in]  _data The contents of the ID header packet. -   \param      _len  The number of bytes of data in the ID header packet. -   \return 0 on success or a negative value on error. -   \retval #OP_ENOTFORMAT If the data does not start with the "OpusHead" -                           string. -   \retval #OP_EVERSION   If the version field signaled a version this library -                           does not know how to parse. -   \retval #OP_EIMPL      If the channel mapping family was 255, which general -                           purpose players should not attempt to play. -   \retval #OP_EBADHEADER If the contents of the packet otherwise violate the -                           Ogg Opus specification: -                          <ul> -                           <li>Insufficient data,</li> -                           <li>Too much data for the known minor versions,</li> -                           <li>An unrecognized channel mapping family,</li> -                           <li>Zero channels or too many channels,</li> -                           <li>Zero coded streams,</li> -                           <li>Too many coupled streams, or</li> -                           <li>An invalid channel mapping index.</li> -                          </ul>*/ -OP_WARN_UNUSED_RESULT int opus_head_parse(OpusHead *_head, - const unsigned char *_data,size_t _len) OP_ARG_NONNULL(2); - -/**Converts a granule position to a sample offset for a given Ogg Opus stream. -   The sample offset is simply <code>_gp-_head->pre_skip</code>. -   Granule position values smaller than OpusHead#pre_skip correspond to audio -    that should never be played, and thus have no associated sample offset. -   This function returns -1 for such values. -   This function also correctly handles extremely large granule positions, -    which may have wrapped around to a negative number when stored in a signed -    ogg_int64_t value. -   \param _head The #OpusHead information from the ID header of the stream. -   \param _gp   The granule position to convert. -   \return The sample offset associated with the given granule position -            (counting at a 48 kHz sampling rate), or the special value -1 on -            error (i.e., the granule position was smaller than the pre-skip -            amount).*/ -ogg_int64_t opus_granule_sample(const OpusHead *_head,ogg_int64_t _gp) - OP_ARG_NONNULL(1); - -/**Parses the contents of the 'comment' header packet of an Ogg Opus stream. -   \param[out] _tags An uninitialized #OpusTags structure. -                     This returns the contents of the parsed packet. -                     The contents of this structure are untouched on error. -                     This may be <code>NULL</code> to merely test the header -                      for validity. -   \param[in]  _data The contents of the 'comment' header packet. -   \param      _len  The number of bytes of data in the 'info' header packet. -   \retval 0              Success. -   \retval #OP_ENOTFORMAT If the data does not start with the "OpusTags" -                           string. -   \retval #OP_EBADHEADER If the contents of the packet otherwise violate the -                           Ogg Opus specification. -   \retval #OP_EFAULT     If there wasn't enough memory to store the tags.*/ -OP_WARN_UNUSED_RESULT int opus_tags_parse(OpusTags *_tags, - const unsigned char *_data,size_t _len) OP_ARG_NONNULL(2); - -/**Performs a deep copy of an #OpusTags structure. -   \param _dst The #OpusTags structure to copy into. -               If this function fails, the contents of this structure remain -                untouched. -   \param _src The #OpusTags structure to copy from. -   \retval 0          Success. -   \retval #OP_EFAULT If there wasn't enough memory to copy the tags.*/ -int opus_tags_copy(OpusTags *_dst,const OpusTags *_src) OP_ARG_NONNULL(1); - -/**Initializes an #OpusTags structure. -   This should be called on a freshly allocated #OpusTags structure before -    attempting to use it. -   \param _tags The #OpusTags structure to initialize.*/ -void opus_tags_init(OpusTags *_tags) OP_ARG_NONNULL(1); - -/**Add a (tag, value) pair to an initialized #OpusTags structure. -   \note Neither opus_tags_add() nor opus_tags_add_comment() support values -    containing embedded NULs, although the bitstream format does support them. -   To add such tags, you will need to manipulate the #OpusTags structure -    directly. -   \param _tags  The #OpusTags structure to add the (tag, value) pair to. -   \param _tag   A NUL-terminated, case-insensitive, ASCII string containing -                  the tag to add (without an '=' character). -   \param _value A NUL-terminated UTF-8 containing the corresponding value. -   \return 0 on success, or a negative value on failure. -   \retval #OP_EFAULT An internal memory allocation failed.*/ -int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value) - OP_ARG_NONNULL(1) OP_ARG_NONNULL(2) OP_ARG_NONNULL(3); - -/**Add a comment to an initialized #OpusTags structure. -   \note Neither opus_tags_add_comment() nor opus_tags_add() support comments -    containing embedded NULs, although the bitstream format does support them. -   To add such tags, you will need to manipulate the #OpusTags structure -    directly. -   \param _tags    The #OpusTags structure to add the comment to. -   \param _comment A NUL-terminated UTF-8 string containing the comment in -                    "TAG=value" form. -   \return 0 on success, or a negative value on failure. -   \retval #OP_EFAULT An internal memory allocation failed.*/ -int opus_tags_add_comment(OpusTags *_tags,const char *_comment) - OP_ARG_NONNULL(1) OP_ARG_NONNULL(2); - -/**Look up a comment value by its tag. -   \param _tags  An initialized #OpusTags structure. -   \param _tag   The tag to look up. -   \param _count The instance of the tag. -                 The same tag can appear multiple times, each with a distinct -                  value, so an index is required to retrieve them all. -                 The order in which these values appear is significant and -                  should be preserved. -                 Use opus_tags_query_count() to get the legal range for the -                  \a _count parameter. -   \return A pointer to the queried tag's value. -           This points directly to data in the #OpusTags structure. -           It should not be modified or freed by the application, and -            modifications to the structure may invalidate the pointer. -   \retval NULL If no matching tag is found.*/ -const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count) - OP_ARG_NONNULL(1) OP_ARG_NONNULL(2); - -/**Look up the number of instances of a tag. -   Call this first when querying for a specific tag and then iterate over the -    number of instances with separate calls to opus_tags_query() to retrieve -    all the values for that tag in order. -   \param _tags An initialized #OpusTags structure. -   \param _tag  The tag to look up. -   \return The number of instances of this particular tag.*/ -int opus_tags_query_count(const OpusTags *_tags,const char *_tag) - OP_ARG_NONNULL(1) OP_ARG_NONNULL(2); - -/**Get the track gain from an R128_TRACK_GAIN tag, if one was specified. -   This searches for the first R128_TRACK_GAIN tag with a valid signed, -    16-bit decimal integer value and returns the value. -   This routine is exposed merely for convenience for applications which wish -    to do something special with the track gain (i.e., display it). -   If you simply wish to apply the track gain instead of the header gain, you -    can use op_set_gain_offset() with an #OP_TRACK_GAIN type and no offset. -   \param      _tags    An initialized #OpusTags structure. -   \param[out] _gain_q8 The track gain, in 1/256ths of a dB. -                        This will lie in the range [-32768,32767], and should -                         be applied in <em>addition</em> to the header gain. -                        On error, no value is returned, and the previous -                         contents remain unchanged. -   \return 0 on success, or a negative value on error. -   \retval #OP_FALSE There was no track gain available in the given tags.*/ -int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8) - OP_ARG_NONNULL(1) OP_ARG_NONNULL(2); - -/**Clears the #OpusTags structure. -   This should be called on an #OpusTags structure after it is no longer -    needed. -   It will free all memory used by the structure members. -   \param _tags The #OpusTags structure to clear.*/ -void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1); - -/**Check if \a _comment is an instance of a \a _tag_name tag. -   \see opus_tagncompare -   \param _tag_name A NUL-terminated, case-insensitive, ASCII string containing -                     the name of the tag to check for (without the terminating -                     '=' character). -   \param _comment  The comment string to check. -   \return An integer less than, equal to, or greater than zero if \a _comment -            is found respectively, to be less than, to match, or be greater -            than a "tag=value" string whose tag matches \a _tag_name.*/ -int opus_tagcompare(const char *_tag_name,const char *_comment); - -/**Check if \a _comment is an instance of a \a _tag_name tag. -   This version is slightly more efficient than opus_tagcompare() if the length -    of the tag name is already known (e.g., because it is a constant). -   \see opus_tagcompare -   \param _tag_name A case-insensitive ASCII string containing the name of the -                     tag to check for (without the terminating '=' character). -   \param _tag_len  The number of characters in the tag name. -                    This must be non-negative. -   \param _comment  The comment string to check. -   \return An integer less than, equal to, or greater than zero if \a _comment -            is found respectively, to be less than, to match, or be greater -            than a "tag=value" string whose tag matches the first \a _tag_len -            characters of \a _tag_name.*/ -int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment); - -/**Parse a single METADATA_BLOCK_PICTURE tag. -   This decodes the BASE64-encoded content of the tag and returns a structure -    with the MIME type, description, image parameters (if known), and the -    compressed image data. -   If the MIME type indicates the presence of an image format we recognize -    (JPEG, PNG, or GIF) and the actual image data contains the magic signature -    associated with that format, then the OpusPictureTag::format field will be -    set to the corresponding format. -   This is provided as a convenience to avoid requiring applications to parse -    the MIME type and/or do their own format detection for the commonly used -    formats. -   In this case, we also attempt to extract the image parameters directly from -    the image data (overriding any that were present in the tag, which the -    specification says applications are not meant to rely on). -   The application must still provide its own support for actually decoding the -    image data and, if applicable, retrieving that data from URLs. -   \param[out] _pic Returns the parsed picture data. -                    No sanitation is done on the type, MIME type, or -                     description fields, so these might return invalid values. -                    The contents of this structure are left unmodified on -                     failure. -   \param      _tag The METADATA_BLOCK_PICTURE tag contents. -                    The leading "METADATA_BLOCK_PICTURE=" portion is optional, -                     to allow the function to be used on either directly on the -                     values in OpusTags::user_comments or on the return value -                     of opus_tags_query(). -   \return 0 on success or a negative value on error. -   \retval #OP_ENOTFORMAT The METADATA_BLOCK_PICTURE contents were not valid. -   \retval #OP_EFAULT     There was not enough memory to store the picture tag -                           contents.*/ -OP_WARN_UNUSED_RESULT int opus_picture_tag_parse(OpusPictureTag *_pic, - const char *_tag) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2); - -/**Initializes an #OpusPictureTag structure. -   This should be called on a freshly allocated #OpusPictureTag structure -    before attempting to use it. -   \param _pic The #OpusPictureTag structure to initialize.*/ -void opus_picture_tag_init(OpusPictureTag *_pic) OP_ARG_NONNULL(1); - -/**Clears the #OpusPictureTag structure. -   This should be called on an #OpusPictureTag structure after it is no longer -    needed. -   It will free all memory used by the structure members. -   \param _pic The #OpusPictureTag structure to clear.*/ -void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1); - -/*@}*/ - -/*@}*/ - -/**\defgroup url_options URL Reading Options*/ -/*@{*/ -/**\name URL reading options -   Options for op_url_stream_create() and associated functions. -   These allow you to provide proxy configuration parameters, skip SSL -    certificate checks, etc. -   Options are processed in order, and if the same option is passed multiple -    times, only the value specified by the last occurrence has an effect -    (unless otherwise specified). -   They may be expanded in the future.*/ -/*@{*/ - -/**@cond PRIVATE*/ - -/*These are the raw numbers used to define the request codes. -  They should not be used directly.*/ -#define OP_SSL_SKIP_CERTIFICATE_CHECK_REQUEST (6464) -#define OP_HTTP_PROXY_HOST_REQUEST            (6528) -#define OP_HTTP_PROXY_PORT_REQUEST            (6592) -#define OP_HTTP_PROXY_USER_REQUEST            (6656) -#define OP_HTTP_PROXY_PASS_REQUEST            (6720) -#define OP_GET_SERVER_INFO_REQUEST            (6784) - -#define OP_URL_OPT(_request) ((_request)+(char *)0) - -/*These macros trigger compilation errors or warnings if the wrong types are -   provided to one of the URL options.*/ -#define OP_CHECK_INT(_x) ((void)((_x)==(opus_int32)0),(opus_int32)(_x)) -#define OP_CHECK_CONST_CHAR_PTR(_x) ((_x)+((_x)-(const char *)(_x))) -#define OP_CHECK_SERVER_INFO_PTR(_x) ((_x)+((_x)-(OpusServerInfo *)(_x))) - -/**@endcond*/ - -/**HTTP/Shoutcast/Icecast server information associated with a URL.*/ -struct OpusServerInfo{ -  /**The name of the server (icy-name/ice-name). -     This is <code>NULL</code> if there was no <code>icy-name</code> or -      <code>ice-name</code> header.*/ -  char        *name; -  /**A short description of the server (icy-description/ice-description). -     This is <code>NULL</code> if there was no <code>icy-description</code> or -      <code>ice-description</code> header.*/ -  char        *description; -  /**The genre the server falls under (icy-genre/ice-genre). -     This is <code>NULL</code> if there was no <code>icy-genre</code> or -      <code>ice-genre</code> header.*/ -  char        *genre; -  /**The homepage for the server (icy-url/ice-url). -     This is <code>NULL</code> if there was no <code>icy-url</code> or -      <code>ice-url</code> header.*/ -  char        *url; -  /**The software used by the origin server (Server). -     This is <code>NULL</code> if there was no <code>Server</code> header.*/ -  char        *server; -  /**The media type of the entity sent to the recepient (Content-Type). -     This is <code>NULL</code> if there was no <code>Content-Type</code> -      header.*/ -  char        *content_type; -  /**The nominal stream bitrate in kbps (icy-br/ice-bitrate). -     This is <code>-1</code> if there was no <code>icy-br</code> or -      <code>ice-bitrate</code> header.*/ -  opus_int32   bitrate_kbps; -  /**Flag indicating whether the server is public (<code>1</code>) or not -      (<code>0</code>) (icy-pub/ice-public). -     This is <code>-1</code> if there was no <code>icy-pub</code> or -      <code>ice-public</code> header.*/ -  int          is_public; -  /**Flag indicating whether the server is using HTTPS instead of HTTP. -     This is <code>0</code> unless HTTPS is being used. -     This may not match the protocol used in the original URL if there were -      redirections.*/ -  int          is_ssl; -}; - -/**Initializes an #OpusServerInfo structure. -   All fields are set as if the corresponding header was not available. -   \param _info The #OpusServerInfo structure to initialize. -   \note If you use this function, you must link against <tt>libopusurl</tt>.*/ -void opus_server_info_init(OpusServerInfo *_info) OP_ARG_NONNULL(1); - -/**Clears the #OpusServerInfo structure. -   This should be called on an #OpusServerInfo structure after it is no longer -    needed. -   It will free all memory used by the structure members. -   \param _info The #OpusServerInfo structure to clear. -   \note If you use this function, you must link against <tt>libopusurl</tt>.*/ -void opus_server_info_clear(OpusServerInfo *_info) OP_ARG_NONNULL(1); - -/**Skip the certificate check when connecting via TLS/SSL (https). -   \param _b <code>opus_int32</code>: Whether or not to skip the certificate -              check. -             The check will be skipped if \a _b is non-zero, and will not be -              skipped if \a _b is zero. -   \hideinitializer*/ -#define OP_SSL_SKIP_CERTIFICATE_CHECK(_b) \ - OP_URL_OPT(OP_SSL_SKIP_CERTIFICATE_CHECK_REQUEST),OP_CHECK_INT(_b) - -/**Proxy connections through the given host. -   If no port is specified via #OP_HTTP_PROXY_PORT, the port number defaults -    to 8080 (http-alt). -   All proxy parameters are ignored for non-http and non-https URLs. -   \param _host <code>const char *</code>: The proxy server hostname. -                This may be <code>NULL</code> to disable the use of a proxy -                 server. -   \hideinitializer*/ -#define OP_HTTP_PROXY_HOST(_host) \ - OP_URL_OPT(OP_HTTP_PROXY_HOST_REQUEST),OP_CHECK_CONST_CHAR_PTR(_host) - -/**Use the given port when proxying connections. -   This option only has an effect if #OP_HTTP_PROXY_HOST is specified with a -    non-<code>NULL</code> \a _host. -   If this option is not provided, the proxy port number defaults to 8080 -    (http-alt). -   All proxy parameters are ignored for non-http and non-https URLs. -   \param _port <code>opus_int32</code>: The proxy server port. -                This must be in the range 0...65535 (inclusive), or the -                 URL function this is passed to will fail. -   \hideinitializer*/ -#define OP_HTTP_PROXY_PORT(_port) \ - OP_URL_OPT(OP_HTTP_PROXY_PORT_REQUEST),OP_CHECK_INT(_port) - -/**Use the given user name for authentication when proxying connections. -   All proxy parameters are ignored for non-http and non-https URLs. -   \param _user const char *: The proxy server user name. -                              This may be <code>NULL</code> to disable proxy -                               authentication. -                              A non-<code>NULL</code> value only has an effect -                               if #OP_HTTP_PROXY_HOST and #OP_HTTP_PROXY_PASS -                               are also specified with non-<code>NULL</code> -                               arguments. -   \hideinitializer*/ -#define OP_HTTP_PROXY_USER(_user) \ - OP_URL_OPT(OP_HTTP_PROXY_USER_REQUEST),OP_CHECK_CONST_CHAR_PTR(_user) - -/**Use the given password for authentication when proxying connections. -   All proxy parameters are ignored for non-http and non-https URLs. -   \param _pass const char *: The proxy server password. -                              This may be <code>NULL</code> to disable proxy -                               authentication. -                              A non-<code>NULL</code> value only has an effect -                               if #OP_HTTP_PROXY_HOST and #OP_HTTP_PROXY_USER -                               are also specified with non-<code>NULL</code> -                               arguments. -   \hideinitializer*/ -#define OP_HTTP_PROXY_PASS(_pass) \ - OP_URL_OPT(OP_HTTP_PROXY_PASS_REQUEST),OP_CHECK_CONST_CHAR_PTR(_pass) - -/**Parse information about the streaming server (if any) and return it. -   Very little validation is done. -   In particular, OpusServerInfo::url may not be a valid URL, -    OpusServerInfo::bitrate_kbps may not really be in kbps, and -    OpusServerInfo::content_type may not be a valid MIME type. -   The character set of the string fields is not specified anywhere, and should -    not be assumed to be valid UTF-8. -   \param _info OpusServerInfo *: Returns information about the server. -                                  If there is any error opening the stream, the -                                   contents of this structure remain -                                   unmodified. -                                  On success, fills in the structure with the -                                   server information that was available, if -                                   any. -                                  After a successful return, the contents of -                                   this structure should be freed by calling -                                   opus_server_info_clear(). -   \hideinitializer*/ -#define OP_GET_SERVER_INFO(_info) \ - OP_URL_OPT(OP_GET_SERVER_INFO_REQUEST),OP_CHECK_SERVER_INFO_PTR(_info) - -/*@}*/ -/*@}*/ - -/**\defgroup stream_callbacks Abstract Stream Reading Interface*/ -/*@{*/ -/**\name Functions for reading from streams -   These functions define the interface used to read from and seek in a stream -    of data. -   A stream does not need to implement seeking, but the decoder will not be -    able to seek if it does not do so. -   These functions also include some convenience routines for working with -    standard <code>FILE</code> pointers, complete streams stored in a single -    block of memory, or URLs.*/ -/*@{*/ - -/**Reads up to \a _nbytes bytes of data from \a _stream. -   \param      _stream The stream to read from. -   \param[out] _ptr    The buffer to store the data in. -   \param      _nbytes The maximum number of bytes to read. -                       This function may return fewer, though it will not -                        return zero unless it reaches end-of-file. -   \return The number of bytes successfully read, or a negative value on -            error.*/ -typedef int (*op_read_func)(void *_stream,unsigned char *_ptr,int _nbytes); - -/**Sets the position indicator for \a _stream. -   The new position, measured in bytes, is obtained by adding \a _offset -    bytes to the position specified by \a _whence. -   If \a _whence is set to <code>SEEK_SET</code>, <code>SEEK_CUR</code>, or -    <code>SEEK_END</code>, the offset is relative to the start of the stream, -    the current position indicator, or end-of-file, respectively. -   \retval 0  Success. -   \retval -1 Seeking is not supported or an error occurred. -              <code>errno</code> need not be set.*/ -typedef int (*op_seek_func)(void *_stream,opus_int64 _offset,int _whence); - -/**Obtains the current value of the position indicator for \a _stream. -   \return The current position indicator.*/ -typedef opus_int64 (*op_tell_func)(void *_stream); - -/**Closes the underlying stream. -   \retval 0   Success. -   \retval EOF An error occurred. -               <code>errno</code> need not be set.*/ -typedef int (*op_close_func)(void *_stream); - -/**The callbacks used to access non-<code>FILE</code> stream resources. -   The function prototypes are basically the same as for the stdio functions -    <code>fread()</code>, <code>fseek()</code>, <code>ftell()</code>, and -    <code>fclose()</code>. -   The differences are that the <code>FILE *</code> arguments have been -    replaced with a <code>void *</code>, which is to be used as a pointer to -    whatever internal data these functions might need, that #seek and #tell -    take and return 64-bit offsets, and that #seek <em>must</em> return -1 if -    the stream is unseekable.*/ -struct OpusFileCallbacks{ -  /**Used to read data from the stream. -     This must not be <code>NULL</code>.*/ -  op_read_func  read; -  /**Used to seek in the stream. -     This may be <code>NULL</code> if seeking is not implemented.*/ -  op_seek_func  seek; -  /**Used to return the current read position in the stream. -     This may be <code>NULL</code> if seeking is not implemented.*/ -  op_tell_func  tell; -  /**Used to close the stream when the decoder is freed. -     This may be <code>NULL</code> to leave the stream open.*/ -  op_close_func close; -}; - -/**Opens a stream with <code>fopen()</code> and fills in a set of callbacks -    that can be used to access it. -   This is useful to avoid writing your own portable 64-bit seeking wrappers, -    and also avoids cross-module linking issues on Windows, where a -    <code>FILE *</code> must be accessed by routines defined in the same module -    that opened it. -   \param[out] _cb   The callbacks to use for this file. -                     If there is an error opening the file, nothing will be -                      filled in here. -   \param      _path The path to the file to open. -                     On Windows, this string must be UTF-8 (to allow access to -                      files whose names cannot be represented in the current -                      MBCS code page). -                     All other systems use the native character encoding. -   \param      _mode The mode to open the file in. -   \return A stream handle to use with the callbacks, or <code>NULL</code> on -            error.*/ -OP_WARN_UNUSED_RESULT void *op_fopen(OpusFileCallbacks *_cb, - const char *_path,const char *_mode) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2) - OP_ARG_NONNULL(3); - -/**Opens a stream with <code>fdopen()</code> and fills in a set of callbacks -    that can be used to access it. -   This is useful to avoid writing your own portable 64-bit seeking wrappers, -    and also avoids cross-module linking issues on Windows, where a -    <code>FILE *</code> must be accessed by routines defined in the same module -    that opened it. -   \param[out] _cb   The callbacks to use for this file. -                     If there is an error opening the file, nothing will be -                      filled in here. -   \param      _fd   The file descriptor to open. -   \param      _mode The mode to open the file in. -   \return A stream handle to use with the callbacks, or <code>NULL</code> on -            error.*/ -OP_WARN_UNUSED_RESULT void *op_fdopen(OpusFileCallbacks *_cb, - int _fd,const char *_mode) OP_ARG_NONNULL(1) OP_ARG_NONNULL(3); - -/**Opens a stream with <code>freopen()</code> and fills in a set of callbacks -    that can be used to access it. -   This is useful to avoid writing your own portable 64-bit seeking wrappers, -    and also avoids cross-module linking issues on Windows, where a -    <code>FILE *</code> must be accessed by routines defined in the same module -    that opened it. -   \param[out] _cb     The callbacks to use for this file. -                       If there is an error opening the file, nothing will be -                        filled in here. -   \param      _path   The path to the file to open. -                       On Windows, this string must be UTF-8 (to allow access -                        to files whose names cannot be represented in the -                        current MBCS code page). -                       All other systems use the native character encoding. -   \param      _mode   The mode to open the file in. -   \param      _stream A stream previously returned by op_fopen(), op_fdopen(), -                        or op_freopen(). -   \return A stream handle to use with the callbacks, or <code>NULL</code> on -            error.*/ -OP_WARN_UNUSED_RESULT void *op_freopen(OpusFileCallbacks *_cb, - const char *_path,const char *_mode,void *_stream) OP_ARG_NONNULL(1) - OP_ARG_NONNULL(2) OP_ARG_NONNULL(3) OP_ARG_NONNULL(4); - -/**Creates a stream that reads from the given block of memory. -   This block of memory must contain the complete stream to decode. -   This is useful for caching small streams (e.g., sound effects) in RAM. -   \param[out] _cb   The callbacks to use for this stream. -                     If there is an error creating the stream, nothing will be -                      filled in here. -   \param      _data The block of memory to read from. -   \param      _size The size of the block of memory. -   \return A stream handle to use with the callbacks, or <code>NULL</code> on -            error.*/ -OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb, - const unsigned char *_data,size_t _size) OP_ARG_NONNULL(1); - -/**Creates a stream that reads from the given URL. -   This function behaves identically to op_url_stream_create(), except that it -    takes a va_list instead of a variable number of arguments. -   It does not call the <code>va_end</code> macro, and because it invokes the -    <code>va_arg</code> macro, the value of \a _ap is undefined after the call. -   \note If you use this function, you must link against <tt>libopusurl</tt>. -   \param[out]    _cb  The callbacks to use for this stream. -                       If there is an error creating the stream, nothing will -                        be filled in here. -   \param         _url The URL to read from. -                       Currently only the <file:>, <http:>, and <https:> -                        schemes are supported. -                       Both <http:> and <https:> may be disabled at compile -                        time, in which case opening such URLs will always fail. -                       Currently this only supports URIs. -                       IRIs should be converted to UTF-8 and URL-escaped, with -                        internationalized domain names encoded in punycode, -                        before passing them to this function. -   \param[in,out] _ap  A list of the \ref url_options "optional flags" to use. -                       This is a variable-length list of options terminated -                        with <code>NULL</code>. -   \return A stream handle to use with the callbacks, or <code>NULL</code> on -            error.*/ -OP_WARN_UNUSED_RESULT void *op_url_stream_vcreate(OpusFileCallbacks *_cb, - const char *_url,va_list _ap) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2); - -/**Creates a stream that reads from the given URL. -   \note If you use this function, you must link against <tt>libopusurl</tt>. -   \param[out] _cb  The callbacks to use for this stream. -                    If there is an error creating the stream, nothing will be -                     filled in here. -   \param      _url The URL to read from. -                    Currently only the <file:>, <http:>, and <https:> schemes -                     are supported. -                    Both <http:> and <https:> may be disabled at compile time, -                     in which case opening such URLs will always fail. -                    Currently this only supports URIs. -                    IRIs should be converted to UTF-8 and URL-escaped, with -                     internationalized domain names encoded in punycode, before -                     passing them to this function. -   \param      ...  The \ref url_options "optional flags" to use. -                    This is a variable-length list of options terminated with -                     <code>NULL</code>. -   \return A stream handle to use with the callbacks, or <code>NULL</code> on -            error.*/ -OP_WARN_UNUSED_RESULT void *op_url_stream_create(OpusFileCallbacks *_cb, - const char *_url,...) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2); - -/*@}*/ -/*@}*/ - -/**\defgroup stream_open_close Opening and Closing*/ -/*@{*/ -/**\name Functions for opening and closing streams - -   These functions allow you to test a stream to see if it is Opus, open it, -    and close it. -   Several flavors are provided for each of the built-in stream types, plus a -    more general version which takes a set of application-provided callbacks.*/ -/*@{*/ - -/**Test to see if this is an Opus stream. -   For good results, you will need at least 57 bytes (for a pure Opus-only -    stream). -   Something like 512 bytes will give more reliable results for multiplexed -    streams. -   This function is meant to be a quick-rejection filter. -   Its purpose is not to guarantee that a stream is a valid Opus stream, but to -    ensure that it looks enough like Opus that it isn't going to be recognized -    as some other format (except possibly an Opus stream that is also -    multiplexed with other codecs, such as video). -   \param[out] _head     The parsed ID header contents. -                         You may pass <code>NULL</code> if you do not need -                          this information. -                         If the function fails, the contents of this structure -                          remain untouched. -   \param _initial_data  An initial buffer of data from the start of the -                          stream. -   \param _initial_bytes The number of bytes in \a _initial_data. -   \return 0 if the data appears to be Opus, or a negative value on error. -   \retval #OP_FALSE      There was not enough data to tell if this was an Opus -                           stream or not. -   \retval #OP_EFAULT     An internal memory allocation failed. -   \retval #OP_EIMPL      The stream used a feature that is not implemented, -                           such as an unsupported channel family. -   \retval #OP_ENOTFORMAT If the data did not contain a recognizable ID -                           header for an Opus stream. -   \retval #OP_EVERSION   If the version field signaled a version this library -                           does not know how to parse. -   \retval #OP_EBADHEADER The ID header was not properly formatted or contained -                           illegal values.*/ -int op_test(OpusHead *_head, - const unsigned char *_initial_data,size_t _initial_bytes); - -/**Open a stream from the given file path. -   \param      _path  The path to the file to open. -   \param[out] _error Returns 0 on success, or a failure code on error. -                      You may pass in <code>NULL</code> if you don't want the -                       failure code. -                      The failure code will be #OP_EFAULT if the file could not -                       be opened, or one of the other failure codes from -                       op_open_callbacks() otherwise. -   \return A freshly opened \c OggOpusFile, or <code>NULL</code> on error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_open_file(const char *_path,int *_error) - OP_ARG_NONNULL(1); - -/**Open a stream from a memory buffer. -   \param      _data  The memory buffer to open. -   \param      _size  The number of bytes in the buffer. -   \param[out] _error Returns 0 on success, or a failure code on error. -                      You may pass in <code>NULL</code> if you don't want the -                       failure code. -                      See op_open_callbacks() for a full list of failure codes. -   \return A freshly opened \c OggOpusFile, or <code>NULL</code> on error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_open_memory(const unsigned char *_data, - size_t _size,int *_error); - -/**Open a stream from a URL. -   This function behaves identically to op_open_url(), except that it -    takes a va_list instead of a variable number of arguments. -   It does not call the <code>va_end</code> macro, and because it invokes the -    <code>va_arg</code> macro, the value of \a _ap is undefined after the call. -   \note If you use this function, you must link against <tt>libopusurl</tt>. -   \param         _url   The URL to open. -                         Currently only the <file:>, <http:>, and <https:> -                          schemes are supported. -                         Both <http:> and <https:> may be disabled at compile -                          time, in which case opening such URLs will always -                          fail. -                         Currently this only supports URIs. -                         IRIs should be converted to UTF-8 and URL-escaped, -                          with internationalized domain names encoded in -                          punycode, before passing them to this function. -   \param[out]    _error Returns 0 on success, or a failure code on error. -                         You may pass in <code>NULL</code> if you don't want -                          the failure code. -                         See op_open_callbacks() for a full list of failure -                          codes. -   \param[in,out] _ap    A list of the \ref url_options "optional flags" to -                          use. -                         This is a variable-length list of options terminated -                          with <code>NULL</code>. -   \return A freshly opened \c OggOpusFile, or <code>NULL</code> on error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_vopen_url(const char *_url, - int *_error,va_list _ap) OP_ARG_NONNULL(1); - -/**Open a stream from a URL. -   \note If you use this function, you must link against <tt>libopusurl</tt>. -   \param      _url   The URL to open. -                      Currently only the <file:>, <http:>, and <https:> schemes -                       are supported. -                      Both <http:> and <https:> may be disabled at compile -                       time, in which case opening such URLs will always fail. -                      Currently this only supports URIs. -                      IRIs should be converted to UTF-8 and URL-escaped, with -                       internationalized domain names encoded in punycode, -                       before passing them to this function. -   \param[out] _error Returns 0 on success, or a failure code on error. -                      You may pass in <code>NULL</code> if you don't want the -                       failure code. -                      See op_open_callbacks() for a full list of failure codes. -   \param      ...    The \ref url_options "optional flags" to use. -                      This is a variable-length list of options terminated with -                       <code>NULL</code>. -   \return A freshly opened \c OggOpusFile, or <code>NULL</code> on error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_open_url(const char *_url, - int *_error,...) OP_ARG_NONNULL(1); - -/**Open a stream using the given set of callbacks to access it. -   \param _source        The stream to read from (e.g., a <code>FILE *</code>). -   \param _cb            The callbacks with which to access the stream. -                         <code><a href="#op_read_func">read()</a></code> must -                          be implemented. -                         <code><a href="#op_seek_func">seek()</a></code> and -                          <code><a href="#op_tell_func">tell()</a></code> may -                          be <code>NULL</code>, or may always return -1 to -                          indicate a source is unseekable, but if -                          <code><a href="#op_seek_func">seek()</a></code> is -                          implemented and succeeds on a particular source, then -                          <code><a href="#op_tell_func">tell()</a></code> must -                          also. -                         <code><a href="#op_close_func">close()</a></code> may -                          be <code>NULL</code>, but if it is not, it will be -                          called when the \c OggOpusFile is destroyed by -                          op_free(). -                         It will not be called if op_open_callbacks() fails -                          with an error. -   \param _initial_data  An initial buffer of data from the start of the -                          stream. -                         Applications can read some number of bytes from the -                          start of the stream to help identify this as an Opus -                          stream, and then provide them here to allow the -                          stream to be opened, even if it is unseekable. -   \param _initial_bytes The number of bytes in \a _initial_data. -                         If the stream is seekable, its current position (as -                          reported by -                          <code><a href="#opus_tell_func">tell()</a></code> -                          at the start of this function) must be equal to -                          \a _initial_bytes. -                         Otherwise, seeking to absolute positions will -                          generate inconsistent results. -   \param[out] _error    Returns 0 on success, or a failure code on error. -                         You may pass in <code>NULL</code> if you don't want -                          the failure code. -                         The failure code will be one of -                         <dl> -                           <dt>#OP_EREAD</dt> -                           <dd>An underlying read, seek, or tell operation -                            failed when it should have succeeded, or we failed -                            to find data in the stream we had seen before.</dd> -                           <dt>#OP_EFAULT</dt> -                           <dd>There was a memory allocation failure, or an -                            internal library error.</dd> -                           <dt>#OP_EIMPL</dt> -                           <dd>The stream used a feature that is not -                            implemented, such as an unsupported channel -                            family.</dd> -                           <dt>#OP_EINVAL</dt> -                           <dd><code><a href="#op_seek_func">seek()</a></code> -                            was implemented and succeeded on this source, but -                            <code><a href="#op_tell_func">tell()</a></code> -                            did not, or the starting position indicator was -                            not equal to \a _initial_bytes.</dd> -                           <dt>#OP_ENOTFORMAT</dt> -                           <dd>The stream contained a link that did not have -                            any logical Opus streams in it.</dd> -                           <dt>#OP_EBADHEADER</dt> -                           <dd>A required header packet was not properly -                            formatted, contained illegal values, or was missing -                            altogether.</dd> -                           <dt>#OP_EVERSION</dt> -                           <dd>An ID header contained an unrecognized version -                            number.</dd> -                           <dt>#OP_EBADLINK</dt> -                           <dd>We failed to find data we had seen before after -                            seeking.</dd> -                           <dt>#OP_EBADTIMESTAMP</dt> -                           <dd>The first or last timestamp in a link failed -                            basic validity checks.</dd> -                         </dl> -   \return A freshly opened \c OggOpusFile, or <code>NULL</code> on error. -           <tt>libopusfile</tt> does <em>not</em> take ownership of the source -            if the call fails. -           The calling application is responsible for closing the source if -            this call returns an error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_open_callbacks(void *_source, - const OpusFileCallbacks *_cb,const unsigned char *_initial_data, - size_t _initial_bytes,int *_error) OP_ARG_NONNULL(2); - -/**Partially open a stream from the given file path. -   \see op_test_callbacks -   \param      _path  The path to the file to open. -   \param[out] _error Returns 0 on success, or a failure code on error. -                      You may pass in <code>NULL</code> if you don't want the -                       failure code. -                      The failure code will be #OP_EFAULT if the file could not -                       be opened, or one of the other failure codes from -                       op_open_callbacks() otherwise. -   \return A partially opened \c OggOpusFile, or <code>NULL</code> on error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_test_file(const char *_path,int *_error) - OP_ARG_NONNULL(1); - -/**Partially open a stream from a memory buffer. -   \see op_test_callbacks -   \param      _data  The memory buffer to open. -   \param      _size  The number of bytes in the buffer. -   \param[out] _error Returns 0 on success, or a failure code on error. -                      You may pass in <code>NULL</code> if you don't want the -                       failure code. -                      See op_open_callbacks() for a full list of failure codes. -   \return A partially opened \c OggOpusFile, or <code>NULL</code> on error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_test_memory(const unsigned char *_data, - size_t _size,int *_error); - -/**Partially open a stream from a URL. -   This function behaves identically to op_test_url(), except that it -    takes a va_list instead of a variable number of arguments. -   It does not call the <code>va_end</code> macro, and because it invokes the -    <code>va_arg</code> macro, the value of \a _ap is undefined after the call. -   \note If you use this function, you must link against <tt>libopusurl</tt>. -   \see op_test_url -   \see op_test_callbacks -   \param         _url    The URL to open. -                          Currently only the <file:>, <http:>, and <https:> -                           schemes are supported. -                          Both <http:> and <https:> may be disabled at compile -                           time, in which case opening such URLs will always -                           fail. -                          Currently this only supports URIs. -                          IRIs should be converted to UTF-8 and URL-escaped, -                           with internationalized domain names encoded in -                           punycode, before passing them to this function. -   \param[out]    _error  Returns 0 on success, or a failure code on error. -                          You may pass in <code>NULL</code> if you don't want -                           the failure code. -                          See op_open_callbacks() for a full list of failure -                           codes. -   \param[in,out] _ap     A list of the \ref url_options "optional flags" to -                           use. -                          This is a variable-length list of options terminated -                           with <code>NULL</code>. -   \return A partially opened \c OggOpusFile, or <code>NULL</code> on error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_vtest_url(const char *_url, - int *_error,va_list _ap) OP_ARG_NONNULL(1); - -/**Partially open a stream from a URL. -   \note If you use this function, you must link against <tt>libopusurl</tt>. -   \see op_test_callbacks -   \param      _url    The URL to open. -                       Currently only the <file:>, <http:>, and <https:> -                        schemes are supported. -                       Both <http:> and <https:> may be disabled at compile -                        time, in which case opening such URLs will always fail. -                       Currently this only supports URIs. -                       IRIs should be converted to UTF-8 and URL-escaped, with -                        internationalized domain names encoded in punycode, -                        before passing them to this function. -   \param[out] _error  Returns 0 on success, or a failure code on error. -                       You may pass in <code>NULL</code> if you don't want the -                        failure code. -                       See op_open_callbacks() for a full list of failure -                        codes. -   \param      ...     The \ref url_options "optional flags" to use. -                       This is a variable-length list of options terminated -                        with <code>NULL</code>. -   \return A partially opened \c OggOpusFile, or <code>NULL</code> on error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_test_url(const char *_url, - int *_error,...) OP_ARG_NONNULL(1); - -/**Partially open a stream using the given set of callbacks to access it. -   This tests for Opusness and loads the headers for the first link. -   It does not seek (although it tests for seekability). -   You can query a partially open stream for the few pieces of basic -    information returned by op_serialno(), op_channel_count(), op_head(), and -    op_tags() (but only for the first link). -   You may also determine if it is seekable via a call to op_seekable(). -   You cannot read audio from the stream, seek, get the size or duration, -    get information from links other than the first one, or even get the total -    number of links until you finish opening the stream with op_test_open(). -   If you do not need to do any of these things, you can dispose of it with -    op_free() instead. - -   This function is provided mostly to simplify porting existing code that used -    <tt>libvorbisfile</tt>. -   For new code, you are likely better off using op_test() instead, which -    is less resource-intensive, requires less data to succeed, and imposes a -    hard limit on the amount of data it examines (important for unseekable -    sources, where all such data must be buffered until you are sure of the -    stream type). -   \param _source        The stream to read from (e.g., a <code>FILE *</code>). -   \param _cb            The callbacks with which to access the stream. -                         <code><a href="#op_read_func">read()</a></code> must -                          be implemented. -                         <code><a href="#op_seek_func">seek()</a></code> and -                          <code><a href="#op_tell_func">tell()</a></code> may -                          be <code>NULL</code>, or may always return -1 to -                          indicate a source is unseekable, but if -                          <code><a href="#op_seek_func">seek()</a></code> is -                          implemented and succeeds on a particular source, then -                          <code><a href="#op_tell_func">tell()</a></code> must -                          also. -                         <code><a href="#op_close_func">close()</a></code> may -                          be <code>NULL</code>, but if it is not, it will be -                          called when the \c OggOpusFile is destroyed by -                          op_free(). -                         It will not be called if op_open_callbacks() fails -                          with an error. -   \param _initial_data  An initial buffer of data from the start of the -                          stream. -                         Applications can read some number of bytes from the -                          start of the stream to help identify this as an Opus -                          stream, and then provide them here to allow the -                          stream to be tested more thoroughly, even if it is -                          unseekable. -   \param _initial_bytes The number of bytes in \a _initial_data. -                         If the stream is seekable, its current position (as -                          reported by -                          <code><a href="#opus_tell_func">tell()</a></code> -                          at the start of this function) must be equal to -                          \a _initial_bytes. -                         Otherwise, seeking to absolute positions will -                          generate inconsistent results. -   \param[out] _error    Returns 0 on success, or a failure code on error. -                         You may pass in <code>NULL</code> if you don't want -                          the failure code. -                         See op_open_callbacks() for a full list of failure -                          codes. -   \return A partially opened \c OggOpusFile, or <code>NULL</code> on error. -           <tt>libopusfile</tt> does <em>not</em> take ownership of the source -            if the call fails. -           The calling application is responsible for closing the source if -            this call returns an error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_test_callbacks(void *_source, - const OpusFileCallbacks *_cb,const unsigned char *_initial_data, - size_t _initial_bytes,int *_error) OP_ARG_NONNULL(2); - -/**Finish opening a stream partially opened with op_test_callbacks() or one of -    the associated convenience functions. -   If this function fails, you are still responsible for freeing the -    \c OggOpusFile with op_free(). -   \param _of The \c OggOpusFile to finish opening. -   \return 0 on success, or a negative value on error. -   \retval #OP_EREAD         An underlying read, seek, or tell operation failed -                              when it should have succeeded. -   \retval #OP_EFAULT        There was a memory allocation failure, or an -                              internal library error. -   \retval #OP_EIMPL         The stream used a feature that is not implemented, -                              such as an unsupported channel family. -   \retval #OP_EINVAL        The stream was not partially opened with -                              op_test_callbacks() or one of the associated -                              convenience functions. -   \retval #OP_ENOTFORMAT    The stream contained a link that did not have any -                              logical Opus streams in it. -   \retval #OP_EBADHEADER    A required header packet was not properly -                              formatted, contained illegal values, or was -                              missing altogether. -   \retval #OP_EVERSION      An ID header contained an unrecognized version -                              number. -   \retval #OP_EBADLINK      We failed to find data we had seen before after -                              seeking. -   \retval #OP_EBADTIMESTAMP The first or last timestamp in a link failed basic -                              validity checks.*/ -int op_test_open(OggOpusFile *_of) OP_ARG_NONNULL(1); - -/**Release all memory used by an \c OggOpusFile. -   \param _of The \c OggOpusFile to free.*/ -void op_free(OggOpusFile *_of); - -/*@}*/ -/*@}*/ - -/**\defgroup stream_info Stream Information*/ -/*@{*/ -/**\name Functions for obtaining information about streams - -   These functions allow you to get basic information about a stream, including -    seekability, the number of links (for chained streams), plus the size, -    duration, bitrate, header parameters, and meta information for each link -    (or, where available, the stream as a whole). -   Some of these (size, duration) are only available for seekable streams. -   You can also query the current stream position, link, and playback time, -    and instantaneous bitrate during playback. - -   Some of these functions may be used successfully on the partially open -    streams returned by op_test_callbacks() or one of the associated -    convenience functions. -   Their documention will indicate so explicitly.*/ -/*@{*/ - -/**Returns whether or not the data source being read is seekable. -   This is true if -   <ol> -   <li>The <code><a href="#op_seek_func">seek()</a></code> and -    <code><a href="#op_tell_func">tell()</a></code> callbacks are both -    non-<code>NULL</code>,</li> -   <li>The <code><a href="#op_seek_func">seek()</a></code> callback was -    successfully executed at least once, and</li> -   <li>The <code><a href="#op_tell_func">tell()</a></code> callback was -    successfully able to report the position indicator afterwards.</li> -   </ol> -   This function may be called on partially-opened streams. -   \param _of The \c OggOpusFile whose seekable status is to be returned. -   \return A non-zero value if seekable, and 0 if unseekable.*/ -int op_seekable(const OggOpusFile *_of) OP_ARG_NONNULL(1); - -/**Returns the number of links in this chained stream. -   This function may be called on partially-opened streams, but it will always -    return 1. -   The actual number of links is not known until the stream is fully opened. -   \param _of The \c OggOpusFile from which to retrieve the link count. -   \return For fully-open seekable sources, this returns the total number of -            links in the whole stream, which will be at least 1. -           For partially-open or unseekable sources, this always returns 1.*/ -int op_link_count(const OggOpusFile *_of) OP_ARG_NONNULL(1); - -/**Get the serial number of the given link in a (possibly-chained) Ogg Opus -    stream. -   This function may be called on partially-opened streams, but it will always -    return the serial number of the Opus stream in the first link. -   \param _of The \c OggOpusFile from which to retrieve the serial number. -   \param _li The index of the link whose serial number should be retrieved. -              Use a negative number to get the serial number of the current -               link. -   \return The serial number of the given link. -           If \a _li is greater than the total number of links, this returns -            the serial number of the last link. -           If the source is not seekable, this always returns the serial number -            of the current link.*/ -opus_uint32 op_serialno(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); - -/**Get the channel count of the given link in a (possibly-chained) Ogg Opus -    stream. -   This is equivalent to <code>op_head(_of,_li)->channel_count</code>, but -    is provided for convenience. -   This function may be called on partially-opened streams, but it will always -    return the channel count of the Opus stream in the first link. -   \param _of The \c OggOpusFile from which to retrieve the channel count. -   \param _li The index of the link whose channel count should be retrieved. -              Use a negative number to get the channel count of the current -               link. -   \return The channel count of the given link. -           If \a _li is greater than the total number of links, this returns -            the channel count of the last link. -           If the source is not seekable, this always returns the channel count -            of the current link.*/ -int op_channel_count(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); - -/**Get the total (compressed) size of the stream, or of an individual link in -    a (possibly-chained) Ogg Opus stream, including all headers and Ogg muxing -    overhead. -   \param _of The \c OggOpusFile from which to retrieve the compressed size. -   \param _li The index of the link whose compressed size should be computed. -              Use a negative number to get the compressed size of the entire -               stream. -   \return The compressed size of the entire stream if \a _li is negative, the -            compressed size of link \a _li if it is non-negative, or a negative -            value on error. -           The compressed size of the entire stream may be smaller than that -            of the underlying source if trailing garbage was detected in the -            file. -   \retval #OP_EINVAL The source is not seekable (so we can't know the length), -                       \a _li wasn't less than the total number of links in -                       the stream, or the stream was only partially open.*/ -opus_int64 op_raw_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); - -/**Get the total PCM length (number of samples at 48 kHz) of the stream, or of -    an individual link in a (possibly-chained) Ogg Opus stream. -   Users looking for <code>op_time_total()</code> should use op_pcm_total() -    instead. -   Because timestamps in Opus are fixed at 48 kHz, there is no need for a -    separate function to convert this to seconds (and leaving it out avoids -    introducing floating point to the API, for those that wish to avoid it). -   \param _of The \c OggOpusFile from which to retrieve the PCM offset. -   \param _li The index of the link whose PCM length should be computed. -              Use a negative number to get the PCM length of the entire stream. -   \return The PCM length of the entire stream if \a _li is negative, the PCM -            length of link \a _li if it is non-negative, or a negative value on -            error. -   \retval #OP_EINVAL The source is not seekable (so we can't know the length), -                       \a _li wasn't less than the total number of links in -                       the stream, or the stream was only partially open.*/ -ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); - -/**Get the ID header information for the given link in a (possibly chained) Ogg -    Opus stream. -   This function may be called on partially-opened streams, but it will always -    return the ID header information of the Opus stream in the first link. -   \param _of The \c OggOpusFile from which to retrieve the ID header -               information. -   \param _li The index of the link whose ID header information should be -               retrieved. -              Use a negative number to get the ID header information of the -               current link. -              For an unseekable stream, \a _li is ignored, and the ID header -               information for the current link is always returned, if -               available. -   \return The contents of the ID header for the given link.*/ -const OpusHead *op_head(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); - -/**Get the comment header information for the given link in a (possibly -    chained) Ogg Opus stream. -   This function may be called on partially-opened streams, but it will always -    return the tags from the Opus stream in the first link. -   \param _of The \c OggOpusFile from which to retrieve the comment header -               information. -   \param _li The index of the link whose comment header information should be -               retrieved. -              Use a negative number to get the comment header information of -               the current link. -              For an unseekable stream, \a _li is ignored, and the comment -               header information for the current link is always returned, if -               available. -   \return The contents of the comment header for the given link, or -            <code>NULL</code> if this is an unseekable stream that encountered -            an invalid link.*/ -const OpusTags *op_tags(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); - -/**Retrieve the index of the current link. -   This is the link that produced the data most recently read by -    op_read_float() or its associated functions, or, after a seek, the link -    that the seek target landed in. -   Reading more data may advance the link index (even on the first read after a -    seek). -   \param _of The \c OggOpusFile from which to retrieve the current link index. -   \return The index of the current link on success, or a negative value on -            failure. -           For seekable streams, this is a number between 0 and the value -            returned by op_link_count(). -           For unseekable streams, this value starts at 0 and increments by one -            each time a new link is encountered (even though op_link_count() -            always returns 1). -   \retval #OP_EINVAL The stream was only partially open.*/ -int op_current_link(const OggOpusFile *_of) OP_ARG_NONNULL(1); - -/**Computes the bitrate for a given link in a (possibly chained) Ogg Opus -    stream. -   The stream must be seekable to compute the bitrate. -   For unseekable streams, use op_bitrate_instant() to get periodic estimates. -   \param _of The \c OggOpusFile from which to retrieve the bitrate. -   \param _li The index of the link whose bitrate should be computed. -              USe a negative number to get the bitrate of the whole stream. -   \return The bitrate on success, or a negative value on error. -   \retval #OP_EINVAL The stream was only partially open, the stream was not -                       seekable, or \a _li was larger than the number of -                       links.*/ -opus_int32 op_bitrate(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); - -/**Compute the instantaneous bitrate, measured as the ratio of bits to playable -    samples decoded since a) the last call to op_bitrate_instant(), b) the last -    seek, or c) the start of playback, whichever was most recent. -   This will spike somewhat after a seek or at the start/end of a chain -    boundary, as pre-skip, pre-roll, and end-trimming causes samples to be -    decoded but not played. -   \param _of The \c OggOpusFile from which to retrieve the bitrate. -   \return The bitrate, in bits per second, or a negative value on error. -   \retval #OP_FALSE  No data has been decoded since any of the events -                       described above. -   \retval #OP_EINVAL The stream was only partially open.*/ -opus_int32 op_bitrate_instant(OggOpusFile *_of) OP_ARG_NONNULL(1); - -/**Obtain the current value of the position indicator for \a _of. -   \param _of The \c OggOpusFile from which to retrieve the position indicator. -   \return The byte position that is currently being read from. -   \retval #OP_EINVAL The stream was only partially open.*/ -opus_int64 op_raw_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1); - -/**Obtain the PCM offset of the next sample to be read. -   If the stream is not properly timestamped, this might not increment by the -    proper amount between reads, or even return monotonically increasing -    values. -   \param _of The \c OggOpusFile from which to retrieve the PCM offset. -   \return The PCM offset of the next sample to be read. -   \retval #OP_EINVAL The stream was only partially open.*/ -ogg_int64_t op_pcm_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1); - -/*@}*/ -/*@}*/ - -/**\defgroup stream_seeking Seeking*/ -/*@{*/ -/**\name Functions for seeking in Opus streams - -   These functions let you seek in Opus streams, if the underlying source -    support it. -   Seeking is implemented for all built-in stream I/O routines, though some -    individual sources may not be seekable (pipes, live HTTP streams, or HTTP -    streams from a server that does not support <code>Range</code> requests). - -   op_raw_seek() is the fastest: it is guaranteed to perform at most one -    physical seek, but, since the target is a byte position, makes no guarantee -    how close to a given time it will come. -   op_pcm_seek() provides sample-accurate seeking. -   The number of physical seeks it requires is still quite small (often 1 or -    2, even in highly variable bitrate streams). - -   Seeking in Opus requires decoding some pre-roll amount before playback to -    allow the internal state to converge (as if recovering from packet loss). -   This is handled internally by <tt>libopusfile</tt>, but means there is -    little extra overhead for decoding up to the exact position requested -    (since it must decode some amount of audio anyway). -   It also means that decoding after seeking may not return exactly the same -    values as would be obtained by decoding the stream straight through. -   However, such differences are expected to be smaller than the loss -    introduced by Opus's lossy compression.*/ -/*@{*/ - -/**Seek to a byte offset relative to the <b>compressed</b> data. -   This also scans packets to update the PCM cursor. -   It will cross a logical bitstream boundary, but only if it can't get any -    packets out of the tail of the link to which it seeks. -   \param _of          The \c OggOpusFile in which to seek. -   \param _byte_offset The byte position to seek to. -   \return 0 on success, or a negative error code on failure. -   \retval #OP_EREAD    The underlying seek operation failed. -   \retval #OP_EINVAL   The stream was only partially open, or the target was -                         outside the valid range for the stream. -   \retval #OP_ENOSEEK  This stream is not seekable. -   \retval #OP_EBADLINK Failed to initialize a decoder for a stream for an -                         unknown reason.*/ -int op_raw_seek(OggOpusFile *_of,opus_int64 _byte_offset) OP_ARG_NONNULL(1); - -/**Seek to the specified PCM offset, such that decoding will begin at exactly -    the requested position. -   \param _of         The \c OggOpusFile in which to seek. -   \param _pcm_offset The PCM offset to seek to. -                      This is in samples at 48 kHz relative to the start of the -                       stream. -   \return 0 on success, or a negative value on error. -   \retval #OP_EREAD    An underlying read or seek operation failed. -   \retval #OP_EINVAL   The stream was only partially open, or the target was -                         outside the valid range for the stream. -   \retval #OP_ENOSEEK  This stream is not seekable. -   \retval #OP_EBADLINK We failed to find data we had seen before, or the -                         bitstream structure was sufficiently malformed that -                         seeking to the target destination was impossible.*/ -int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1); - -/*@}*/ -/*@}*/ - -/**\defgroup stream_decoding Decoding*/ -/*@{*/ -/**\name Functions for decoding audio data - -   These functions retrieve actual decoded audio data from the stream. -   The general functions, op_read() and op_read_float() return 16-bit or -    floating-point output, both using native endian ordering. -   The number of channels returned can change from link to link in a chained -    stream. -   There are special functions, op_read_stereo() and op_read_float_stereo(), -    which always output two channels, to simplify applications which do not -    wish to handle multichannel audio. -   These downmix multichannel files to two channels, so they can always return -    samples in the same format for every link in a chained file. - -   If the rest of your audio processing chain can handle floating point, those -    routines should be preferred, as floating point output avoids introducing -    clipping and other issues which might be avoided entirely if, e.g., you -    scale down the volume at some other stage. -   However, if you intend to direct consume 16-bit samples, the conversion in -    <tt>libopusfile</tt> provides noise-shaping dithering and, if compiled -    against <tt>libopus</tt> 1.1 or later, soft-clipping prevention. - -   <tt>libopusfile</tt> can also be configured at compile time to use the -    fixed-point <tt>libopus</tt> API. -   If so, <tt>libopusfile</tt>'s floating-point API may also be disabled. -   In that configuration, nothing in <tt>libopusfile</tt> will use any -    floating-point operations, to simplify support on devices without an -    adequate FPU. - -   \warning HTTPS streams may be be vulnerable to truncation attacks if you do -    not check the error return code from op_read_float() or its associated -    functions. -   If the remote peer does not close the connection gracefully (with a TLS -    "close notify" message), these functions will return #OP_EREAD instead of 0 -    when they reach the end of the file. -   If you are reading from an <https:> URL (particularly if seeking is not -    supported), you should make sure to check for this error and warn the user -    appropriately.*/ -/*@{*/ - -/**Indicates that the decoding callback should produce signed 16-bit -    native-endian output samples.*/ -#define OP_DEC_FORMAT_SHORT (7008) -/**Indicates that the decoding callback should produce 32-bit native-endian -    float samples.*/ -#define OP_DEC_FORMAT_FLOAT (7040) - -/**Indicates that the decoding callback did not decode anything, and that -    <tt>libopusfile</tt> should decode normally instead.*/ -#define OP_DEC_USE_DEFAULT  (6720) - -/**Called to decode an Opus packet. -   This should invoke the functional equivalent of opus_multistream_decode() or -    opus_multistream_decode_float(), except that it returns 0 on success -    instead of the number of decoded samples (which is known a priori). -   \param _ctx       The application-provided callback context. -   \param _decoder   The decoder to use to decode the packet. -   \param[out] _pcm  The buffer to decode into. -                     This will always have enough room for \a _nchannels of -                      \a _nsamples samples, which should be placed into this -                      buffer interleaved. -   \param _op        The packet to decode. -                     This will always have its granule position set to a valid -                      value. -   \param _nsamples  The number of samples expected from the packet. -   \param _nchannels The number of channels expected from the packet. -   \param _format    The desired sample output format. -                     This is either #OP_DEC_FORMAT_SHORT or -                      #OP_DEC_FORMAT_FLOAT. -   \param _li        The index of the link from which this packet was decoded. -   \return A non-negative value on success, or a negative value on error. -           The error codes should be the same as those returned by -            opus_multistream_decode() or opus_multistream_decode_float(). -   \retval 0                   Decoding was successful. -                               The application has filled the buffer with -                                exactly <code>\a _nsamples*\a -                                _nchannels</code> samples in the requested -                                format. -   \retval #OP_DEC_USE_DEFAULT No decoding was done. -                               <tt>libopusfile</tt> should decode normally -                                instead.*/ -typedef int (*op_decode_cb_func)(void *_ctx,OpusMSDecoder *_decoder,void *_pcm, - const ogg_packet *_op,int _nsamples,int _nchannels,int _format,int _li); - -/**Sets the packet decode callback function. -   This is called once for each packet that needs to be decoded. -   A call to this function is no guarantee that the audio will eventually be -    delivered to the application. -   Some or all of the data from the packet may be discarded (i.e., at the -    beginning or end of a link, or after a seek), however the callback is -    required to provide all of it. -   \param _of        The \c OggOpusFile on which to set the decode callback. -   \param _decode_cb The callback function to call. -                     This may be <code>NULL</code> to disable calling the -                      callback. -   \param _ctx       The application-provided context pointer to pass to the -                      callback on each call.*/ -void op_set_decode_callback(OggOpusFile *_of, - op_decode_cb_func _decode_cb,void *_ctx) OP_ARG_NONNULL(1); - -/**Gain offset type that indicates that the provided offset is relative to the -    header gain. -   This is the default.*/ -#define OP_HEADER_GAIN   (0) - -/**Gain offset type that indicates that the provided offset is relative to the -    R128_TRACK_GAIN value (if any), in addition to the header gain.*/ -#define OP_TRACK_GAIN    (3008) - -/**Gain offset type that indicates that the provided offset should be used as -    the gain directly, without applying any the header or track gains.*/ -#define OP_ABSOLUTE_GAIN (3009) - -/**Sets the gain to be used for decoded output. -   By default, the gain in the header is applied with no additional offset. -   The total gain (including header gain and/or track gain, if applicable, and -    this offset), will be clamped to [-32768,32767]/256 dB. -   This is more than enough to saturate or underflow 16-bit PCM. -   \note The new gain will not be applied to any already buffered, decoded -    output. -   This means you cannot change it sample-by-sample, as at best it will be -    updated packet-by-packet. -   It is meant for setting a target volume level, rather than applying smooth -    fades, etc. -   \param _of             The \c OggOpusFile on which to set the gain offset. -   \param _gain_type      One of #OP_HEADER_GAIN, #OP_TRACK_GAIN, or -                           #OP_ABSOLUTE_GAIN. -   \param _gain_offset_q8 The gain offset to apply, in 1/256ths of a dB. -   \return 0 on success or a negative value on error. -   \retval #OP_EINVAL The \a _gain_type was unrecognized.*/ -int op_set_gain_offset(OggOpusFile *_of, - int _gain_type,opus_int32 _gain_offset_q8) OP_ARG_NONNULL(1); - -/**Sets whether or not dithering is enabled for 16-bit decoding. -   By default, when <tt>libopusfile</tt> is compiled to use floating-point -    internally, calling op_read() or op_read_stereo() will first decode to -    float, and then convert to fixed-point using noise-shaping dithering. -   This flag can be used to disable that dithering. -   When the application uses op_read_float() or op_read_float_stereo(), or when -    the library has been compiled to decode directly to fixed point, this flag -    has no effect. -   \param _of      The \c OggOpusFile on which to enable or disable dithering. -   \param _enabled A non-zero value to enable dithering, or 0 to disable it.*/ -void op_set_dither_enabled(OggOpusFile *_of,int _enabled) OP_ARG_NONNULL(1); - -/**Reads more samples from the stream. -   \note Although \a _buf_size must indicate the total number of values that -    can be stored in \a _pcm, the return value is the number of samples -    <em>per channel</em>. -   This is done because -   <ol> -   <li>The channel count cannot be known a priori (reading more samples might -        advance us into the next link, with a different channel count), so -        \a _buf_size cannot also be in units of samples per channel,</li> -   <li>Returning the samples per channel matches the <code>libopus</code> API -        as closely as we're able,</li> -   <li>Returning the total number of values instead of samples per channel -        would mean the caller would need a division to compute the samples per -        channel, and might worry about the possibility of getting back samples -        for some channels and not others, and</li> -   <li>This approach is relatively fool-proof: if an application passes too -        small a value to \a _buf_size, they will simply get fewer samples back, -        and if they assume the return value is the total number of values, then -        they will simply read too few (rather than reading too many and going -        off the end of the buffer).</li> -   </ol> -   \param      _of       The \c OggOpusFile from which to read. -   \param[out] _pcm      A buffer in which to store the output PCM samples, as -                          signed native-endian 16-bit values at 48 kHz -                          with a nominal range of <code>[-32768,32767)</code>. -                         Multiple channels are interleaved using the -                          <a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis -                          channel ordering</a>. -                         This must have room for at least \a _buf_size values. -   \param      _buf_size The number of values that can be stored in \a _pcm. -                         It is recommended that this be large enough for at -                          least 120 ms of data at 48 kHz per channel (5760 -                          values per channel). -                         Smaller buffers will simply return less data, possibly -                          consuming more memory to buffer the data internally. -                         <tt>libopusfile</tt> may return less data than -                          requested. -                         If so, there is no guarantee that the remaining data -                          in \a _pcm will be unmodified. -   \param[out] _li       The index of the link this data was decoded from. -                         You may pass <code>NULL</code> if you do not need this -                          information. -                         If this function fails (returning a negative value), -                          this parameter is left unset. -   \return The number of samples read per channel on success, or a negative -            value on failure. -           The channel count can be retrieved on success by calling -            <code>op_head(_of,*_li)</code>. -           The number of samples returned may be 0 if the buffer was too small -            to store even a single sample for all channels, or if end-of-file -            was reached. -           The list of possible failure codes follows. -           Most of them can only be returned by unseekable, chained streams -            that encounter a new link. -   \retval #OP_HOLE          There was a hole in the data, and some samples -                              may have been skipped. -                             Call this function again to continue decoding -                              past the hole. -   \retval #OP_EREAD         An underlying read operation failed. -                             This may signal a truncation attack from an -                              <https:> source. -   \retval #OP_EFAULT        An internal memory allocation failed. -   \retval #OP_EIMPL         An unseekable stream encountered a new link that -                              used a feature that is not implemented, such as -                              an unsupported channel family. -   \retval #OP_EINVAL        The stream was only partially open. -   \retval #OP_ENOTFORMAT    An unseekable stream encountered a new link that -                              did not have any logical Opus streams in it. -   \retval #OP_EBADHEADER    An unseekable stream encountered a new link with a -                              required header packet that was not properly -                              formatted, contained illegal values, or was -                              missing altogether. -   \retval #OP_EVERSION      An unseekable stream encountered a new link with -                              an ID header that contained an unrecognized -                              version number. -   \retval #OP_EBADPACKET    Failed to properly decode the next packet. -   \retval #OP_EBADLINK      We failed to find data we had seen before. -   \retval #OP_EBADTIMESTAMP An unseekable stream encountered a new link with -                              a starting timestamp that failed basic validity -                              checks.*/ -OP_WARN_UNUSED_RESULT int op_read(OggOpusFile *_of, - opus_int16 *_pcm,int _buf_size,int *_li) OP_ARG_NONNULL(1); - -/**Reads more samples from the stream. -   \note Although \a _buf_size must indicate the total number of values that -    can be stored in \a _pcm, the return value is the number of samples -    <em>per channel</em>. -   <ol> -   <li>The channel count cannot be known a priori (reading more samples might -        advance us into the next link, with a different channel count), so -        \a _buf_size cannot also be in units of samples per channel,</li> -   <li>Returning the samples per channel matches the <code>libopus</code> API -        as closely as we're able,</li> -   <li>Returning the total number of values instead of samples per channel -        would mean the caller would need a division to compute the samples per -        channel, and might worry about the possibility of getting back samples -        for some channels and not others, and</li> -   <li>This approach is relatively fool-proof: if an application passes too -        small a value to \a _buf_size, they will simply get fewer samples back, -        and if they assume the return value is the total number of values, then -        they will simply read too few (rather than reading too many and going -        off the end of the buffer).</li> -   </ol> -   \param      _of       The \c OggOpusFile from which to read. -   \param[out] _pcm      A buffer in which to store the output PCM samples as -                          signed floats at 48 kHz with a nominal range of -                          <code>[-1.0,1.0]</code>. -                         Multiple channels are interleaved using the -                          <a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis -                          channel ordering</a>. -                         This must have room for at least \a _buf_size floats. -   \param      _buf_size The number of floats that can be stored in \a _pcm. -                         It is recommended that this be large enough for at -                          least 120 ms of data at 48 kHz per channel (5760 -                          samples per channel). -                         Smaller buffers will simply return less data, possibly -                          consuming more memory to buffer the data internally. -                         If less than \a _buf_size values are returned, -                          <tt>libopusfile</tt> makes no guarantee that the -                          remaining data in \a _pcm will be unmodified. -   \param[out] _li       The index of the link this data was decoded from. -                         You may pass <code>NULL</code> if you do not need this -                          information. -                         If this function fails (returning a negative value), -                          this parameter is left unset. -   \return The number of samples read per channel on success, or a negative -            value on failure. -           The channel count can be retrieved on success by calling -            <code>op_head(_of,*_li)</code>. -           The number of samples returned may be 0 if the buffer was too small -            to store even a single sample for all channels, or if end-of-file -            was reached. -           The list of possible failure codes follows. -           Most of them can only be returned by unseekable, chained streams -            that encounter a new link. -   \retval #OP_HOLE          There was a hole in the data, and some samples -                              may have been skipped. -                             Call this function again to continue decoding -                              past the hole. -   \retval #OP_EREAD         An underlying read operation failed. -                             This may signal a truncation attack from an -                              <https:> source. -   \retval #OP_EFAULT        An internal memory allocation failed. -   \retval #OP_EIMPL         An unseekable stream encountered a new link that -                              used a feature that is not implemented, such as -                              an unsupported channel family. -   \retval #OP_EINVAL        The stream was only partially open. -   \retval #OP_ENOTFORMAT    An unseekable stream encountered a new link that -                              did not have any logical Opus streams in it. -   \retval #OP_EBADHEADER    An unseekable stream encountered a new link with a -                              required header packet that was not properly -                              formatted, contained illegal values, or was -                              missing altogether. -   \retval #OP_EVERSION      An unseekable stream encountered a new link with -                              an ID header that contained an unrecognized -                              version number. -   \retval #OP_EBADPACKET    Failed to properly decode the next packet. -   \retval #OP_EBADLINK      We failed to find data we had seen before. -   \retval #OP_EBADTIMESTAMP An unseekable stream encountered a new link with -                              a starting timestamp that failed basic validity -                              checks.*/ -OP_WARN_UNUSED_RESULT int op_read_float(OggOpusFile *_of, - float *_pcm,int _buf_size,int *_li) OP_ARG_NONNULL(1); - -/**Reads more samples from the stream and downmixes to stereo, if necessary. -   This function is intended for simple players that want a uniform output -    format, even if the channel count changes between links in a chained -    stream. -   \note \a _buf_size indicates the total number of values that can be stored -    in \a _pcm, while the return value is the number of samples <em>per -    channel</em>, even though the channel count is known, for consistency with -    op_read(). -   \param      _of       The \c OggOpusFile from which to read. -   \param[out] _pcm      A buffer in which to store the output PCM samples, as -                          signed native-endian 16-bit values at 48 kHz -                          with a nominal range of <code>[-32768,32767)</code>. -                         The left and right channels are interleaved in the -                          buffer. -                         This must have room for at least \a _buf_size values. -   \param      _buf_size The number of values that can be stored in \a _pcm. -                         It is recommended that this be large enough for at -                          least 120 ms of data at 48 kHz per channel (11520 -                          values total). -                         Smaller buffers will simply return less data, possibly -                          consuming more memory to buffer the data internally. -                         If less than \a _buf_size values are returned, -                          <tt>libopusfile</tt> makes no guarantee that the -                          remaining data in \a _pcm will be unmodified. -   \return The number of samples read per channel on success, or a negative -            value on failure. -           The number of samples returned may be 0 if the buffer was too small -            to store even a single sample for both channels, or if end-of-file -            was reached. -           The list of possible failure codes follows. -           Most of them can only be returned by unseekable, chained streams -            that encounter a new link. -   \retval #OP_HOLE          There was a hole in the data, and some samples -                              may have been skipped. -                             Call this function again to continue decoding -                              past the hole. -   \retval #OP_EREAD         An underlying read operation failed. -                             This may signal a truncation attack from an -                              <https:> source. -   \retval #OP_EFAULT        An internal memory allocation failed. -   \retval #OP_EIMPL         An unseekable stream encountered a new link that -                              used a feature that is not implemented, such as -                              an unsupported channel family. -   \retval #OP_EINVAL        The stream was only partially open. -   \retval #OP_ENOTFORMAT    An unseekable stream encountered a new link that -                              did not have any logical Opus streams in it. -   \retval #OP_EBADHEADER    An unseekable stream encountered a new link with a -                              required header packet that was not properly -                              formatted, contained illegal values, or was -                              missing altogether. -   \retval #OP_EVERSION      An unseekable stream encountered a new link with -                              an ID header that contained an unrecognized -                              version number. -   \retval #OP_EBADPACKET    Failed to properly decode the next packet. -   \retval #OP_EBADLINK      We failed to find data we had seen before. -   \retval #OP_EBADTIMESTAMP An unseekable stream encountered a new link with -                              a starting timestamp that failed basic validity -                              checks.*/ -OP_WARN_UNUSED_RESULT int op_read_stereo(OggOpusFile *_of, - opus_int16 *_pcm,int _buf_size) OP_ARG_NONNULL(1); - -/**Reads more samples from the stream and downmixes to stereo, if necessary. -   This function is intended for simple players that want a uniform output -    format, even if the channel count changes between links in a chained -    stream. -   \note \a _buf_size indicates the total number of values that can be stored -    in \a _pcm, while the return value is the number of samples <em>per -    channel</em>, even though the channel count is known, for consistency with -    op_read_float(). -   \param      _of       The \c OggOpusFile from which to read. -   \param[out] _pcm      A buffer in which to store the output PCM samples, as -                          signed floats at 48 kHz with a nominal range of -                          <code>[-1.0,1.0]</code>. -                         The left and right channels are interleaved in the -                          buffer. -                         This must have room for at least \a _buf_size values. -   \param      _buf_size The number of values that can be stored in \a _pcm. -                         It is recommended that this be large enough for at -                          least 120 ms of data at 48 kHz per channel (11520 -                          values total). -                         Smaller buffers will simply return less data, possibly -                          consuming more memory to buffer the data internally. -                         If less than \a _buf_size values are returned, -                          <tt>libopusfile</tt> makes no guarantee that the -                          remaining data in \a _pcm will be unmodified. -   \return The number of samples read per channel on success, or a negative -            value on failure. -           The number of samples returned may be 0 if the buffer was too small -            to store even a single sample for both channels, or if end-of-file -            was reached. -           The list of possible failure codes follows. -           Most of them can only be returned by unseekable, chained streams -            that encounter a new link. -   \retval #OP_HOLE          There was a hole in the data, and some samples -                              may have been skipped. -                             Call this function again to continue decoding -                              past the hole. -   \retval #OP_EREAD         An underlying read operation failed. -                             This may signal a truncation attack from an -                              <https:> source. -   \retval #OP_EFAULT        An internal memory allocation failed. -   \retval #OP_EIMPL         An unseekable stream encountered a new link that -                              used a feature that is not implemented, such as -                              an unsupported channel family. -   \retval #OP_EINVAL        The stream was only partially open. -   \retval #OP_ENOTFORMAT    An unseekable stream encountered a new link that -                              that did not have any logical Opus streams in it. -   \retval #OP_EBADHEADER    An unseekable stream encountered a new link with a -                              required header packet that was not properly -                              formatted, contained illegal values, or was -                              missing altogether. -   \retval #OP_EVERSION      An unseekable stream encountered a new link with -                              an ID header that contained an unrecognized -                              version number. -   \retval #OP_EBADPACKET    Failed to properly decode the next packet. -   \retval #OP_EBADLINK      We failed to find data we had seen before. -   \retval #OP_EBADTIMESTAMP An unseekable stream encountered a new link with -                              a starting timestamp that failed basic validity -                              checks.*/ -OP_WARN_UNUSED_RESULT int op_read_float_stereo(OggOpusFile *_of, - float *_pcm,int _buf_size) OP_ARG_NONNULL(1); - -/*@}*/ -/*@}*/ - -# if OP_GNUC_PREREQ(4,0) -#  pragma GCC visibility pop -# endif - -# if defined(__cplusplus) -} -# endif - -#endif diff --git a/src/opusfile-0.5/src/http.c b/src/opusfile-0.5/src/http.c deleted file mode 100644 index 4a9eaf59..00000000 --- a/src/opusfile-0.5/src/http.c +++ /dev/null @@ -1,3391 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012                * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ********************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "internal.h" -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <string.h> - -/*RFCs referenced in this file: -  RFC  761: DOD Standard Transmission Control Protocol -  RFC 1535: A Security Problem and Proposed Correction With Widely Deployed DNS -   Software -  RFC 1738: Uniform Resource Locators (URL) -  RFC 1945: Hypertext Transfer Protocol -- HTTP/1.0 -  RFC 2068: Hypertext Transfer Protocol -- HTTP/1.1 -  RFC 2145: Use and Interpretation of HTTP Version Numbers -  RFC 2246: The TLS Protocol Version 1.0 -  RFC 2459: Internet X.509 Public Key Infrastructure Certificate and -   Certificate Revocation List (CRL) Profile -  RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1 -  RFC 2617: HTTP Authentication: Basic and Digest Access Authentication -  RFC 2817: Upgrading to TLS Within HTTP/1.1 -  RFC 2818: HTTP Over TLS -  RFC 3492: Punycode: A Bootstring encoding of Unicode for Internationalized -   Domain Names in Applications (IDNA) -  RFC 3986: Uniform Resource Identifier (URI): Generic Syntax -  RFC 3987: Internationalized Resource Identifiers (IRIs) -  RFC 4343: Domain Name System (DNS) Case Insensitivity Clarification -  RFC 5894: Internationalized Domain Names for Applications (IDNA): -   Background, Explanation, and Rationale -  RFC 6066: Transport Layer Security (TLS) Extensions: Extension Definitions -  RFC 6125: Representation and Verification of Domain-Based Application Service -   Identity within Internet Public Key Infrastructure Using X.509 (PKIX) -   Certificates in the Context of Transport Layer Security (TLS) -  RFC 6555: Happy Eyeballs: Success with Dual-Stack Hosts*/ - -typedef struct OpusParsedURL   OpusParsedURL; -typedef struct OpusStringBuf   OpusStringBuf; -typedef struct OpusHTTPConn    OpusHTTPConn; -typedef struct OpusHTTPStream  OpusHTTPStream; - -static char *op_string_range_dup(const char *_start,const char *_end){ -  size_t  len; -  char   *ret; -  OP_ASSERT(_start<=_end); -  len=_end-_start; -  /*This is to help avoid overflow elsewhere, later.*/ -  if(OP_UNLIKELY(len>=INT_MAX))return NULL; -  ret=(char *)_ogg_malloc(sizeof(*ret)*(len+1)); -  if(OP_LIKELY(ret!=NULL)){ -    ret=(char *)memcpy(ret,_start,sizeof(*ret)*(len)); -    ret[len]='\0'; -  } -  return ret; -} - -static char *op_string_dup(const char *_s){ -  return op_string_range_dup(_s,_s+strlen(_s)); -} - -static char *op_string_tolower(char *_s){ -  int i; -  for(i=0;_s[i]!='\0';i++){ -    int c; -    c=_s[i]; -    if(c>='A'&&c<='Z')c+='a'-'A'; -    _s[i]=(char)c; -  } -  return _s; -} - -/*URI character classes (from RFC 3986).*/ -#define OP_URL_ALPHA \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" -#define OP_URL_DIGIT       "0123456789" -#define OP_URL_HEXDIGIT    "0123456789ABCDEFabcdef" -/*Not a character class, but the characters allowed in <scheme>.*/ -#define OP_URL_SCHEME      OP_URL_ALPHA OP_URL_DIGIT "+-." -#define OP_URL_GEN_DELIMS  "#/:?@[]" -#define OP_URL_SUB_DELIMS  "!$&'()*+,;=" -#define OP_URL_RESERVED    OP_URL_GEN_DELIMS OP_URL_SUB_DELIMS -#define OP_URL_UNRESERVED  OP_URL_ALPHA OP_URL_DIGIT "-._~" -/*Not a character class, but the characters allowed in <pct-encoded>.*/ -#define OP_URL_PCT_ENCODED "%" -/*Not a character class or production rule, but for convenience.*/ -#define OP_URL_PCHAR_BASE \ - OP_URL_UNRESERVED OP_URL_PCT_ENCODED OP_URL_SUB_DELIMS -#define OP_URL_PCHAR       OP_URL_PCHAR_BASE ":@" -/*Not a character class, but the characters allowed in <userinfo> and -   <IP-literal>.*/ -#define OP_URL_PCHAR_NA    OP_URL_PCHAR_BASE ":" -/*Not a character class, but the characters allowed in <segment-nz-nc>.*/ -#define OP_URL_PCHAR_NC    OP_URL_PCHAR_BASE "@" -/*Not a character clsss, but the characters allowed in <path>.*/ -#define OP_URL_PATH        OP_URL_PCHAR "/" -/*Not a character class, but the characters allowed in <query> / <fragment>.*/ -#define OP_URL_QUERY_FRAG  OP_URL_PCHAR "/?" - -/*Check the <% HEXDIG HEXDIG> escapes of a URL for validity. -  Return: 0 if valid, or a negative value on failure.*/ -static int op_validate_url_escapes(const char *_s){ -  int i; -  for(i=0;_s[i];i++){ -    if(_s[i]=='%'){ -      if(OP_UNLIKELY(!isxdigit(_s[i+1])) -       ||OP_UNLIKELY(!isxdigit(_s[i+2])) -       /*RFC 3986 says %00 "should be rejected if the application is not -          expecting to receive raw data within a component."*/ -       ||OP_UNLIKELY(_s[i+1]=='0'&&_s[i+2]=='0')){ -        return OP_FALSE; -      } -      i+=2; -    } -  } -  return 0; -} - -/*Convert a hex digit to its actual value. -  _c: The hex digit to convert. -      Presumed to be valid ('0'...'9', 'A'...'F', or 'a'...'f'). -  Return: The value of the digit, in the range [0,15].*/ -static int op_hex_value(int _c){ -  return _c>='a'?_c-'a'+10:_c>='A'?_c-'A'+10:_c-'0'; -} - -/*Unescape all the <% HEXDIG HEXDIG> sequences in a string in-place. -  This does no validity checking.*/ -static char *op_unescape_url_component(char *_s){ -  int i; -  int j; -  for(i=j=0;_s[i];i++,j++){ -    if(_s[i]=='%'){ -      _s[i]=(char)(op_hex_value(_s[i+1])<<4|op_hex_value(_s[i+2])); -      i+=2; -    } -  } -  return _s; -} - -/*Parse a file: URL. -  This code is not meant to be fast: strspn() with large sets is likely to be -   slow, but it is very convenient. -  It is meant to be RFC 1738-compliant (as updated by RFC 3986).*/ -static const char *op_parse_file_url(const char *_src){ -  const char *scheme_end; -  const char *path; -  const char *path_end; -  scheme_end=_src+strspn(_src,OP_URL_SCHEME); -  if(OP_UNLIKELY(*scheme_end!=':') -   ||scheme_end-_src!=4||op_strncasecmp(_src,"file",4)!=0){ -    /*Unsupported protocol.*/ -    return NULL; -  } -  /*Make sure all escape sequences are valid to simplify unescaping later.*/ -  if(OP_UNLIKELY(op_validate_url_escapes(scheme_end+1)<0))return NULL; -  if(scheme_end[1]=='/'&&scheme_end[2]=='/'){ -    const char *host; -    /*file: URLs can have a host! -      Yeah, I was surprised, too, but that's what RFC 1738 says. -      It also says, "The file URL scheme is unusual in that it does not specify -       an Internet protocol or access method for such files; as such, its -       utility in network protocols between hosts is limited," which is a mild -       understatement.*/ -    host=scheme_end+3; -    /*The empty host is what we expect.*/ -    if(OP_LIKELY(*host=='/'))path=host; -    else{ -      const char *host_end; -      char        host_buf[28]; -      /*RFC 1738 says localhost "is interpreted as `the machine from which the -         URL is being interpreted,'" so let's check for it.*/ -      host_end=host+strspn(host,OP_URL_PCHAR_BASE); -      /*No <port> allowed. -        This also rejects IP-Literals.*/ -      if(*host_end!='/')return NULL; -      /*An escaped "localhost" can take at most 27 characters.*/ -      if(OP_UNLIKELY(host_end-host>27))return NULL; -      memcpy(host_buf,host,sizeof(*host_buf)*(host_end-host)); -      host_buf[host_end-host]='\0'; -      op_unescape_url_component(host_buf); -      op_string_tolower(host_buf); -      /*Some other host: give up.*/ -      if(OP_UNLIKELY(strcmp(host_buf,"localhost")!=0))return NULL; -      path=host_end; -    } -  } -  else path=scheme_end+1; -  path_end=path+strspn(path,OP_URL_PATH); -  /*This will reject a <query> or <fragment> component, too. -    I don't know what to do with queries, but a temporal fragment would at -     least make sense. -    RFC 1738 pretty clearly defines a <searchpart> that's equivalent to the -     RFC 3986 <query> component for other schemes, but not the file: scheme, -     so I'm going to just reject it.*/ -  if(*path_end!='\0')return NULL; -  return path; -} - -#if defined(OP_ENABLE_HTTP) -# if defined(_WIN32) -#  include <winsock2.h> -#  include <ws2tcpip.h> -#  include <openssl/ssl.h> -#  include "winerrno.h" - -typedef SOCKET op_sock; - -#  define OP_INVALID_SOCKET (INVALID_SOCKET) - -/*Vista and later support WSAPoll(), but we don't want to rely on that. -  Instead we re-implement it badly using select(). -  Unfortunately, they define a conflicting struct pollfd, so we only define our -   own if it looks like that one has not already been defined.*/ -#  if !defined(POLLIN) -/*Equivalent to POLLIN.*/ -#   define POLLRDNORM (0x0100) -/*Priority band data can be read.*/ -#   define POLLRDBAND (0x0200) -/*There is data to read.*/ -#   define POLLIN     (POLLRDNORM|POLLRDBAND) -/* There is urgent data to read.*/ -#   define POLLPRI    (0x0400) -/*Equivalent to POLLOUT.*/ -#   define POLLWRNORM (0x0010) -/*Writing now will not block.*/ -#   define POLLOUT    (POLLWRNORM) -/*Priority data may be written.*/ -#   define POLLWRBAND (0x0020) -/*Error condition (output only).*/ -#   define POLLERR    (0x0001) -/*Hang up (output only).*/ -#   define POLLHUP    (0x0002) -/*Invalid request: fd not open (output only).*/ -#   define POLLNVAL   (0x0004) - -struct pollfd{ -  /*File descriptor.*/ -  op_sock fd; -  /*Requested events.*/ -  short   events; -  /*Returned events.*/ -  short   revents; -}; -#  endif - -/*But Winsock never defines nfds_t (it's simply hard-coded to ULONG).*/ -typedef unsigned long nfds_t; - -/*The usage of FD_SET() below is O(N^2). -  This is okay because select() is limited to 64 sockets in Winsock, anyway. -  In practice, we only ever call it with one or two sockets.*/ -static int op_poll_win32(struct pollfd *_fds,nfds_t _nfds,int _timeout){ -  struct timeval tv; -  fd_set         ifds; -  fd_set         ofds; -  fd_set         efds; -  nfds_t         i; -  int            ret; -  FD_ZERO(&ifds); -  FD_ZERO(&ofds); -  FD_ZERO(&efds); -  for(i=0;i<_nfds;i++){ -    _fds[i].revents=0; -    if(_fds[i].events&POLLIN)FD_SET(_fds[i].fd,&ifds); -    if(_fds[i].events&POLLOUT)FD_SET(_fds[i].fd,&ofds); -    FD_SET(_fds[i].fd,&efds); -  } -  if(_timeout>=0){ -    tv.tv_sec=_timeout/1000; -    tv.tv_usec=(_timeout%1000)*1000; -  } -  ret=select(-1,&ifds,&ofds,&efds,_timeout<0?NULL:&tv); -  if(ret>0){ -    for(i=0;i<_nfds;i++){ -      if(FD_ISSET(_fds[i].fd,&ifds))_fds[i].revents|=POLLIN; -      if(FD_ISSET(_fds[i].fd,&ofds))_fds[i].revents|=POLLOUT; -      /*This isn't correct: there are several different things that might have -         happened to a fd in efds, but I don't know a good way to distinguish -         them without more context from the caller. -        It's okay, because we don't actually check any of these bits, we just -         need _some_ bit set.*/ -      if(FD_ISSET(_fds[i].fd,&efds))_fds[i].revents|=POLLHUP; -    } -  } -  return ret; -} - -/*We define op_errno() to make it clear that it's not an l-value like normal -   errno is.*/ -#  define op_errno() (WSAGetLastError()?WSAGetLastError()-WSABASEERR:0) -#  define op_reset_errno() (WSASetLastError(0)) - -/*The remaining functions don't get an op_ prefix even though they only -   operate on sockets, because we don't use non-socket I/O here, and this -   minimizes the changes needed to deal with Winsock.*/ -#  define close(_fd) closesocket(_fd) -/*This relies on sizeof(u_long)==sizeof(int), which is always true on both -   Win32 and Win64.*/ -#  define ioctl(_fd,_req,_arg) ioctlsocket(_fd,_req,(u_long *)(_arg)) -#  define getsockopt(_fd,_level,_name,_val,_len) \ - getsockopt(_fd,_level,_name,(char *)(_val),_len) -#  define setsockopt(_fd,_level,_name,_val,_len) \ - setsockopt(_fd,_level,_name,(const char *)(_val),_len) -#  define poll(_fds,_nfds,_timeout) op_poll_win32(_fds,_nfds,_timeout) - -#  if defined(_MSC_VER) -typedef ptrdiff_t ssize_t; -#  endif - -/*Load certificates from the built-in certificate store.*/ -int SSL_CTX_set_default_verify_paths_win32(SSL_CTX *_ssl_ctx); -#  define SSL_CTX_set_default_verify_paths \ - SSL_CTX_set_default_verify_paths_win32 - -# else -/*Normal Berkeley sockets.*/ -#  include <sys/ioctl.h> -#  include <sys/types.h> -#  include <sys/socket.h> -#  include <arpa/inet.h> -#  include <netinet/in.h> -#  include <netinet/tcp.h> -#  include <fcntl.h> -#  include <netdb.h> -#  include <poll.h> -#  include <unistd.h> -#  include <openssl/ssl.h> - -typedef int op_sock; - -#  define OP_INVALID_SOCKET (-1) - -#  define op_errno() (errno) -#  define op_reset_errno() (errno=0) - -# endif -# include <sys/timeb.h> -# include <openssl/x509v3.h> - -/*The maximum number of simultaneous connections. -  RFC 2616 says this SHOULD NOT be more than 2, but everyone on the modern web -   ignores that (e.g., IE 8 bumped theirs up from 2 to 6, Firefox uses 15). -  If it makes you feel better, we'll only ever actively read from one of these -   at a time. -  The others are kept around mainly to avoid slow-starting a new connection -   when seeking, and time out rapidly.*/ -# define OP_NCONNS_MAX (4) - -/*The amount of time before we attempt to re-resolve the host. -  This is 10 minutes, as recommended in RFC 6555 for expiring cached connection -   results for dual-stack hosts.*/ -# define OP_RESOLVE_CACHE_TIMEOUT_MS (10*60*(opus_int32)1000) - -/*The number of redirections at which we give up. -  The value here is the current default in Firefox. -  RFC 2068 mandated a maximum of 5, but RFC 2616 relaxed that to "a client -   SHOULD detect infinite redirection loops." -  Fortunately, 20 is less than infinity.*/ -# define OP_REDIRECT_LIMIT (20) - -/*The initial size of the buffer used to read a response message (before the -   body).*/ -# define OP_RESPONSE_SIZE_MIN (510) -/*The maximum size of a response message (before the body). -  Responses larger than this will be discarded. -  I've seen a real server return 20 kB of data for a 302 Found response. -  Increasing this beyond 32kB will cause problems on platforms with a 16-bit -   int.*/ -# define OP_RESPONSE_SIZE_MAX (32766) - -/*The number of milliseconds we will allow a connection to sit idle before we -   refuse to resurrect it. -  Apache as of 2.2 has reduced its default timeout to 5 seconds (from 15), so -   that's what we'll use here.*/ -# define OP_CONNECTION_IDLE_TIMEOUT_MS (5*1000) - -/*The number of milliseconds we will wait to send or receive data before giving -   up.*/ -# define OP_POLL_TIMEOUT_MS (30*1000) - -/*We will always attempt to read ahead at least this much in preference to -   opening a new connection.*/ -# define OP_READAHEAD_THRESH_MIN (32*(opus_int32)1024) - -/*The amount of data to request after a seek. -  This is a trade-off between read throughput after a seek vs. the the ability -   to quickly perform another seek with the same connection.*/ -# define OP_PIPELINE_CHUNK_SIZE     (32*(opus_int32)1024) -/*Subsequent chunks are requested with larger and larger sizes until they pass -   this threshold, after which we just ask for the rest of the resource.*/ -# define OP_PIPELINE_CHUNK_SIZE_MAX (1024*(opus_int32)1024) -/*This is the maximum number of requests we'll make with a single connection. -  Many servers will simply disconnect after we attempt some number of requests, -   possibly without sending a Connection: close header, meaning we won't -   discover it until we try to read beyond the end of the current chunk. -  We can reconnect when that happens, but this is slow. -  Instead, we impose a limit ourselves (set to the default for Apache -   installations and thus likely the most common value in use).*/ -# define OP_PIPELINE_MAX_REQUESTS   (100) -/*This should be the number of requests, starting from a chunk size of -   OP_PIPELINE_CHUNK_SIZE and doubling each time, until we exceed -   OP_PIPELINE_CHUNK_SIZE_MAX and just request the rest of the file. -  We won't reuse a connection when seeking unless it has at least this many -   requests left, to reduce the chances we'll have to open a new connection -   while reading forward afterwards.*/ -# define OP_PIPELINE_MIN_REQUESTS   (7) - -/*Is this an https URL? -  For now we can simply check the last letter of the scheme.*/ -# define OP_URL_IS_SSL(_url) ((_url)->scheme[4]=='s') - -/*Does this URL use the default port for its scheme?*/ -# define OP_URL_IS_DEFAULT_PORT(_url) \ - (!OP_URL_IS_SSL(_url)&&(_url)->port==80 \ - ||OP_URL_IS_SSL(_url)&&(_url)->port==443) - -struct OpusParsedURL{ -  /*Either "http" or "https".*/ -  char     *scheme; -  /*The user name from the <userinfo> component, or NULL.*/ -  char     *user; -  /*The password from the <userinfo> component, or NULL.*/ -  char     *pass; -  /*The <host> component. -    This may not be NULL.*/ -  char     *host; -  /*The <path> and <query> components. -    This may not be NULL.*/ -  char     *path; -  /*The <port> component. -    This is set to the default port if the URL did not contain one.*/ -  unsigned  port; -}; - -/*Parse a URL. -  This code is not meant to be fast: strspn() with large sets is likely to be -   slow, but it is very convenient. -  It is meant to be RFC 3986-compliant. -  We currently do not support IRIs (Internationalized Resource Identifiers, -   RFC 3987). -  Callers should translate them to URIs first.*/ -static int op_parse_url_impl(OpusParsedURL *_dst,const char *_src){ -  const char  *scheme_end; -  const char  *authority; -  const char  *userinfo_end; -  const char  *user; -  const char  *user_end; -  const char  *pass; -  const char  *hostport; -  const char  *hostport_end; -  const char  *host_end; -  const char  *port; -  opus_int32   port_num; -  const char  *port_end; -  const char  *path; -  const char  *path_end; -  const char  *uri_end; -  scheme_end=_src+strspn(_src,OP_URL_SCHEME); -  if(OP_UNLIKELY(*scheme_end!=':') -   ||OP_UNLIKELY(scheme_end-_src<4)||OP_UNLIKELY(scheme_end-_src>5) -   ||OP_UNLIKELY(op_strncasecmp(_src,"https",scheme_end-_src)!=0)){ -    /*Unsupported protocol.*/ -    return OP_EIMPL; -  } -  if(OP_UNLIKELY(scheme_end[1]!='/')||OP_UNLIKELY(scheme_end[2]!='/')){ -    /*We require an <authority> component.*/ -    return OP_EINVAL; -  } -  authority=scheme_end+3; -  /*Make sure all escape sequences are valid to simplify unescaping later.*/ -  if(OP_UNLIKELY(op_validate_url_escapes(authority)<0))return OP_EINVAL; -  /*Look for a <userinfo> component.*/ -  userinfo_end=authority+strspn(authority,OP_URL_PCHAR_NA); -  if(*userinfo_end=='@'){ -    /*Found one.*/ -    user=authority; -    /*Look for a password (yes, clear-text passwords are deprecated, I know, -       but what else are people supposed to use? use SSL if you care).*/ -    user_end=authority+strspn(authority,OP_URL_PCHAR_BASE); -    if(*user_end==':')pass=user_end+1; -    else pass=NULL; -    hostport=userinfo_end+1; -  } -  else{ -    /*We shouldn't have to initialize user_end, but gcc is too dumb to figure -       out that user!=NULL below means we didn't take this else branch.*/ -    user=user_end=NULL; -    pass=NULL; -    hostport=authority; -  } -  /*Try to figure out where the <host> component ends.*/ -  if(hostport[0]=='['){ -    hostport++; -    /*We have an <IP-literal>, which can contain colons.*/ -    hostport_end=host_end=hostport+strspn(hostport,OP_URL_PCHAR_NA); -    if(OP_UNLIKELY(*hostport_end++!=']'))return OP_EINVAL; -  } -  /*Currently we don't support IDNA (RFC 5894), because I don't want to deal -     with the policy about which domains should not be internationalized to -     avoid confusing similarities. -    Give this API Punycode (RFC 3492) domain names instead.*/ -  else hostport_end=host_end=hostport+strspn(hostport,OP_URL_PCHAR_BASE); -  /*TODO: Validate host.*/ -  /*Is there a port number?*/ -  port_num=-1; -  if(*hostport_end==':'){ -    int i; -    port=hostport_end+1; -    port_end=port+strspn(port,OP_URL_DIGIT); -    path=port_end; -    /*Not part of RFC 3986, but require port numbers in the range 0...65535.*/ -    if(OP_LIKELY(port_end-port>0)){ -      while(*port=='0')port++; -      if(OP_UNLIKELY(port_end-port>5))return OP_EINVAL; -      port_num=0; -      for(i=0;i<port_end-port;i++)port_num=port_num*10+port[i]-'0'; -      if(OP_UNLIKELY(port_num>65535))return OP_EINVAL; -    } -  } -  else path=hostport_end; -  path_end=path+strspn(path,OP_URL_PATH); -  /*If the path is not empty, it must begin with a '/'.*/ -  if(OP_LIKELY(path_end>path)&&OP_UNLIKELY(path[0]!='/'))return OP_EINVAL; -  /*Consume the <query> component, if any (right now we don't split this out -     from the <path> component).*/ -  if(*path_end=='?')path_end=path_end+strspn(path_end,OP_URL_QUERY_FRAG); -  /*Discard the <fragment> component, if any. -    This doesn't get sent to the server. -    Some day we should add support for Media Fragment URIs -     <http://www.w3.org/TR/media-frags/>.*/ -  if(*path_end=='#')uri_end=path_end+1+strspn(path_end+1,OP_URL_QUERY_FRAG); -  else uri_end=path_end; -  /*If there's anything left, this was not a valid URL.*/ -  if(OP_UNLIKELY(*uri_end!='\0'))return OP_EINVAL; -  _dst->scheme=op_string_range_dup(_src,scheme_end); -  if(OP_UNLIKELY(_dst->scheme==NULL))return OP_EFAULT; -  op_string_tolower(_dst->scheme); -  if(user!=NULL){ -    _dst->user=op_string_range_dup(user,user_end); -    if(OP_UNLIKELY(_dst->user==NULL))return OP_EFAULT; -    op_unescape_url_component(_dst->user); -    /*Unescaping might have created a ':' in the username. -      That's not allowed by RFC 2617's Basic Authentication Scheme.*/ -    if(OP_UNLIKELY(strchr(_dst->user,':')!=NULL))return OP_EINVAL; -  } -  else _dst->user=NULL; -  if(pass!=NULL){ -    _dst->pass=op_string_range_dup(pass,userinfo_end); -    if(OP_UNLIKELY(_dst->pass==NULL))return OP_EFAULT; -    op_unescape_url_component(_dst->pass); -  } -  else _dst->pass=NULL; -  _dst->host=op_string_range_dup(hostport,host_end); -  if(OP_UNLIKELY(_dst->host==NULL))return OP_EFAULT; -  if(port_num<0){ -    if(_src[4]=='s')port_num=443; -    else port_num=80; -  } -  _dst->port=(unsigned)port_num; -  /*RFC 2616 says an empty <abs-path> component is equivalent to "/", and we -     MUST use the latter in the Request-URI. -    Reserve space for the slash here.*/ -  if(path==path_end||path[0]=='?')path--; -  _dst->path=op_string_range_dup(path,path_end); -  if(OP_UNLIKELY(_dst->path==NULL))return OP_EFAULT; -  /*And force-set it here.*/ -  _dst->path[0]='/'; -  return 0; -} - -static void op_parsed_url_init(OpusParsedURL *_url){ -  memset(_url,0,sizeof(*_url)); -} - -static void op_parsed_url_clear(OpusParsedURL *_url){ -  _ogg_free(_url->scheme); -  _ogg_free(_url->user); -  _ogg_free(_url->pass); -  _ogg_free(_url->host); -  _ogg_free(_url->path); -} - -static int op_parse_url(OpusParsedURL *_dst,const char *_src){ -  OpusParsedURL url; -  int           ret; -  op_parsed_url_init(&url); -  ret=op_parse_url_impl(&url,_src); -  if(OP_UNLIKELY(ret<0))op_parsed_url_clear(&url); -  else *_dst=*&url; -  return ret; -} - -/*A buffer to hold growing strings. -  The main purpose of this is to consolidate allocation checks and simplify -   cleanup on a failed allocation.*/ -struct OpusStringBuf{ -  char *buf; -  int   nbuf; -  int   cbuf; -}; - -static void op_sb_init(OpusStringBuf *_sb){ -  _sb->buf=NULL; -  _sb->nbuf=0; -  _sb->cbuf=0; -} - -static void op_sb_clear(OpusStringBuf *_sb){ -  _ogg_free(_sb->buf); -} - -/*Make sure we have room for at least _capacity characters (plus 1 more for the -   terminating NUL).*/ -static int op_sb_ensure_capacity(OpusStringBuf *_sb,int _capacity){ -  char *buf; -  int   cbuf; -  buf=_sb->buf; -  cbuf=_sb->cbuf; -  if(_capacity>=cbuf-1){ -    if(OP_UNLIKELY(cbuf>INT_MAX-1>>1))return OP_EFAULT; -    if(OP_UNLIKELY(_capacity>=INT_MAX-1))return OP_EFAULT; -    cbuf=OP_MAX(2*cbuf+1,_capacity+1); -    buf=_ogg_realloc(buf,sizeof(*buf)*cbuf); -    if(OP_UNLIKELY(buf==NULL))return OP_EFAULT; -    _sb->buf=buf; -    _sb->cbuf=cbuf; -  } -  return 0; -} - -/*Increase the capacity of the buffer, but not to more than _max_size -   characters (plus 1 more for the terminating NUL).*/ -static int op_sb_grow(OpusStringBuf *_sb,int _max_size){ -  char *buf; -  int   cbuf; -  buf=_sb->buf; -  cbuf=_sb->cbuf; -  OP_ASSERT(_max_size<=INT_MAX-1); -  cbuf=cbuf<=_max_size-1>>1?2*cbuf+1:_max_size+1; -  buf=_ogg_realloc(buf,sizeof(*buf)*cbuf); -  if(OP_UNLIKELY(buf==NULL))return OP_EFAULT; -  _sb->buf=buf; -  _sb->cbuf=cbuf; -  return 0; -} - -static int op_sb_append(OpusStringBuf *_sb,const char *_s,int _len){ -  char *buf; -  int   nbuf; -  int   ret; -  nbuf=_sb->nbuf; -  if(OP_UNLIKELY(nbuf>INT_MAX-_len))return OP_EFAULT; -  ret=op_sb_ensure_capacity(_sb,nbuf+_len); -  if(OP_UNLIKELY(ret<0))return ret; -  buf=_sb->buf; -  memcpy(buf+nbuf,_s,sizeof(*buf)*_len); -  nbuf+=_len; -  buf[nbuf]='\0'; -  _sb->nbuf=nbuf; -  return 0; -} - -static int op_sb_append_string(OpusStringBuf *_sb,const char *_s){ -  return op_sb_append(_sb,_s,strlen(_s)); -} - -static int op_sb_append_port(OpusStringBuf *_sb,unsigned _port){ -  char port_buf[7]; -  OP_ASSERT(_port<=65535U); -  sprintf(port_buf,":%u",_port); -  return op_sb_append_string(_sb,port_buf); -} - -static int op_sb_append_nonnegative_int64(OpusStringBuf *_sb,opus_int64 _i){ -  char digit; -  int  nbuf_start; -  int  ret; -  OP_ASSERT(_i>=0); -  nbuf_start=_sb->nbuf; -  ret=0; -  do{ -    digit='0'+_i%10; -    ret|=op_sb_append(_sb,&digit,1); -    _i/=10; -  } -  while(_i>0); -  if(OP_LIKELY(ret>=0)){ -    char *buf; -    int   nbuf_end; -    buf=_sb->buf; -    nbuf_end=_sb->nbuf-1; -    /*We've added the digits backwards. -      Reverse them.*/ -    while(nbuf_start<nbuf_end){ -      digit=buf[nbuf_start]; -      buf[nbuf_start]=buf[nbuf_end]; -      buf[nbuf_end]=digit; -      nbuf_start++; -      nbuf_end--; -    } -  } -  return ret; -} - -static struct addrinfo *op_resolve(const char *_host,unsigned _port){ -  struct addrinfo *addrs; -  struct addrinfo  hints; -  char             service[6]; -  memset(&hints,0,sizeof(hints)); -  hints.ai_socktype=SOCK_STREAM; -#if !defined(_WIN32) -  hints.ai_flags=AI_NUMERICSERV; -#endif -  OP_ASSERT(_port<=65535U); -  sprintf(service,"%u",_port); -  if(OP_LIKELY(!getaddrinfo(_host,service,&hints,&addrs)))return addrs; -  return NULL; -} - -static int op_sock_set_nonblocking(op_sock _fd,int _nonblocking){ -#if !defined(_WIN32) -  int flags; -  flags=fcntl(_fd,F_GETFL); -  if(OP_UNLIKELY(flags<0))return flags; -  if(_nonblocking)flags|=O_NONBLOCK; -  else flags&=~O_NONBLOCK; -  return fcntl(_fd,F_SETFL,flags); -#else -  return ioctl(_fd,FIONBIO,&_nonblocking); -#endif -} - -/*Disable/enable write coalescing if we can. -  We always send whole requests at once and always parse the response headers -   before sending another one, so normally write coalescing just causes added -   delay.*/ -static void op_sock_set_tcp_nodelay(op_sock _fd,int _nodelay){ -# if defined(TCP_NODELAY)&&(defined(IPPROTO_TCP)||defined(SOL_TCP)) -#  if defined(IPPROTO_TCP) -#   define OP_SO_LEVEL IPPROTO_TCP -#  else -#   define OP_SO_LEVEL SOL_TCP -#  endif -  /*It doesn't really matter if this call fails, but it would be interesting -     to hit a case where it does.*/ -  OP_ALWAYS_TRUE(!setsockopt(_fd,OP_SO_LEVEL,TCP_NODELAY, -   &_nodelay,sizeof(_nodelay))); -# endif -} - -#if defined(_WIN32) -static void op_init_winsock(){ -  static LONG    count; -  static WSADATA wsadata; -  if(InterlockedIncrement(&count)==1)WSAStartup(0x0202,&wsadata); -} -#endif - -/*A single physical connection to an HTTP server. -  We may have several of these open at once.*/ -struct OpusHTTPConn{ -  /*The current position indicator for this connection.*/ -  opus_int64    pos; -  /*The position where the current request will end, or -1 if we're reading -     until EOF (an unseekable stream or the initial HTTP/1.0 request).*/ -  opus_int64    end_pos; -  /*The position where next request we've sent will start, or -1 if we haven't -     sent the next request yet.*/ -  opus_int64    next_pos; -  /*The end of the next request or -1 if we requested the rest of the resource. -    This is only set to a meaningful value if next_pos is not -1.*/ -  opus_int64    next_end; -  /*The SSL connection, if this is https.*/ -  SSL          *ssl_conn; -  /*The next connection in either the LRU or free list.*/ -  OpusHTTPConn *next; -  /*The last time we blocked for reading from this connection.*/ -  struct timeb  read_time; -  /*The number of bytes we've read since the last time we blocked.*/ -  opus_int64    read_bytes; -  /*The estimated throughput of this connection, in bytes/s.*/ -  opus_int64    read_rate; -  /*The socket we're reading from.*/ -  op_sock       fd; -  /*The number of remaining requests we are allowed on this connection.*/ -  int           nrequests_left; -  /*The chunk size to use for pipelining requests.*/ -  opus_int32    chunk_size; -}; - -static void op_http_conn_init(OpusHTTPConn *_conn){ -  _conn->next_pos=-1; -  _conn->ssl_conn=NULL; -  _conn->next=NULL; -  _conn->fd=OP_INVALID_SOCKET; -} - -static void op_http_conn_clear(OpusHTTPConn *_conn){ -  if(_conn->ssl_conn!=NULL)SSL_free(_conn->ssl_conn); -  /*SSL frees the BIO for us.*/ -  if(_conn->fd!=OP_INVALID_SOCKET)close(_conn->fd); -} - -/*The global stream state.*/ -struct OpusHTTPStream{ -  /*The list of connections.*/ -  OpusHTTPConn     conns[OP_NCONNS_MAX]; -  /*The context object used as a framework for TLS/SSL functions.*/ -  SSL_CTX         *ssl_ctx; -  /*The cached session to reuse for future connections.*/ -  SSL_SESSION     *ssl_session; -  /*The LRU list (ordered from MRU to LRU) of currently connected -     connections.*/ -  OpusHTTPConn    *lru_head; -  /*The free list.*/ -  OpusHTTPConn    *free_head; -  /*The URL to connect to.*/ -  OpusParsedURL    url; -  /*Information about the address we connected to.*/ -  struct addrinfo  addr_info; -  /*The address we connected to.*/ -  union{ -    struct sockaddr     s; -    struct sockaddr_in  v4; -    struct sockaddr_in6 v6; -  }                addr; -  /*The last time we re-resolved the host.*/ -  struct timeb     resolve_time; -  /*A buffer used to build HTTP requests.*/ -  OpusStringBuf    request; -  /*A buffer used to build proxy CONNECT requests.*/ -  OpusStringBuf    proxy_connect; -  /*A buffer used to receive the response headers.*/ -  OpusStringBuf    response; -  /*The Content-Length, if specified, or -1 otherwise. -    This will always be specified for seekable streams.*/ -  opus_int64       content_length; -  /*The position indicator used when no connection is active.*/ -  opus_int64       pos; -  /*The host we actually connected to.*/ -  char            *connect_host; -  /*The port we actually connected to.*/ -  unsigned         connect_port; -  /*The connection we're currently reading from. -    This can be -1 if no connection is active.*/ -  int              cur_conni; -  /*Whether or not the server supports range requests.*/ -  int              seekable; -  /*Whether or not the server supports HTTP/1.1 with persistent connections.*/ -  int              pipeline; -  /*Whether or not we should skip certificate checks.*/ -  int              skip_certificate_check; -  /*The offset of the tail of the request. -    Only the offset in the Range: header appears after this, allowing us to -     quickly edit the request to ask for a new range.*/ -  int              request_tail; -  /*The estimated time required to open a new connection, in milliseconds.*/ -  opus_int32       connect_rate; -}; - -static void op_http_stream_init(OpusHTTPStream *_stream){ -  OpusHTTPConn **pnext; -  int            ci; -  pnext=&_stream->free_head; -  for(ci=0;ci<OP_NCONNS_MAX;ci++){ -    op_http_conn_init(_stream->conns+ci); -    *pnext=_stream->conns+ci; -    pnext=&_stream->conns[ci].next; -  } -  _stream->ssl_ctx=NULL; -  _stream->ssl_session=NULL; -  _stream->lru_head=NULL; -  op_parsed_url_init(&_stream->url); -  op_sb_init(&_stream->request); -  op_sb_init(&_stream->proxy_connect); -  op_sb_init(&_stream->response); -  _stream->connect_host=NULL; -  _stream->seekable=0; -} - -/*Close the connection and move it to the free list. -  _stream:     The stream containing the free list. -  _conn:       The connection to close. -  _penxt:      The linked-list pointer currently pointing to this connection. -  _gracefully: Whether or not to shut down cleanly.*/ -static void op_http_conn_close(OpusHTTPStream *_stream,OpusHTTPConn *_conn, - OpusHTTPConn **_pnext,int _gracefully){ -  /*If we don't shut down gracefully, the server MUST NOT re-use our session -     according to RFC 2246, because it can't tell the difference between an -     abrupt close and a truncation attack. -    So we shut down gracefully if we can. -    However, we will not wait if this would block (it's not worth the savings -     from session resumption to do so). -    Clients (that's us) MAY resume a TLS session that ended with an incomplete -     close, according to RFC 2818, so there's no reason to make sure the server -     shut things down gracefully.*/ -  if(_gracefully&&_conn->ssl_conn!=NULL)SSL_shutdown(_conn->ssl_conn); -  op_http_conn_clear(_conn); -  _conn->next_pos=-1; -  _conn->ssl_conn=NULL; -  _conn->fd=OP_INVALID_SOCKET; -  OP_ASSERT(*_pnext==_conn); -  *_pnext=_conn->next; -  _conn->next=_stream->free_head; -  _stream->free_head=_conn; -} - -static void op_http_stream_clear(OpusHTTPStream *_stream){ -  while(_stream->lru_head!=NULL){ -    op_http_conn_close(_stream,_stream->lru_head,&_stream->lru_head,0); -  } -  if(_stream->ssl_session!=NULL)SSL_SESSION_free(_stream->ssl_session); -  if(_stream->ssl_ctx!=NULL)SSL_CTX_free(_stream->ssl_ctx); -  op_sb_clear(&_stream->response); -  op_sb_clear(&_stream->proxy_connect); -  op_sb_clear(&_stream->request); -  if(_stream->connect_host!=_stream->url.host)_ogg_free(_stream->connect_host); -  op_parsed_url_clear(&_stream->url); -} - -static int op_http_conn_write_fully(OpusHTTPConn *_conn, - const char *_buf,int _buf_size){ -  struct pollfd  fd; -  SSL           *ssl_conn; -  fd.fd=_conn->fd; -  ssl_conn=_conn->ssl_conn; -  while(_buf_size>0){ -    int err; -    if(ssl_conn!=NULL){ -      int ret; -      ret=SSL_write(ssl_conn,_buf,_buf_size); -      if(ret>0){ -        /*Wrote some data.*/ -        _buf+=ret; -        _buf_size-=ret; -        continue; -      } -      /*Connection closed.*/ -      else if(ret==0)return OP_FALSE; -      err=SSL_get_error(ssl_conn,ret); -      /*Yes, renegotiations can cause SSL_write() to block for reading.*/ -      if(err==SSL_ERROR_WANT_READ)fd.events=POLLIN; -      else if(err==SSL_ERROR_WANT_WRITE)fd.events=POLLOUT; -      else return OP_FALSE; -    } -    else{ -      ssize_t ret; -      op_reset_errno(); -      ret=send(fd.fd,_buf,_buf_size,0); -      if(ret>0){ -        _buf+=ret; -        _buf_size-=ret; -        continue; -      } -      err=op_errno(); -      if(err!=EAGAIN&&err!=EWOULDBLOCK)return OP_FALSE; -      fd.events=POLLOUT; -    } -    if(poll(&fd,1,OP_POLL_TIMEOUT_MS)<=0)return OP_FALSE; -  } -  return 0; -} - -static int op_http_conn_estimate_available(OpusHTTPConn *_conn){ -  int available; -  int ret; -  ret=ioctl(_conn->fd,FIONREAD,&available); -  if(ret<0)available=0; -  /*This requires the SSL read_ahead flag to be unset to work. -    We ignore partial records as well as the protocol overhead for any pending -     bytes. -    This means we might return somewhat less than can truly be read without -     blocking (if there's a partial record). -    This is okay, because we're using this value to estimate network transfer -     time, and we _have_ already received those bytes. -    We also might return slightly more (due to protocol overhead), but that's -     small enough that it probably doesn't matter.*/ -  if(_conn->ssl_conn!=NULL)available+=SSL_pending(_conn->ssl_conn); -  return available; -} - -static opus_int32 op_time_diff_ms(const struct timeb *_end, - const struct timeb *_start){ -  opus_int64 dtime; -  dtime=_end->time-(opus_int64)_start->time; -  OP_ASSERT(_end->millitm<1000); -  OP_ASSERT(_start->millitm<1000); -  if(OP_UNLIKELY(dtime>(OP_INT32_MAX-1000)/1000))return OP_INT32_MAX; -  if(OP_UNLIKELY(dtime<(OP_INT32_MIN+1000)/1000))return OP_INT32_MIN; -  return (opus_int32)dtime*1000+_end->millitm-_start->millitm; -} - -/*Update the read rate estimate for this connection.*/ -static void op_http_conn_read_rate_update(OpusHTTPConn *_conn){ -  struct timeb read_time; -  opus_int32   read_delta_ms; -  opus_int64   read_delta_bytes; -  opus_int64   read_rate; -  read_delta_bytes=_conn->read_bytes; -  if(read_delta_bytes<=0)return; -  ftime(&read_time); -  read_delta_ms=op_time_diff_ms(&read_time,&_conn->read_time); -  read_rate=_conn->read_rate; -  read_delta_ms=OP_MAX(read_delta_ms,1); -  read_rate+=read_delta_bytes*1000/read_delta_ms-read_rate+4>>3; -  *&_conn->read_time=*&read_time; -  _conn->read_bytes=0; -  _conn->read_rate=read_rate; -} - -/*Tries to read from the given connection. -  [out] _buf: Returns the data read. -  _buf_size:  The size of the buffer. -  _blocking:  Whether or not to block until some data is retrieved. -  Return: A positive number of bytes read on success. -          0:        The read would block, or the connection was closed. -          OP_EREAD: There was a fatal read error.*/ -static int op_http_conn_read(OpusHTTPConn *_conn, - char *_buf,int _buf_size,int _blocking){ -  struct pollfd  fd; -  SSL           *ssl_conn; -  int            nread; -  int            nread_unblocked; -  fd.fd=_conn->fd; -  ssl_conn=_conn->ssl_conn; -  nread=nread_unblocked=0; -  /*RFC 2818 says "client implementations MUST treat any premature closes as -     errors and the data received as potentially truncated," so we make very -     sure to report read errors upwards.*/ -  do{ -    int err; -    if(ssl_conn!=NULL){ -      int ret; -      ret=SSL_read(ssl_conn,_buf+nread,_buf_size-nread); -      OP_ASSERT(ret<=_buf_size-nread); -      if(ret>0){ -        /*Read some data. -          Keep going to see if there's more.*/ -        nread+=ret; -        nread_unblocked+=ret; -        continue; -      } -      /*If we already read some data, return it right now.*/ -      if(nread>0)break; -      err=SSL_get_error(ssl_conn,ret); -      if(ret==0){ -        /*Connection close. -          Check for a clean shutdown to prevent truncation attacks. -          This check always succeeds for SSLv2, as it has no "close notify" -           message and thus can't verify an orderly shutdown.*/ -        return err==SSL_ERROR_ZERO_RETURN?0:OP_EREAD; -      } -      if(err==SSL_ERROR_WANT_READ)fd.events=POLLIN; -      /*Yes, renegotiations can cause SSL_read() to block for writing.*/ -      else if(err==SSL_ERROR_WANT_WRITE)fd.events=POLLOUT; -      /*Some other error.*/ -      else return OP_EREAD; -    } -    else{ -      ssize_t ret; -      op_reset_errno(); -      ret=recv(fd.fd,_buf+nread,_buf_size-nread,0); -      OP_ASSERT(ret<=_buf_size-nread); -      if(ret>0){ -        /*Read some data. -          Keep going to see if there's more.*/ -        nread+=ret; -        nread_unblocked+=ret; -        continue; -      } -      /*If we already read some data or the connection was closed, return -         right now.*/ -      if(ret==0||nread>0)break; -      err=op_errno(); -      if(err!=EAGAIN&&err!=EWOULDBLOCK)return OP_EREAD; -      fd.events=POLLIN; -    } -    _conn->read_bytes+=nread_unblocked; -    op_http_conn_read_rate_update(_conn); -    nread_unblocked=0; -    if(!_blocking)break; -    /*Need to wait to get any data at all.*/ -    if(poll(&fd,1,OP_POLL_TIMEOUT_MS)<=0)return OP_EREAD; -  } -  while(nread<_buf_size); -  _conn->read_bytes+=nread_unblocked; -  return nread; -} - -/*Tries to look at the pending data for a connection without consuming it. -  [out] _buf: Returns the data at which we're peeking. -  _buf_size:  The size of the buffer.*/ -static int op_http_conn_peek(OpusHTTPConn *_conn,char *_buf,int _buf_size){ -  struct pollfd   fd; -  SSL            *ssl_conn; -  int             ret; -  fd.fd=_conn->fd; -  ssl_conn=_conn->ssl_conn; -  for(;;){ -    int err; -    if(ssl_conn!=NULL){ -      ret=SSL_peek(ssl_conn,_buf,_buf_size); -      /*Either saw some data or the connection was closed.*/ -      if(ret>=0)return ret; -      err=SSL_get_error(ssl_conn,ret); -      if(err==SSL_ERROR_WANT_READ)fd.events=POLLIN; -      /*Yes, renegotiations can cause SSL_peek() to block for writing.*/ -      else if(err==SSL_ERROR_WANT_WRITE)fd.events=POLLOUT; -      else return 0; -    } -    else{ -      op_reset_errno(); -      ret=(int)recv(fd.fd,_buf,_buf_size,MSG_PEEK); -      /*Either saw some data or the connection was closed.*/ -      if(ret>=0)return ret; -      err=op_errno(); -      if(err!=EAGAIN&&err!=EWOULDBLOCK)return 0; -      fd.events=POLLIN; -    } -    /*Need to wait to get any data at all.*/ -    if(poll(&fd,1,OP_POLL_TIMEOUT_MS)<=0)return 0; -  } -} - -/*When parsing response headers, RFC 2616 mandates that all lines end in CR LF. -  However, even in the year 2012, I have seen broken servers use just a LF. -  This is the evil that Postel's advice from RFC 761 breeds.*/ - -/*Reads the entirety of a response to an HTTP request into the response buffer. -  Actual parsing and validation is done later. -  Return: The number of bytes in the response on success, OP_EREAD if the -           connection was closed before reading any data, or another negative -           value on any other error.*/ -static int op_http_conn_read_response(OpusHTTPConn *_conn, - OpusStringBuf *_response){ -  int ret; -  _response->nbuf=0; -  ret=op_sb_ensure_capacity(_response,OP_RESPONSE_SIZE_MIN); -  if(OP_UNLIKELY(ret<0))return ret; -  for(;;){ -    char *buf; -    int   size; -    int   capacity; -    int   read_limit; -    int   terminated; -    size=_response->nbuf; -    capacity=_response->cbuf-1; -    if(OP_UNLIKELY(size>=capacity)){ -      ret=op_sb_grow(_response,OP_RESPONSE_SIZE_MAX); -      if(OP_UNLIKELY(ret<0))return ret; -      capacity=_response->cbuf-1; -      /*The response was too large. -        This prevents a bad server from running us out of memory.*/ -      if(OP_UNLIKELY(size>=capacity))return OP_EIMPL; -    } -    buf=_response->buf; -    ret=op_http_conn_peek(_conn,buf+size,capacity-size); -    if(OP_UNLIKELY(ret<=0))return size<=0?OP_EREAD:OP_FALSE; -    /*We read some data.*/ -    /*Make sure the starting characters are "HTTP". -      Otherwise we could wind up waiting forever for a response from -       something that is not an HTTP server.*/ -    if(size<4&&op_strncasecmp(buf,"HTTP",OP_MIN(size+ret,4))!=0){ -      return OP_FALSE; -    } -    /*How far can we read without passing the "\r\n\r\n" terminator?*/ -    buf[size+ret]='\0'; -    terminated=0; -    for(read_limit=OP_MAX(size-3,0);read_limit<size+ret;read_limit++){ -      /*We don't look for the leading '\r' thanks to broken servers.*/ -      if(buf[read_limit]=='\n'){ -        if(buf[read_limit+1]=='\r'&&OP_LIKELY(buf[read_limit+2]=='\n')){ -          terminated=3; -          break; -        } -        /*This case is for broken servers.*/ -        else if(OP_UNLIKELY(buf[read_limit+1]=='\n')){ -          terminated=2; -          break; -        } -      } -    } -    read_limit+=terminated; -    OP_ASSERT(size<=read_limit); -    OP_ASSERT(read_limit<=size+ret); -    /*Actually consume that data.*/ -    ret=op_http_conn_read(_conn,buf+size,read_limit-size,1); -    if(OP_UNLIKELY(ret<=0))return OP_FALSE; -    size+=ret; -    buf[size]='\0'; -    _response->nbuf=size; -    /*We found the terminator and read all the data up to and including it.*/ -    if(terminated&&OP_LIKELY(size>=read_limit))return size; -  } -  return OP_EIMPL; -} - -# define OP_HTTP_DIGIT "0123456789" - -/*The Reason-Phrase is not allowed to contain control characters, except -   horizontal tab (HT: \011).*/ -# define OP_HTTP_CREASON_PHRASE \ - "\001\002\003\004\005\006\007\010\012\013\014\015\016\017\020\021" \ - "\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177" - -# define OP_HTTP_CTLS \ - "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020" \ - "\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177" - -/*This also includes '\t', but we get that from OP_HTTP_CTLS.*/ -# define OP_HTTP_SEPARATORS " \"(),/:;<=>?@[\\]{}" - -/*TEXT can also include LWS, but that has structure, so we parse it -   separately.*/ -# define OP_HTTP_CTOKEN OP_HTTP_CTLS OP_HTTP_SEPARATORS - -/*Return: The amount of linear white space (LWS) at the start of _s.*/ -static int op_http_lwsspn(const char *_s){ -  int i; -  for(i=0;;){ -    if(_s[0]=='\r'&&_s[1]=='\n'&&(_s[2]=='\t'||_s[2]==' '))i+=3; -    /*This case is for broken servers.*/ -    else if(_s[0]=='\n'&&(_s[1]=='\t'||_s[1]==' '))i+=2; -    else if(_s[i]=='\t'||_s[i]==' ')i++; -    else return i; -  } -} - -static char *op_http_parse_status_line(int *_v1_1_compat, - char **_status_code,char *_response){ -  char   *next; -  char   *status_code; -  int     v1_1_compat; -  size_t  d; -  /*RFC 2616 Section 6.1 does not say that the tokens in the Status-Line cannot -     be separated by optional LWS, but since it specifically calls out where -     spaces are to be placed and that CR and LF are not allowed except at the -     end, I am assuming this to be true.*/ -  /*We already validated that this starts with "HTTP"*/ -  OP_ASSERT(op_strncasecmp(_response,"HTTP",4)==0); -  next=_response+4; -  if(OP_UNLIKELY(*next++!='/'))return NULL; -  d=strspn(next,OP_HTTP_DIGIT); -  /*"Leading zeros MUST be ignored by recipients."*/ -  while(*next=='0'){ -    next++; -    OP_ASSERT(d>0); -    d--; -  } -  /*We only support version 1.x*/ -  if(OP_UNLIKELY(d!=1)||OP_UNLIKELY(*next++!='1'))return NULL; -  if(OP_UNLIKELY(*next++!='.'))return NULL; -  d=strspn(next,OP_HTTP_DIGIT); -  if(OP_UNLIKELY(d<=0))return NULL; -  /*"Leading zeros MUST be ignored by recipients."*/ -  while(*next=='0'){ -    next++; -    OP_ASSERT(d>0); -    d--; -  } -  /*We don't need to parse the version number. -    Any non-zero digit means it's greater than 1.*/ -  v1_1_compat=d>0; -  next+=d; -  if(OP_UNLIKELY(*next++!=' '))return NULL; -  status_code=next; -  d=strspn(next,OP_HTTP_DIGIT); -  if(OP_UNLIKELY(d!=3))return NULL; -  next+=d; -  /*The Reason-Phrase can be empty, but the space must be here.*/ -  if(OP_UNLIKELY(*next++!=' '))return NULL; -  next+=strcspn(next,OP_HTTP_CREASON_PHRASE); -  /*We are not mandating this be present thanks to broken servers.*/ -  if(OP_LIKELY(*next=='\r'))next++; -  if(OP_UNLIKELY(*next++!='\n'))return NULL; -  if(_v1_1_compat!=NULL)*_v1_1_compat=v1_1_compat; -  *_status_code=status_code; -  return next; -} - -/*Get the next response header. -  [out] _header: The header token, NUL-terminated, with leading and trailing -                  whitespace stripped, and converted to lower case (to simplify -                  case-insensitive comparisons), or NULL if there are no more -                  response headers. -  [out] _cdr:    The remaining contents of the header, excluding the initial -                  colon (':') and the terminating CRLF ("\r\n"), -                  NUL-terminated, and with leading and trailing whitespace -                  stripped, or NULL if there are no more response headers. -  [inout] _s:    On input, this points to the start of the current line of the -                  response headers. -                 On output, it points to the start of the first line following -                  this header, or NULL if there are no more response headers. -  Return: 0 on success, or a negative value on failure.*/ -static int op_http_get_next_header(char **_header,char **_cdr,char **_s){ -  char   *header; -  char   *header_end; -  char   *cdr; -  char   *cdr_end; -  char   *next; -  size_t  d; -  next=*_s; -  /*The second case is for broken servers.*/ -  if(next[0]=='\r'&&next[1]=='\n'||OP_UNLIKELY(next[0]=='\n')){ -    /*No more headers.*/ -    *_header=NULL; -    *_cdr=NULL; -    *_s=NULL; -    return 0; -  } -  header=next+op_http_lwsspn(next); -  d=strcspn(header,OP_HTTP_CTOKEN); -  if(OP_UNLIKELY(d<=0))return OP_FALSE; -  header_end=header+d; -  next=header_end+op_http_lwsspn(header_end); -  if(OP_UNLIKELY(*next++!=':'))return OP_FALSE; -  next+=op_http_lwsspn(next); -  cdr=next; -  do{ -    cdr_end=next+strcspn(next,OP_HTTP_CTLS); -    next=cdr_end+op_http_lwsspn(cdr_end); -  } -  while(next>cdr_end); -  /*We are not mandating this be present thanks to broken servers.*/ -  if(OP_LIKELY(*next=='\r'))next++; -  if(OP_UNLIKELY(*next++!='\n'))return OP_FALSE; -  *header_end='\0'; -  *cdr_end='\0'; -  /*Field names are case-insensitive.*/ -  op_string_tolower(header); -  *_header=header; -  *_cdr=cdr; -  *_s=next; -  return 0; -} - -static opus_int64 op_http_parse_nonnegative_int64(const char **_next, - const char *_cdr){ -  const char *next; -  opus_int64  ret; -  int         i; -  next=_cdr+strspn(_cdr,OP_HTTP_DIGIT); -  *_next=next; -  if(OP_UNLIKELY(next<=_cdr))return OP_FALSE; -  while(*_cdr=='0')_cdr++; -  if(OP_UNLIKELY(next-_cdr>19))return OP_EIMPL; -  ret=0; -  for(i=0;i<next-_cdr;i++){ -    int digit; -    digit=_cdr[i]-'0'; -    /*Check for overflow.*/ -    if(OP_UNLIKELY(ret>(OP_INT64_MAX-9)/10+(digit<=7)))return OP_EIMPL; -    ret=ret*10+digit; -  } -  return ret; -} - -static opus_int64 op_http_parse_content_length(const char *_cdr){ -  const char *next; -  opus_int64  content_length; -  content_length=op_http_parse_nonnegative_int64(&next,_cdr); -  if(OP_UNLIKELY(*next!='\0'))return OP_FALSE; -  return content_length; -} - -static int op_http_parse_content_range(opus_int64 *_first,opus_int64 *_last, - opus_int64 *_length,const char *_cdr){ -  opus_int64 first; -  opus_int64 last; -  opus_int64 length; -  size_t     d; -  if(OP_UNLIKELY(op_strncasecmp(_cdr,"bytes",5)!=0))return OP_FALSE; -  _cdr+=5; -  d=op_http_lwsspn(_cdr); -  if(OP_UNLIKELY(d<=0))return OP_FALSE; -  _cdr+=d; -  if(*_cdr!='*'){ -    first=op_http_parse_nonnegative_int64(&_cdr,_cdr); -    if(OP_UNLIKELY(first<0))return (int)first; -    _cdr+=op_http_lwsspn(_cdr); -    if(*_cdr++!='-')return OP_FALSE; -    _cdr+=op_http_lwsspn(_cdr); -    last=op_http_parse_nonnegative_int64(&_cdr,_cdr); -    if(OP_UNLIKELY(last<0))return (int)last; -    _cdr+=op_http_lwsspn(_cdr); -  } -  else{ -    /*This is for a 416 response (Requested range not satisfiable).*/ -    first=last=-1; -    _cdr++; -  } -  if(OP_UNLIKELY(*_cdr++!='/'))return OP_FALSE; -  if(*_cdr!='*'){ -    length=op_http_parse_nonnegative_int64(&_cdr,_cdr); -    if(OP_UNLIKELY(length<0))return (int)length; -  } -  else{ -    /*The total length is unspecified.*/ -    _cdr++; -    length=-1; -  } -  if(OP_UNLIKELY(*_cdr!='\0'))return OP_FALSE; -  if(OP_UNLIKELY(last<first))return OP_FALSE; -  if(length>=0&&OP_UNLIKELY(last>=length))return OP_FALSE; -  *_first=first; -  *_last=last; -  *_length=length; -  return 0; -} - -/*Parse the Connection response header and look for a "close" token. -  Return: 1 if a "close" token is found, 0 if it's not found, and a negative -           value on error.*/ -static int op_http_parse_connection(char *_cdr){ -  size_t d; -  int    ret; -  ret=0; -  for(;;){ -    d=strcspn(_cdr,OP_HTTP_CTOKEN); -    if(OP_UNLIKELY(d<=0))return OP_FALSE; -    if(op_strncasecmp(_cdr,"close",(int)d)==0)ret=1; -    /*We're supposed to strip and ignore any headers mentioned in the -       Connection header if this response is from an HTTP/1.0 server (to -       work around forwarding of hop-by-hop headers by old proxies), but the -       only hop-by-hop header we look at is Connection itself. -      Everything else is a well-defined end-to-end header, and going back and -       undoing the things we did based on already-examined headers would be -       hard (since we only scan them once, in a destructive manner). -      Therefore we just ignore all the other tokens.*/ -    _cdr+=d; -    d=op_http_lwsspn(_cdr); -    if(d<=0)break; -    _cdr+=d; -  } -  return OP_UNLIKELY(*_cdr!='\0')?OP_FALSE:ret; -} - -typedef int (*op_ssl_step_func)(SSL *_ssl_conn); - -/*Try to run an SSL function to completion (blocking if necessary).*/ -static int op_do_ssl_step(SSL *_ssl_conn,op_sock _fd,op_ssl_step_func _step){ -  struct pollfd fd; -  fd.fd=_fd; -  for(;;){ -    int ret; -    int err; -    ret=(*_step)(_ssl_conn); -    if(ret>=0)return ret; -    err=SSL_get_error(_ssl_conn,ret); -    if(err==SSL_ERROR_WANT_READ)fd.events=POLLIN; -    else if(err==SSL_ERROR_WANT_WRITE)fd.events=POLLOUT; -    else return OP_FALSE; -    if(poll(&fd,1,OP_POLL_TIMEOUT_MS)<=0)return OP_FALSE; -  } -} - -/*Implement a BIO type that just indicates every operation should be retried. -  We use this when initializing an SSL connection via a proxy to allow the -   initial handshake to proceed all the way up to the first read attempt, and -   then return. -  This allows the TLS client hello message to be pipelined with the HTTP -   CONNECT request.*/ - -static int op_bio_retry_write(BIO *_b,const char *_buf,int _num){ -  (void)_buf; -  (void)_num; -  BIO_clear_retry_flags(_b); -  BIO_set_retry_write(_b); -  return -1; -} - -static int op_bio_retry_read(BIO *_b,char *_buf,int _num){ -  (void)_buf; -  (void)_num; -  BIO_clear_retry_flags(_b); -  BIO_set_retry_read(_b); -  return -1; -} - -static int op_bio_retry_puts(BIO *_b,const char *_str){ -  return op_bio_retry_write(_b,_str,0); -} - -static long op_bio_retry_ctrl(BIO *_b,int _cmd,long _num,void *_ptr){ -  long ret; -  (void)_b; -  (void)_num; -  (void)_ptr; -  ret=0; -  switch(_cmd){ -    case BIO_CTRL_RESET: -    case BIO_C_RESET_READ_REQUEST:{ -      BIO_clear_retry_flags(_b); -      /*Fall through.*/ -    } -    case BIO_CTRL_EOF: -    case BIO_CTRL_SET: -    case BIO_CTRL_SET_CLOSE: -    case BIO_CTRL_FLUSH: -    case BIO_CTRL_DUP:{ -      ret=1; -    }break; -  } -  return ret; -} - -static int op_bio_retry_new(BIO *_b){ -  _b->init=1; -  _b->num=0; -  _b->ptr=NULL; -  return 1; -} - -static int op_bio_retry_free(BIO *_b){ -  return _b!=NULL; -} - -/*This is not const because OpenSSL doesn't allow it, even though it won't -   write to it.*/ -static BIO_METHOD op_bio_retry_method={ -  BIO_TYPE_NULL, -  "retry", -  op_bio_retry_write, -  op_bio_retry_read, -  op_bio_retry_puts, -  NULL, -  op_bio_retry_ctrl, -  op_bio_retry_new, -  op_bio_retry_free, -  NULL -}; - -/*Establish a CONNECT tunnel and pipeline the start of the TLS handshake for -   proxying https URL requests.*/ -static int op_http_conn_establish_tunnel(OpusHTTPStream *_stream, - OpusHTTPConn *_conn,op_sock _fd,SSL *_ssl_conn,BIO *_ssl_bio){ -  BIO  *retry_bio; -  char *status_code; -  char *next; -  int   ret; -  _conn->ssl_conn=NULL; -  _conn->fd=_fd; -  OP_ASSERT(_stream->proxy_connect.nbuf>0); -  ret=op_http_conn_write_fully(_conn, -   _stream->proxy_connect.buf,_stream->proxy_connect.nbuf); -  if(OP_UNLIKELY(ret<0))return ret; -  retry_bio=BIO_new(&op_bio_retry_method); -  if(OP_UNLIKELY(retry_bio==NULL))return OP_EFAULT; -  SSL_set_bio(_ssl_conn,retry_bio,_ssl_bio); -  SSL_set_connect_state(_ssl_conn); -  /*This shouldn't succeed, since we can't read yet.*/ -  OP_ALWAYS_TRUE(SSL_connect(_ssl_conn)<0); -  SSL_set_bio(_ssl_conn,_ssl_bio,_ssl_bio); -  /*Only now do we disable write coalescing, to allow the CONNECT -     request and the start of the TLS handshake to be combined.*/ -  op_sock_set_tcp_nodelay(_fd,1); -  ret=op_http_conn_read_response(_conn,&_stream->response); -  if(OP_UNLIKELY(ret<0))return ret; -  next=op_http_parse_status_line(NULL,&status_code,_stream->response.buf); -  /*According to RFC 2817, "Any successful (2xx) response to a -     CONNECT request indicates that the proxy has established a -     connection to the requested host and port.*/ -  if(OP_UNLIKELY(next==NULL)||OP_UNLIKELY(status_code[0]!='2'))return OP_FALSE; -  return 0; -} - -/*Match a host name against a host with a possible wildcard pattern according -   to the rules of RFC 6125 Section 6.4.3. -  Return: 0 if the pattern doesn't match, and a non-zero value if it does.*/ -static int op_http_hostname_match(const char *_host,size_t _host_len, - ASN1_STRING *_pattern){ -  const char *pattern; -  size_t      host_label_len; -  size_t      host_suffix_len; -  size_t      pattern_len; -  size_t      pattern_label_len; -  size_t      pattern_prefix_len; -  size_t      pattern_suffix_len; -  pattern=(const char *)ASN1_STRING_data(_pattern); -  pattern_len=strlen(pattern); -  /*Check the pattern for embedded NULs.*/ -  if(OP_UNLIKELY(pattern_len!=(size_t)ASN1_STRING_length(_pattern)))return 0; -  pattern_label_len=strcspn(pattern,"."); -  OP_ASSERT(pattern_label_len<=pattern_len); -  pattern_prefix_len=strcspn(pattern,"*"); -  if(pattern_prefix_len>=pattern_label_len){ -    /*"The client SHOULD NOT attempt to match a presented identifier in which -       the wildcard character comprises a label other than the left-most label -       (e.g., do not match bar.*.example.net)." [RFC 6125 Section 6.4.3]*/ -    if(pattern_prefix_len<pattern_len)return 0; -    /*If the pattern does not contain a wildcard in the first element, do an -       exact match. -      Don't use the system strcasecmp here, as that uses the locale and -       RFC 4343 makes clear that DNS's case-insensitivity only applies to -       the ASCII range.*/ -    return _host_len==pattern_len&&op_strncasecmp(_host,pattern,_host_len)==0; -  } -  /*"However, the client SHOULD NOT attempt to match a presented identifier -     where the wildcard character is embedded within an A-label or U-label of -     an internationalized domain name." [RFC 6125 Section 6.4.3]*/ -  if(op_strncasecmp(pattern,"xn--",4)==0)return 0; -  host_label_len=strcspn(_host,"."); -  /*Make sure the host has at least two dots, to prevent the wildcard match -     from being ridiculously wide. -    We should have already checked to ensure it had at least one.*/ -  if(OP_UNLIKELY(_host[host_label_len]!='.') -   ||strchr(_host+host_label_len+1,'.')==NULL){ -    return 0; -  } -  OP_ASSERT(host_label_len<_host_len); -  /*"If the wildcard character is the only character of the left-most label in -     the presented identifier, the client SHOULD NOT compare against anything -     but the left-most label of the reference identifier (e.g., *.example.com -     would match foo.example.com but not bar.foo.example.com)." [RFC 6125 -     Section 6.4.3] -    This is really confusingly worded, as we check this by actually comparing -     the rest of the pattern for an exact match. -    We also use the fact that the wildcard must match at least one character, -     so the left-most label of the hostname must be at least as large as the -     left-most label of the pattern.*/ -  if(host_label_len<pattern_label_len)return 0; -  OP_ASSERT(pattern[pattern_prefix_len]=='*'); -  /*"The client MAY match a presented identifier in which the wildcard -     character is not the only character of the label (e.g., baz*.example.net -     and *baz.example.net and b*z.example.net would be taken to match -     baz1.example.net and foobaz.example.net and buzz.example.net, -     respectively)." [RFC 6125 Section 6.4.3]*/ -  pattern_suffix_len=pattern_len-pattern_prefix_len-1; -  host_suffix_len=_host_len-host_label_len -   +pattern_label_len-pattern_prefix_len-1; -  return pattern_suffix_len==host_suffix_len -   &&op_strncasecmp(_host,pattern,pattern_prefix_len)==0 -   &&op_strncasecmp(_host+_host_len-host_suffix_len, -   pattern+pattern_prefix_len+1,host_suffix_len)==0; -} - -/*Convert a host to a numeric address, if possible. -  Return: A struct addrinfo containing the address, if it was numeric, and NULL -           otherise.*/ -static struct addrinfo *op_inet_pton(const char *_host){ -  struct addrinfo *addrs; -  struct addrinfo  hints; -  memset(&hints,0,sizeof(hints)); -  hints.ai_socktype=SOCK_STREAM; -  hints.ai_flags=AI_NUMERICHOST; -  if(!getaddrinfo(_host,NULL,&hints,&addrs))return addrs; -  return NULL; -} - -/*Verify the server's hostname matches the certificate they presented using -   the procedure from Section 6 of RFC 6125. -  Return: 0 if the certificate doesn't match, and a non-zero value if it does.*/ -static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){ -  X509                   *peer_cert; -  STACK_OF(GENERAL_NAME) *san_names; -  char                   *host; -  size_t                  host_len; -  int                     ret; -  host=_stream->url.host; -  host_len=strlen(host); -  peer_cert=SSL_get_peer_certificate(_ssl_conn); -  /*We set VERIFY_PEER, so we shouldn't get here without a certificate.*/ -  if(OP_UNLIKELY(peer_cert==NULL))return 0; -  ret=0; -  OP_ASSERT(host_len<INT_MAX); -  /*RFC 2818 says (after correcting for Eratta 1077): "If a subjectAltName -     extension of type dNSName is present, that MUST be used as the identity. -    Otherwise, the (most specific) Common Name field in the Subject field of -     the certificate MUST be used. -    Although the use of the Common Name is existing practice, it is deprecated -     and Certification Authorities are encouraged to use the dNSName -     instead." -    "Matching is performed using the matching rules specified by RFC 2459. -    If more than one identity of a given type is present in the certificate -     (e.g., more than one dNSName name), a match in any one of the set is -     considered acceptable. -    Names may contain the wildcard character * which is condered to match any -     single domain name component or component fragment. -    E.g., *.a.com matches foo.a.com but not bar.foo.a.com. -    f*.com matches foo.com but not bar.com." -    "In some cases, the URI is specified as an IP address rather than a -     hostname. -    In this case, the iPAddress subjectAltName must be present in the -     certificate and must exactly match the IP in the URI."*/ -  san_names=X509_get_ext_d2i(peer_cert,NID_subject_alt_name,NULL,NULL); -  if(san_names!=NULL){ -    struct addrinfo *addr; -    unsigned char   *ip; -    int              ip_len; -    int              nsan_names; -    int              sni; -    /*Check to see if the host was specified as a simple IP address.*/ -    addr=op_inet_pton(host); -    ip=NULL; -    ip_len=0; -    if(addr!=NULL){ -      switch(addr->ai_family){ -        case AF_INET:{ -          struct sockaddr_in *s; -          s=(struct sockaddr_in *)addr->ai_addr; -          OP_ASSERT(addr->ai_addrlen>=sizeof(*s)); -          ip=(unsigned char *)&s->sin_addr; -          ip_len=sizeof(s->sin_addr); -        }break; -        case AF_INET6:{ -          struct sockaddr_in6 *s; -          s=(struct sockaddr_in6 *)addr->ai_addr; -          OP_ASSERT(addr->ai_addrlen>=sizeof(*s)); -          ip=(unsigned char *)&s->sin6_addr; -          ip_len=sizeof(s->sin6_addr); -        }break; -      } -    } -    /*We can only verify fully-qualified domain names. -      To quote RFC 6125: "The extracted data MUST include only information that -       can be securely parsed out of the inputs (e.g., parsing the fully -       qualified DNS domain name out of the "host" component (or its -       equivalent) of a URI or deriving the application service type from the -       scheme of a URI) ..." -      We don't have a way to check (without relying on DNS records, which might -       be subverted) if this address is fully-qualified. -      This is particularly problematic when using a CONNECT tunnel, as it is -       the server that does DNS lookup, not us. -      However, we are certain that if the hostname has no '.', it is definitely -       not a fully-qualified domain name (with the exception of crazy TLDs that -       actually resolve, like "uz", but I am willing to ignore those). -      RFC 1535 says "...in any event where a '.' exists in a specified name it -       should be assumed to be a fully qualified domain name (FQDN) and SHOULD -       be tried as a rooted name first." -      That doesn't give us any security guarantees, of course (a subverted DNS -       could fail the original query and our resolver might still retry with a -       local domain appended). -      If we don't have a FQDN, just set the number of names to 0, so we'll fail -       and clean up any resources we allocated.*/ -    if(ip==NULL&&strchr(host,'.')==NULL)nsan_names=0; -    /*RFC 2459 says there MUST be at least one, but we don't depend on it.*/ -    else nsan_names=sk_GENERAL_NAME_num(san_names); -    for(sni=0;sni<nsan_names;sni++){ -      const GENERAL_NAME *name; -      name=sk_GENERAL_NAME_value(san_names,sni); -      if(ip==NULL){ -        if(name->type==GEN_DNS -         &&op_http_hostname_match(host,host_len,name->d.dNSName)){ -          ret=1; -          break; -        } -      } -      else if(name->type==GEN_IPADD){ -        unsigned char *cert_ip; -        /*If we do have an IP address, compare it directly. -          RFC 6125: "When the reference identity is an IP address, the identity -           MUST be converted to the 'network byte order' octet string -           representation. -          For IP Version 4, as specified in RFC 791, the octet string will -           contain exactly four octets. -          For IP Version 6, as specified in RFC 2460, the octet string will -           contain exactly sixteen octets. -          This octet string is then compared against subjectAltName values of -           type iPAddress. -          A match occurs if the reference identity octet string and the value -           octet strings are identical."*/ -        cert_ip=ASN1_STRING_data(name->d.iPAddress); -        if(ip_len==ASN1_STRING_length(name->d.iPAddress) -         &&memcmp(ip,cert_ip,ip_len)==0){ -          ret=1; -          break; -        } -      } -    } -    sk_GENERAL_NAME_pop_free(san_names,GENERAL_NAME_free); -    if(addr!=NULL)freeaddrinfo(addr); -  } -  /*Do the same FQDN check we did above. -    We don't do this once in advance for both cases, because in the -     subjectAltName case we might have an IPv6 address without a dot.*/ -  else if(strchr(host,'.')!=NULL){ -    int last_cn_loc; -    int cn_loc; -    /*If there is no subjectAltName, match against commonName. -      RFC 6125 says that at least one significant CA is known to issue certs -       with multiple CNs, although it SHOULD NOT. -      It also says: "The server's identity may also be verified by comparing -       the reference identity to the Common Name (CN) value in the last -       Relative Distinguished Name (RDN) of the subject field of the server's -       certificate (where "last" refers to the DER-encoded order...)." -      So find the last one and check it.*/ -    cn_loc=-1; -    do{ -      last_cn_loc=cn_loc; -      cn_loc=X509_NAME_get_index_by_NID(X509_get_subject_name(peer_cert), -       NID_commonName,last_cn_loc); -    } -    while(cn_loc>=0); -    ret=last_cn_loc>=0 -     &&op_http_hostname_match(host,host_len, -     X509_NAME_ENTRY_get_data( -     X509_NAME_get_entry(X509_get_subject_name(peer_cert),last_cn_loc))); -  } -  X509_free(peer_cert); -  return ret; -} - -/*Perform the TLS handshake on a new connection.*/ -static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn, - op_sock _fd,SSL *_ssl_conn){ -  SSL_SESSION *ssl_session; -  BIO         *ssl_bio; -  int          skip_certificate_check; -  int          ret; -  ssl_bio=BIO_new_socket(_fd,BIO_NOCLOSE); -  if(OP_LIKELY(ssl_bio==NULL))return OP_FALSE; -# if !defined(OPENSSL_NO_TLSEXT) -  /*Support for RFC 6066 Server Name Indication.*/ -  SSL_set_tlsext_host_name(_ssl_conn,_stream->url.host); -# endif -  /*Resume a previous session if available.*/ -  if(_stream->ssl_session!=NULL){ -    SSL_set_session(_ssl_conn,_stream->ssl_session); -  } -  /*If we're proxying, establish the CONNECT tunnel.*/ -  if(_stream->proxy_connect.nbuf>0){ -    ret=op_http_conn_establish_tunnel(_stream,_conn, -     _fd,_ssl_conn,ssl_bio); -    if(OP_UNLIKELY(ret<0))return ret; -  } -  else{ -    /*Otherwise, just use this socket directly.*/ -    op_sock_set_tcp_nodelay(_fd,1); -    SSL_set_bio(_ssl_conn,ssl_bio,ssl_bio); -    SSL_set_connect_state(_ssl_conn); -  } -  ret=op_do_ssl_step(_ssl_conn,_fd,SSL_connect); -  if(OP_UNLIKELY(ret<=0))return OP_FALSE; -  ssl_session=_stream->ssl_session; -  skip_certificate_check=_stream->skip_certificate_check; -  if(ssl_session==NULL||!skip_certificate_check){ -    ret=op_do_ssl_step(_ssl_conn,_fd,SSL_do_handshake); -    if(OP_UNLIKELY(ret<=0))return OP_FALSE; -    /*OpenSSL does not do hostname verification, despite the fact that we just -       passed it the hostname above in the call to SSL_set_tlsext_host_name(), -       because they are morons. -      Do it for them.*/ -    if(!skip_certificate_check&&!op_http_verify_hostname(_stream,_ssl_conn)){ -      return OP_FALSE; -    } -    if(ssl_session==NULL){ -      /*Save the session for later resumption.*/ -      _stream->ssl_session=SSL_get1_session(_ssl_conn); -    } -  } -  _conn->ssl_conn=_ssl_conn; -  _conn->fd=_fd; -  _conn->nrequests_left=OP_PIPELINE_MAX_REQUESTS; -  return 0; -} - -/*Try to start a connection to the next address in the given list of a given -   type. -  _fd:           The socket to connect with. -  [inout] _addr: A pointer to the list of addresses. -                 This will be advanced to the first one that matches the given -                  address family (possibly the current one). -  _ai_family:    The address family to connect to. -  Return: 1        If the connection was successful. -          0        If the connection is in progress. -          OP_FALSE If the connection failed and there were no more addresses -                    left to try. -                    *_addr will be set to NULL in this case.*/ -static int op_sock_connect_next(op_sock _fd, - const struct addrinfo **_addr,int _ai_family){ -  const struct addrinfo *addr; -  int                    err; -  addr=*_addr; -  for(;;){ -    /*Move to the next address of the requested type.*/ -    for(;addr!=NULL&&addr->ai_family!=_ai_family;addr=addr->ai_next); -    *_addr=addr; -    /*No more: failure.*/ -    if(addr==NULL)return OP_FALSE; -    if(connect(_fd,addr->ai_addr,addr->ai_addrlen)>=0)return 1; -    err=op_errno(); -    /*Winsock will set WSAEWOULDBLOCK.*/ -    if(OP_LIKELY(err==EINPROGRESS||err==EWOULDBLOCK))return 0; -    addr=addr->ai_next; -  } -} - -/*The number of address families to try connecting to simultaneously.*/ -# define OP_NPROTOS (2) - -static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn, - const struct addrinfo *_addrs,struct timeb *_start_time){ -  const struct addrinfo *addr; -  const struct addrinfo *addrs[OP_NPROTOS]; -  struct pollfd          fds[OP_NPROTOS]; -  int                    ai_family; -  int                    nprotos; -  int                    ret; -  int                    pi; -  int                    pj; -  for(pi=0;pi<OP_NPROTOS;pi++)addrs[pi]=NULL; -  /*Try connecting via both IPv4 and IPv6 simultaneously, and keep the first -     one that succeeds. -    Start by finding the first address from each family. -    We order the first connection attempts in the same order the address -     families were returned in the DNS records in accordance with RFC 6555.*/ -  for(addr=_addrs,nprotos=0;addr!=NULL&&nprotos<OP_NPROTOS;addr=addr->ai_next){ -    if(addr->ai_family==AF_INET6||addr->ai_family==AF_INET){ -      OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in6)); -      OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in)); -      /*If we've seen this address family before, skip this address for now.*/ -      for(pi=0;pi<nprotos;pi++)if(addrs[pi]->ai_family==addr->ai_family)break; -      if(pi<nprotos)continue; -      addrs[nprotos++]=addr; -    } -  } -  /*Pop the connection off the free list and put it on the LRU list.*/ -  OP_ASSERT(_stream->free_head==_conn); -  _stream->free_head=_conn->next; -  _conn->next=_stream->lru_head; -  _stream->lru_head=_conn; -  ftime(_start_time); -  *&_conn->read_time=*_start_time; -  _conn->read_bytes=0; -  _conn->read_rate=0; -  /*Try to start a connection to each protocol. -    RFC 6555 says it is RECOMMENDED that connection attempts be paced -     150...250 ms apart "to balance human factors against network load", but -     that "stateful algorithms" (that's us) "are expected to be more -     aggressive". -    We are definitely more aggressive: we don't pace at all.*/ -  for(pi=0;pi<nprotos;pi++){ -    ai_family=addrs[pi]->ai_family; -    fds[pi].fd=socket(ai_family,SOCK_STREAM,addrs[pi]->ai_protocol); -    fds[pi].events=POLLOUT; -    if(OP_LIKELY(fds[pi].fd!=OP_INVALID_SOCKET)){ -      if(OP_LIKELY(op_sock_set_nonblocking(fds[pi].fd,1)>=0)){ -        ret=op_sock_connect_next(fds[pi].fd,addrs+pi,ai_family); -        if(OP_UNLIKELY(ret>0)){ -          /*It succeeded right away (technically possible), so stop.*/ -          nprotos=pi+1; -          break; -        } -        /*Otherwise go on to the next protocol, and skip the clean-up below.*/ -        else if(ret==0)continue; -        /*Tried all the addresses for this protocol.*/ -      } -      /*Clean up the socket.*/ -      close(fds[pi].fd); -    } -    /*Remove this protocol from the list.*/ -    memmove(addrs+pi,addrs+pi+1,sizeof(*addrs)*(nprotos-pi-1)); -    nprotos--; -    pi--; -  } -  /*Wait for one of the connections to finish.*/ -  while(pi>=nprotos&&nprotos>0&&poll(fds,nprotos,OP_POLL_TIMEOUT_MS)>0){ -    for(pi=0;pi<nprotos;pi++){ -      socklen_t errlen; -      int       err; -      /*Still waiting...*/ -      if(!fds[pi].revents)continue; -      errlen=sizeof(err); -      /*Some platforms will return the pending error in &err and return 0. -        Others will put it in errno and return -1.*/ -      ret=getsockopt(fds[pi].fd,SOL_SOCKET,SO_ERROR,&err,&errlen); -      if(ret<0)err=op_errno(); -      /*Success!*/ -      if(err==0||err==EISCONN)break; -      /*Move on to the next address for this protocol.*/ -      ai_family=addrs[pi]->ai_family; -      addrs[pi]=addrs[pi]->ai_next; -      ret=op_sock_connect_next(fds[pi].fd,addrs+pi,ai_family); -      /*It succeeded right away, so stop.*/ -      if(ret>0)break; -      /*Otherwise go on to the next protocol, and skip the clean-up below.*/ -      else if(ret==0)continue; -      /*Tried all the addresses for this protocol. -        Remove it from the list.*/ -      close(fds[pi].fd); -      memmove(fds+pi,fds+pi+1,sizeof(*fds)*(nprotos-pi-1)); -      memmove(addrs+pi,addrs+pi+1,sizeof(*addrs)*(nprotos-pi-1)); -      nprotos--; -      pi--; -    } -  } -  /*Close all the other sockets.*/ -  for(pj=0;pj<nprotos;pj++)if(pi!=pj)close(fds[pj].fd); -  /*If none of them succeeded, we're done.*/ -  if(pi>=nprotos)return OP_FALSE; -  /*Save this address for future connection attempts.*/ -  if(addrs[pi]!=&_stream->addr_info){ -    memcpy(&_stream->addr_info,addrs[pi],sizeof(_stream->addr_info)); -    _stream->addr_info.ai_addr=&_stream->addr.s; -    _stream->addr_info.ai_next=NULL; -    memcpy(&_stream->addr,addrs[pi]->ai_addr,addrs[pi]->ai_addrlen); -  } -  if(OP_URL_IS_SSL(&_stream->url)){ -    SSL *ssl_conn; -    /*Start the SSL connection.*/ -    OP_ASSERT(_stream->ssl_ctx!=NULL); -    ssl_conn=SSL_new(_stream->ssl_ctx); -    if(OP_LIKELY(ssl_conn!=NULL)){ -      ret=op_http_conn_start_tls(_stream,_conn,fds[pi].fd,ssl_conn); -      if(OP_LIKELY(ret>=0))return ret; -      SSL_free(ssl_conn); -    } -    close(fds[pi].fd); -    _conn->fd=OP_INVALID_SOCKET; -    return OP_FALSE; -  } -  /*Just a normal non-SSL connection.*/ -  _conn->ssl_conn=NULL; -  _conn->fd=fds[pi].fd; -  _conn->nrequests_left=OP_PIPELINE_MAX_REQUESTS; -  /*Disable write coalescing. -    We always send whole requests at once and always parse the response headers -     before sending another one.*/ -  op_sock_set_tcp_nodelay(fds[pi].fd,1); -  return 0; -} - -static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn, - const struct addrinfo *_addrs,struct timeb *_start_time){ -  struct timeb     resolve_time; -  struct addrinfo *new_addrs; -  int              ret; -  /*Re-resolve the host if we need to (RFC 6555 says we MUST do so -     occasionally).*/ -  new_addrs=NULL; -  ftime(&resolve_time); -  if(_addrs!=&_stream->addr_info||op_time_diff_ms(&resolve_time, -   &_stream->resolve_time)>=OP_RESOLVE_CACHE_TIMEOUT_MS){ -    new_addrs=op_resolve(_stream->connect_host,_stream->connect_port); -    if(OP_LIKELY(new_addrs!=NULL)){ -      _addrs=new_addrs; -      *&_stream->resolve_time=*&resolve_time; -    } -    else if(OP_LIKELY(_addrs==NULL))return OP_FALSE; -  } -  ret=op_http_connect_impl(_stream,_conn,_addrs,_start_time); -  if(new_addrs!=NULL)freeaddrinfo(new_addrs); -  return ret; -} - -# define OP_BASE64_LENGTH(_len) (((_len)+2)/3*4) - -static const char BASE64_TABLE[64]={ -  'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', -  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', -  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', -  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' -}; - -static char *op_base64_encode(char *_dst,const char *_src,int _len){ -  unsigned s0; -  unsigned s1; -  unsigned s2; -  int      ngroups; -  int      i; -  ngroups=_len/3; -  for(i=0;i<ngroups;i++){ -    s0=_src[3*i+0]; -    s1=_src[3*i+1]; -    s2=_src[3*i+2]; -    _dst[4*i+0]=BASE64_TABLE[s0>>2]; -    _dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4]; -    _dst[4*i+2]=BASE64_TABLE[(s1&15)<<2|s2>>6]; -    _dst[4*i+3]=BASE64_TABLE[s2&63]; -  } -  _len-=3*i; -  if(_len==1){ -    s0=_src[3*i+0]; -    _dst[4*i+0]=BASE64_TABLE[s0>>2]; -    _dst[4*i+1]=BASE64_TABLE[(s0&3)<<4]; -    _dst[4*i+2]='='; -    _dst[4*i+3]='='; -    i++; -  } -  else if(_len==2){ -    s0=_src[3*i+0]; -    s1=_src[3*i+1]; -    _dst[4*i+0]=BASE64_TABLE[s0>>2]; -    _dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4]; -    _dst[4*i+2]=BASE64_TABLE[(s1&15)<<2]; -    _dst[4*i+3]='='; -    i++; -  } -  _dst[4*i]='\0'; -  return _dst+4*i; -} - -/*Construct an HTTP authorization header using RFC 2617's Basic Authentication -   Scheme and append it to the given string buffer.*/ -static int op_sb_append_basic_auth_header(OpusStringBuf *_sb, - const char *_header,const char *_user,const char *_pass){ -  int user_len; -  int pass_len; -  int user_pass_len; -  int base64_len; -  int nbuf_total; -  int ret; -  ret=op_sb_append_string(_sb,_header); -  ret|=op_sb_append(_sb,": Basic ",8); -  user_len=strlen(_user); -  pass_len=strlen(_pass); -  if(OP_UNLIKELY(pass_len>INT_MAX-user_len))return OP_EFAULT; -  if(OP_UNLIKELY(user_len+pass_len>(INT_MAX>>2)*3-3))return OP_EFAULT; -  user_pass_len=user_len+1+pass_len; -  base64_len=OP_BASE64_LENGTH(user_pass_len); -  /*Stick "user:pass" at the end of the buffer so we can Base64 encode it -     in-place.*/ -  nbuf_total=_sb->nbuf; -  if(OP_UNLIKELY(base64_len>INT_MAX-nbuf_total))return OP_EFAULT; -  nbuf_total+=base64_len; -  ret|=op_sb_ensure_capacity(_sb,nbuf_total); -  if(OP_UNLIKELY(ret<0))return ret; -  _sb->nbuf=nbuf_total-user_pass_len; -  OP_ALWAYS_TRUE(!op_sb_append(_sb,_user,user_len)); -  OP_ALWAYS_TRUE(!op_sb_append(_sb,":",1)); -  OP_ALWAYS_TRUE(!op_sb_append(_sb,_pass,pass_len)); -  op_base64_encode(_sb->buf+nbuf_total-base64_len, -   _sb->buf+nbuf_total-user_pass_len,user_pass_len); -  return op_sb_append(_sb,"\r\n",2); -} - -static int op_http_allow_pipelining(const char *_server){ -  /*Servers known to do bad things with pipelined requests. -    This list is taken from Gecko's nsHttpConnection::SupportsPipelining() (in -     netwerk/protocol/http/nsHttpConnection.cpp).*/ -  static const char *BAD_SERVERS[]={ -    "EFAServer/", -    "Microsoft-IIS/4.", -    "Microsoft-IIS/5.", -    "Netscape-Enterprise/3.", -    "Netscape-Enterprise/4.", -    "Netscape-Enterprise/5.", -    "Netscape-Enterprise/6.", -    "WebLogic 3.", -    "WebLogic 4.", -    "WebLogic 5.", -    "WebLogic 6.", -    "Winstone Servlet Engine v0." -  }; -# define NBAD_SERVERS ((int)(sizeof(BAD_SERVERS)/sizeof(*BAD_SERVERS))) -  if(*_server>='E'&&*_server<='W'){ -    int si; -    for(si=0;si<NBAD_SERVERS;si++){ -      if(strncmp(_server,BAD_SERVERS[si],strlen(BAD_SERVERS[si]))==0){ -        return 0; -      } -    } -  } -  return 1; -# undef NBAD_SERVERS -} - -static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url, - int _skip_certificate_check,const char *_proxy_host,unsigned _proxy_port, - const char *_proxy_user,const char *_proxy_pass,OpusServerInfo *_info){ -  struct addrinfo *addrs; -  int              nredirs; -  int              ret; -#if defined(_WIN32) -  op_init_winsock(); -#endif -  ret=op_parse_url(&_stream->url,_url); -  if(OP_UNLIKELY(ret<0))return ret; -  if(_proxy_host!=NULL){ -    if(OP_UNLIKELY(_proxy_port>65535U))return OP_EINVAL; -    _stream->connect_host=op_string_dup(_proxy_host); -    _stream->connect_port=_proxy_port; -  } -  else{ -    _stream->connect_host=_stream->url.host; -    _stream->connect_port=_stream->url.port; -  } -  addrs=NULL; -  for(nredirs=0;nredirs<OP_REDIRECT_LIMIT;nredirs++){ -    OpusParsedURL  next_url; -    struct timeb   start_time; -    struct timeb   end_time; -    char          *next; -    char          *status_code; -    int            minor_version_pos; -    int            v1_1_compat; -    /*Initialize the SSL library if necessary.*/ -    if(OP_URL_IS_SSL(&_stream->url)&&_stream->ssl_ctx==NULL){ -      SSL_CTX *ssl_ctx; -# if !defined(OPENSSL_NO_LOCKING) -      /*The documentation says SSL_library_init() is not reentrant. -        We don't want to add our own depenencies on a threading library, and it -         appears that it's safe to call OpenSSL's locking functions before the -         library is initialized, so that's what we'll do (really OpenSSL should -         do this for us). -        This doesn't guarantee that _other_ threads in the application aren't -         calling SSL_library_init() at the same time, but there's not much we -         can do about that.*/ -      CRYPTO_w_lock(CRYPTO_LOCK_SSL); -# endif -      SSL_library_init(); -      /*Needed to get SHA2 algorithms with old OpenSSL versions.*/ -      OpenSSL_add_ssl_algorithms(); -# if !defined(OPENSSL_NO_LOCKING) -      CRYPTO_w_unlock(CRYPTO_LOCK_SSL); -# endif -      ssl_ctx=SSL_CTX_new(SSLv23_client_method()); -      if(ssl_ctx==NULL)return OP_EFAULT; -      if(!_skip_certificate_check){ -        /*We don't do anything if this fails, since it just means we won't load -           any certificates (and thus all checks will fail). -          However, as that is probably the result of a system -           mis-configuration, assert here to make it easier to identify.*/ -        OP_ALWAYS_TRUE(SSL_CTX_set_default_verify_paths(ssl_ctx)); -        SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,NULL); -      } -      _stream->ssl_ctx=ssl_ctx; -      _stream->skip_certificate_check=_skip_certificate_check; -      if(_proxy_host!=NULL){ -        /*We need to establish a CONNECT tunnel to handle https proxying. -          Build the request we'll send to do so.*/ -        _stream->proxy_connect.nbuf=0; -        ret=op_sb_append(&_stream->proxy_connect,"CONNECT ",8); -        ret|=op_sb_append_string(&_stream->proxy_connect,_stream->url.host); -        ret|=op_sb_append_port(&_stream->proxy_connect,_stream->url.port); -        /*CONNECT requires at least HTTP 1.1.*/ -        ret|=op_sb_append(&_stream->proxy_connect," HTTP/1.1\r\n",11); -        ret|=op_sb_append(&_stream->proxy_connect,"Host: ",6); -        ret|=op_sb_append_string(&_stream->proxy_connect,_stream->url.host); -        /*The example in RFC 2817 Section 5.2 specifies an explicit port even -           when connecting to the default port. -          Given that the proxy doesn't know whether we're trying to connect to -           an http or an https URL except by the port number, this seems like a -           good idea.*/ -        ret|=op_sb_append_port(&_stream->proxy_connect,_stream->url.port); -        ret|=op_sb_append(&_stream->proxy_connect,"\r\n",2); -        ret|=op_sb_append(&_stream->proxy_connect,"User-Agent: .\r\n",15); -        if(_proxy_user!=NULL&&_proxy_pass!=NULL){ -          ret|=op_sb_append_basic_auth_header(&_stream->proxy_connect, -           "Proxy-Authorization",_proxy_user,_proxy_pass); -        } -        /*For backwards compatibility.*/ -        ret|=op_sb_append(&_stream->proxy_connect, -         "Proxy-Connection: keep-alive\r\n",30); -        ret|=op_sb_append(&_stream->proxy_connect,"\r\n",2); -        if(OP_UNLIKELY(ret<0))return ret; -      } -    } -    /*Actually make the connection.*/ -    ret=op_http_connect(_stream,_stream->conns+0,addrs,&start_time); -    if(OP_UNLIKELY(ret<0))return ret; -    /*Build the request to send.*/ -    _stream->request.nbuf=0; -    ret=op_sb_append(&_stream->request,"GET ",4); -    ret|=op_sb_append_string(&_stream->request, -     _proxy_host!=NULL?_url:_stream->url.path); -    /*Send HTTP/1.0 by default for maximum compatibility (so we don't have to -       re-try if HTTP/1.1 fails, though it shouldn't, even for a 1.0 server). -      This means we aren't conditionally compliant with RFC 2145, because we -       violate the requirement that "An HTTP client SHOULD send a request -       version equal to the highest version for which the client is at least -       conditionally compliant...". -      According to RFC 2145, that means we can't claim any compliance with any -       IETF HTTP specification.*/ -    ret|=op_sb_append(&_stream->request," HTTP/1.0\r\n",11); -    /*Remember where this is so we can upgrade to HTTP/1.1 if the server -       supports it.*/ -    minor_version_pos=_stream->request.nbuf-3; -    ret|=op_sb_append(&_stream->request,"Host: ",6); -    ret|=op_sb_append_string(&_stream->request,_stream->url.host); -    if(!OP_URL_IS_DEFAULT_PORT(&_stream->url)){ -      ret|=op_sb_append_port(&_stream->request,_stream->url.port); -    } -    ret|=op_sb_append(&_stream->request,"\r\n",2); -    /*User-Agents have been a bad idea, so send as little as possible. -      RFC 2616 requires at least one token in the User-Agent, which must have -       at least one character.*/ -    ret|=op_sb_append(&_stream->request,"User-Agent: .\r\n",15); -    if(_proxy_host!=NULL&&!OP_URL_IS_SSL(&_stream->url) -     &&_proxy_user!=NULL&&_proxy_pass!=NULL){ -      ret|=op_sb_append_basic_auth_header(&_stream->request, -       "Proxy-Authorization",_proxy_user,_proxy_pass); -    } -    if(_stream->url.user!=NULL&&_stream->url.pass!=NULL){ -      ret|=op_sb_append_basic_auth_header(&_stream->request, -       "Authorization",_stream->url.user,_stream->url.pass); -    } -    /*Always send a Referer [sic] header. -      It's common to refuse to serve a resource unless one is present. -      We just use the relative "/" URI to suggest we came from the same domain, -       as this is the most common check. -      This might violate RFC 2616's mandate that the field "MUST NOT be sent if -       the Request-URI was obtained from a source that does not have its own -       URI, such as input from the user keyboard," but we don't really have any -       way to know.*/ -    /*TODO: Should we update this on redirects?*/ -    ret|=op_sb_append(&_stream->request,"Referer: /\r\n",12); -    /*Always send a Range request header to find out if we're seekable. -      This requires an HTTP/1.1 server to succeed, but we'll still get what we -       want with an HTTP/1.0 server that ignores this request header.*/ -    ret|=op_sb_append(&_stream->request,"Range: bytes=0-\r\n",17); -    /*Remember where this is so we can append offsets to it later.*/ -    _stream->request_tail=_stream->request.nbuf-4; -    ret|=op_sb_append(&_stream->request,"\r\n",2); -    if(OP_UNLIKELY(ret<0))return ret; -    ret=op_http_conn_write_fully(_stream->conns+0, -     _stream->request.buf,_stream->request.nbuf); -    if(OP_UNLIKELY(ret<0))return ret; -    ret=op_http_conn_read_response(_stream->conns+0,&_stream->response); -    if(OP_UNLIKELY(ret<0))return ret; -    ftime(&end_time); -    next=op_http_parse_status_line(&v1_1_compat,&status_code, -     _stream->response.buf); -    if(OP_UNLIKELY(next==NULL))return OP_FALSE; -    if(status_code[0]=='2'){ -      opus_int64 content_length; -      opus_int64 range_length; -      int        pipeline_supported; -      int        pipeline_disabled; -      /*We only understand 20x codes.*/ -      if(status_code[1]!='0')return OP_FALSE; -      content_length=-1; -      range_length=-1; -      /*Pipelining must be explicitly enabled.*/ -      pipeline_supported=0; -      pipeline_disabled=0; -      for(;;){ -        char *header; -        char *cdr; -        ret=op_http_get_next_header(&header,&cdr,&next); -        if(OP_UNLIKELY(ret<0))return ret; -        if(header==NULL)break; -        if(strcmp(header,"content-length")==0){ -          /*Two Content-Length headers?*/ -          if(OP_UNLIKELY(content_length>=0))return OP_FALSE; -          content_length=op_http_parse_content_length(cdr); -          if(OP_UNLIKELY(content_length<0))return (int)content_length; -          /*Make sure the Content-Length and Content-Range headers match.*/ -          if(range_length>=0&&OP_UNLIKELY(content_length!=range_length)){ -            return OP_FALSE; -          } -        } -        else if(strcmp(header,"content-range")==0){ -          opus_int64 range_first; -          opus_int64 range_last; -          /*Two Content-Range headers?*/ -          if(OP_UNLIKELY(range_length>=0))return OP_FALSE; -          ret=op_http_parse_content_range(&range_first,&range_last, -           &range_length,cdr); -          if(OP_UNLIKELY(ret<0))return ret; -          /*"A response with satus code 206 (Partial Content) MUST NOT -             include a Content-Range field with a byte-range-resp-spec of -             '*'."*/ -          if(status_code[2]=='6' -           &&(OP_UNLIKELY(range_first<0)||OP_UNLIKELY(range_last<0))){ -            return OP_FALSE; -          } -          /*We asked for the entire resource.*/ -          if(range_length>=0){ -            /*Quit if we didn't get it.*/ -            if(range_last>=0&&OP_UNLIKELY(range_last!=range_length-1)){ -              return OP_FALSE; -            } -          } -          /*If there was no length, use the end of the range.*/ -          else if(range_last>=0)range_length=range_last+1; -          /*Make sure the Content-Length and Content-Range headers match.*/ -          if(content_length>=0&&OP_UNLIKELY(content_length!=range_length)){ -            return OP_FALSE; -          } -        } -        else if(strcmp(header,"connection")==0){ -          /*According to RFC 2616, if an HTTP/1.1 application does not support -             pipelining, it "MUST include the 'close' connection option in -             every message." -            Therefore, if we receive one in the initial response, disable -             pipelining entirely. -            The server still might support it (e.g., we might just have hit the -             request limit for a temporary child process), but if it doesn't -             and we assume it does, every time we cross a chunk boundary we'll -             error out and reconnect, adding lots of latency.*/ -          ret=op_http_parse_connection(cdr); -          if(OP_UNLIKELY(ret<0))return ret; -          pipeline_disabled|=ret; -        } -        else if(strcmp(header,"server")==0){ -          /*If we got a Server response header, and it wasn't from a known-bad -             server, enable pipelining, as long as it's at least HTTP/1.1. -            According to RFC 2145, the server is supposed to respond with the -             highest minor version number it supports unless it is known or -             suspected that we incorrectly implement the HTTP specification. -            So it should send back at least HTTP/1.1, despite our HTTP/1.0 -             request.*/ -          pipeline_supported=v1_1_compat; -          if(v1_1_compat)pipeline_disabled|=!op_http_allow_pipelining(cdr); -          if(_info!=NULL&&_info->server==NULL)_info->server=op_string_dup(cdr); -        } -        /*Collect station information headers if the caller requested it. -          If there's more than one copy of a header, the first one wins.*/ -        else if(_info!=NULL){ -          if(strcmp(header,"content-type")==0){ -            if(_info->content_type==NULL){ -              _info->content_type=op_string_dup(cdr); -            } -          } -          else if(header[0]=='i'&&header[1]=='c' -           &&(header[2]=='e'||header[2]=='y')&&header[3]=='-'){ -            if(strcmp(header+4,"name")==0){ -              if(_info->name==NULL)_info->name=op_string_dup(cdr); -            } -            else if(strcmp(header+4,"description")==0){ -              if(_info->description==NULL)_info->description=op_string_dup(cdr); -            } -            else if(strcmp(header+4,"genre")==0){ -              if(_info->genre==NULL)_info->genre=op_string_dup(cdr); -            } -            else if(strcmp(header+4,"url")==0){ -              if(_info->url==NULL)_info->url=op_string_dup(cdr); -            } -            else if(strcmp(header,"icy-br")==0 -             ||strcmp(header,"ice-bitrate")==0){ -              if(_info->bitrate_kbps<0){ -                opus_int64 bitrate_kbps; -                /*Just re-using this function to parse a random unsigned -                   integer field.*/ -                bitrate_kbps=op_http_parse_content_length(cdr); -                if(bitrate_kbps>=0&&bitrate_kbps<=OP_INT32_MAX){ -                  _info->bitrate_kbps=(opus_int32)bitrate_kbps; -                } -              } -            } -            else if(strcmp(header,"icy-pub")==0 -             ||strcmp(header,"ice-public")==0){ -              if(_info->is_public<0&&(cdr[0]=='0'||cdr[0]=='1')&&cdr[1]=='\0'){ -                _info->is_public=cdr[0]-'0'; -              } -            } -          } -        } -      } -      switch(status_code[2]){ -        /*200 OK*/ -        case '0':break; -        /*203 Non-Authoritative Information*/ -        case '3':break; -        /*204 No Content*/ -        case '4':{ -          if(content_length>=0&&OP_UNLIKELY(content_length!=0)){ -            return OP_FALSE; -          } -        }break; -        /*206 Partial Content*/ -        case '6':{ -          /*No Content-Range header.*/ -          if(OP_UNLIKELY(range_length<0))return OP_FALSE; -          content_length=range_length; -          /*The server supports range requests for this resource. -            We can seek.*/ -          _stream->seekable=1; -        }break; -        /*201 Created: the response "SHOULD include an entity containing a list -           of resource characteristics and location(s)," but not an Opus file. -          202 Accepted: the response "SHOULD include an indication of request's -           current status and either a pointer to a status monitor or some -           estimate of when the user can expect the request to be fulfilled," -           but not an Opus file. -          205 Reset Content: this "MUST NOT include an entity," meaning no Opus -           file. -          207...209 are not yet defined, so we don't know how to handle them.*/ -        default:return OP_FALSE; -      } -      _stream->content_length=content_length; -      _stream->pipeline=pipeline_supported&&!pipeline_disabled; -      /*Pipelining requires HTTP/1.1 persistent connections.*/ -      if(_stream->pipeline)_stream->request.buf[minor_version_pos]='1'; -      _stream->conns[0].pos=0; -      _stream->conns[0].end_pos=_stream->seekable?content_length:-1; -      _stream->conns[0].chunk_size=-1; -      _stream->cur_conni=0; -      _stream->connect_rate=op_time_diff_ms(&end_time,&start_time); -      _stream->connect_rate=OP_MAX(_stream->connect_rate,1); -      if(_info!=NULL)_info->is_ssl=OP_URL_IS_SSL(&_stream->url); -      /*The URL has been successfully opened.*/ -      return 0; -    } -    /*Shouldn't get 1xx; 4xx and 5xx are both failures (and we don't retry). -      Everything else is undefined.*/ -    else if(status_code[0]!='3')return OP_FALSE; -    /*We have some form of redirect request.*/ -    /*We only understand 30x codes.*/ -    if(status_code[1]!='0')return OP_FALSE; -    switch(status_code[2]){ -      /*300 Multiple Choices: "If the server has a preferred choice of -         representation, it SHOULD include the specific URI for that -         representation in the Location field," otherwise we'll fail.*/ -      case '0': -      /*301 Moved Permanently*/ -      case '1': -      /*302 Found*/ -      case '2': -      /*307 Temporary Redirect*/ -      case '7': -      /*308 Permanent Redirect (defined by draft-reschke-http-status-308-07).*/ -      case '8':break; -      /*305 Use Proxy: "The Location field gives the URI of the proxy." -        TODO: This shouldn't actually be that hard to do.*/ -      case '5':return OP_EIMPL; -      /*303 See Other: "The new URI is not a substitute reference for the -         originally requested resource." -        304 Not Modified: "The 304 response MUST NOT contain a message-body." -        306 (Unused) -        309 is not yet defined, so we don't know how to handle it.*/ -      default:return OP_FALSE; -    } -    _url=NULL; -    for(;;){ -      char *header; -      char *cdr; -      ret=op_http_get_next_header(&header,&cdr,&next); -      if(OP_UNLIKELY(ret<0))return ret; -      if(header==NULL)break; -      if(strcmp(header,"location")==0&&OP_LIKELY(_url==NULL))_url=cdr; -    } -    if(OP_UNLIKELY(_url==NULL))return OP_FALSE; -    ret=op_parse_url(&next_url,_url); -    if(OP_UNLIKELY(ret<0))return ret; -    if(_proxy_host==NULL||_stream->ssl_session!=NULL){ -      if(strcmp(_stream->url.host,next_url.host)==0 -       &&_stream->url.port==next_url.port){ -        /*Try to skip re-resolve when connecting to the same host.*/ -        addrs=&_stream->addr_info; -      } -      else{ -        if(_stream->ssl_session!=NULL){ -          /*Forget any cached SSL session from the last host.*/ -          SSL_SESSION_free(_stream->ssl_session); -          _stream->ssl_session=NULL; -        } -      } -    } -    if(_proxy_host==NULL){ -      OP_ASSERT(_stream->connect_host==_stream->url.host); -      _stream->connect_host=next_url.host; -      _stream->connect_port=next_url.port; -    } -    /*Always try to skip re-resolve for proxy connections.*/ -    else addrs=&_stream->addr_info; -    op_parsed_url_clear(&_stream->url); -    *&_stream->url=*&next_url; -    /*TODO: On servers/proxies that support pipelining, we might be able to -       re-use this connection.*/ -    op_http_conn_close(_stream,_stream->conns+0,&_stream->lru_head,1); -  } -  /*Redirection limit reached.*/ -  return OP_FALSE; -} - -static int op_http_conn_send_request(OpusHTTPStream *_stream, - OpusHTTPConn *_conn,opus_int64 _pos,opus_int32 _chunk_size, - int _try_not_to_block){ -  opus_int64 next_end; -  int        ret; -  /*We shouldn't have another request outstanding.*/ -  OP_ASSERT(_conn->next_pos<0); -  /*Build the request to send.*/ -  OP_ASSERT(_stream->request.nbuf>=_stream->request_tail); -  _stream->request.nbuf=_stream->request_tail; -  ret=op_sb_append_nonnegative_int64(&_stream->request,_pos); -  ret|=op_sb_append(&_stream->request,"-",1); -  if(_chunk_size>0&&OP_ADV_OFFSET(_pos,2*_chunk_size)<_stream->content_length){ -    /*We shouldn't be pipelining requests with non-HTTP/1.1 servers.*/ -    OP_ASSERT(_stream->pipeline); -    next_end=_pos+_chunk_size; -    ret|=op_sb_append_nonnegative_int64(&_stream->request,next_end-1); -    /*Use a larger chunk size for our next request.*/ -    _chunk_size<<=1; -    /*But after a while, just request the rest of the resource.*/ -    if(_chunk_size>OP_PIPELINE_CHUNK_SIZE_MAX)_chunk_size=-1; -  } -  else{ -    /*Either this was a non-pipelined request or we were close enough to the -       end to just ask for the rest.*/ -    next_end=-1; -    _chunk_size=-1; -  } -  ret|=op_sb_append(&_stream->request,"\r\n\r\n",4); -  if(OP_UNLIKELY(ret<0))return ret; -  /*If we don't want to block, check to see if there's enough space in the send -     queue. -    There's still a chance we might block, even if there is enough space, but -     it's a much slimmer one. -    Blocking at all is pretty unlikely, as we won't have any requests queued -     when _try_not_to_block is set, so if FIONSPACE isn't available (e.g., on -     Linux), just skip the test.*/ -  if(_try_not_to_block){ -# if defined(FIONSPACE) -    int available; -    ret=ioctl(_conn->fd,FIONSPACE,&available); -    if(ret<0||available<_stream->request.nbuf)return 1; -# endif -  } -  ret=op_http_conn_write_fully(_conn, -   _stream->request.buf,_stream->request.nbuf); -  if(OP_UNLIKELY(ret<0))return ret; -  _conn->next_pos=_pos; -  _conn->next_end=next_end; -  /*Save the chunk size to use for the next request.*/ -  _conn->chunk_size=_chunk_size; -  _conn->nrequests_left--; -  return ret; -} - -/*Handles the response to all requests after the first one. -  Return: 1 if the connection was closed or timed out, 0 on success, or a -           negative value on any other error.*/ -static int op_http_conn_handle_response(OpusHTTPStream *_stream, - OpusHTTPConn *_conn){ -  char       *next; -  char       *status_code; -  opus_int64  range_length; -  opus_int64  next_pos; -  opus_int64  next_end; -  int         ret; -  ret=op_http_conn_read_response(_conn,&_stream->response); -  /*If the server just closed the connection on us, we may have just hit a -     connection re-use limit, so we might want to retry.*/ -  if(OP_UNLIKELY(ret<0))return ret==OP_EREAD?1:ret; -  next=op_http_parse_status_line(NULL,&status_code,_stream->response.buf); -  if(OP_UNLIKELY(next==NULL))return OP_FALSE; -  /*We _need_ a 206 Partial Content response. -    Nothing else will do.*/ -  if(strncmp(status_code,"206",3)!=0){ -    /*But on a 408 Request Timeout, we might want to re-try.*/ -    return strncmp(status_code,"408",3)==0?1:OP_FALSE; -  } -  next_pos=_conn->next_pos; -  next_end=_conn->next_end; -  range_length=-1; -  for(;;){ -    char *header; -    char *cdr; -    ret=op_http_get_next_header(&header,&cdr,&next); -    if(OP_UNLIKELY(ret<0))return ret; -    if(header==NULL)break; -    if(strcmp(header,"content-range")==0){ -      opus_int64 range_first; -      opus_int64 range_last; -      /*Two Content-Range headers?*/ -      if(OP_UNLIKELY(range_length>=0))return OP_FALSE; -      ret=op_http_parse_content_range(&range_first,&range_last, -       &range_length,cdr); -      if(OP_UNLIKELY(ret<0))return ret; -      /*"A response with satus code 206 (Partial Content) MUST NOT -         include a Content-Range field with a byte-range-resp-spec of -         '*'."*/ -      if(OP_UNLIKELY(range_first<0)||OP_UNLIKELY(range_last<0))return OP_FALSE; -      /*We also don't want range_last to overflow.*/ -      if(OP_UNLIKELY(range_last>=OP_INT64_MAX))return OP_FALSE; -      range_last++; -      /*Quit if we didn't get the offset we asked for.*/ -      if(range_first!=next_pos)return OP_FALSE; -      if(next_end<0){ -        /*We asked for the rest of the resource.*/ -        if(range_length>=0){ -          /*Quit if we didn't get it.*/ -          if(OP_UNLIKELY(range_last!=range_length))return OP_FALSE; -        } -        /*If there was no length, use the end of the range.*/ -        else range_length=range_last; -        next_end=range_last; -      } -      else{ -        if(range_last!=next_end)return OP_FALSE; -        /*If there was no length, use the larger of the content length or the -           end of this chunk.*/ -        if(range_length<0){ -          range_length=OP_MAX(range_last,_stream->content_length); -        } -      } -    } -    else if(strcmp(header,"content-length")==0){ -      opus_int64 content_length; -      /*Validate the Content-Length header, if present, against the request we -         made.*/ -      content_length=op_http_parse_content_length(cdr); -      if(OP_UNLIKELY(content_length<0))return (int)content_length; -      if(next_end<0){ -        /*If we haven't seen the Content-Range header yet and we asked for the -            rest of the resource, set next_end, so we can make sure they match -            when we do find the Content-Range header.*/ -        if(OP_UNLIKELY(next_pos>OP_INT64_MAX-content_length))return OP_FALSE; -        next_end=next_pos+content_length; -      } -      /*Otherwise, make sure they match now.*/ -      else if(OP_UNLIKELY(next_end-next_pos!=content_length))return OP_FALSE; -    } -    else if(strcmp(header,"connection")==0){ -      ret=op_http_parse_connection(cdr); -      if(OP_UNLIKELY(ret<0))return ret; -      /*If the server told us it was going to close the connection, don't make -         any more requests.*/ -      if(OP_UNLIKELY(ret>0))_conn->nrequests_left=0; -    } -  } -  /*No Content-Range header.*/ -  if(OP_UNLIKELY(range_length<0))return OP_FALSE; -  /*Update the content_length if necessary.*/ -  _stream->content_length=range_length; -  _conn->pos=next_pos; -  _conn->end_pos=next_end; -  _conn->next_pos=-1; -  return 0; -} - -/*Open a new connection that will start reading at byte offset _pos. -  _pos:        The byte offset to start reading from. -  _chunk_size: The number of bytes to ask for in the initial request, or -1 to -                request the rest of the resource. -               This may be more bytes than remain, in which case it will be -                converted into a request for the rest.*/ -static int op_http_conn_open_pos(OpusHTTPStream *_stream, - OpusHTTPConn *_conn,opus_int64 _pos,opus_int32 _chunk_size){ -  struct timeb  start_time; -  struct timeb  end_time; -  opus_int32    connect_rate; -  opus_int32    connect_time; -  int           ret; -  ret=op_http_connect(_stream,_conn,&_stream->addr_info,&start_time); -  if(OP_UNLIKELY(ret<0))return ret; -  ret=op_http_conn_send_request(_stream,_conn,_pos,_chunk_size,0); -  if(OP_UNLIKELY(ret<0))return ret; -  ret=op_http_conn_handle_response(_stream,_conn); -  if(OP_UNLIKELY(ret!=0))return OP_FALSE; -  ftime(&end_time); -  _stream->cur_conni=_conn-_stream->conns; -  OP_ASSERT(_stream->cur_conni>=0&&_stream->cur_conni<OP_NCONNS_MAX); -  /*The connection has been successfully opened. -    Update the connection time estimate.*/ -  connect_time=op_time_diff_ms(&end_time,&start_time); -  connect_rate=_stream->connect_rate; -  connect_rate+=OP_MAX(connect_time,1)-connect_rate+8>>4; -  _stream->connect_rate=connect_rate; -  return 0; -} - -/*Read data from the current response body. -  If we're pipelining and we get close to the end of this response, queue -   another request. -  If we've reached the end of this response body, parse the next response and -   keep going. -  [out] _buf: Returns the data read. -  _buf_size:  The size of the buffer. -  Return: A positive number of bytes read on success. -          0:        The connection was closed. -          OP_EREAD: There was a fatal read error.*/ -static int op_http_conn_read_body(OpusHTTPStream *_stream, - OpusHTTPConn *_conn,unsigned char *_buf,int _buf_size){ -  opus_int64 pos; -  opus_int64 end_pos; -  opus_int64 next_pos; -  opus_int64 content_length; -  int        nread; -  int        pipeline; -  int        ret; -  /*Currently this function can only be called on the LRU head. -    Otherwise, we'd need a _pnext pointer if we needed to close the connection, -     and re-opening it would re-organize the lists.*/ -  OP_ASSERT(_stream->lru_head==_conn); -  /*We should have filterd out empty reads by this point.*/ -  OP_ASSERT(_buf_size>0); -  pos=_conn->pos; -  end_pos=_conn->end_pos; -  next_pos=_conn->next_pos; -  pipeline=_stream->pipeline; -  content_length=_stream->content_length; -  if(end_pos>=0){ -    /*Have we reached the end of the current response body?*/ -    if(pos>=end_pos){ -      OP_ASSERT(content_length>=0); -      /*If this was the end of the stream, we're done. -        Also return early if a non-blocking read was requested (regardless of -         whether we might be able to parse the next response without -         blocking).*/ -      if(content_length<=end_pos)return 0; -      /*Otherwise, start on the next response.*/ -      if(next_pos<0){ -        /*We haven't issued another request yet.*/ -        if(!pipeline||_conn->nrequests_left<=0){ -          /*There are two ways to get here: either the server told us it was -             going to close the connection after the last request, or we -             thought we were reading the whole resource, but it grew while we -             were reading it. -            The only way the latter could have happened is if content_length -             changed while seeking. -            Open a new request to read the rest.*/ -          OP_ASSERT(_stream->seekable); -          /*Try to open a new connection to read another chunk.*/ -          op_http_conn_close(_stream,_conn,&_stream->lru_head,1); -          /*If we're not pipelining, we should be requesting the rest.*/ -          OP_ASSERT(pipeline||_conn->chunk_size==-1); -          ret=op_http_conn_open_pos(_stream,_conn,end_pos,_conn->chunk_size); -          if(OP_UNLIKELY(ret<0))return OP_EREAD; -        } -        else{ -          /*Issue the request now (better late than never).*/ -          ret=op_http_conn_send_request(_stream,_conn,pos,_conn->chunk_size,0); -          if(OP_UNLIKELY(ret<0))return OP_EREAD; -          next_pos=_conn->next_pos; -          OP_ASSERT(next_pos>=0); -        } -      } -      if(next_pos>=0){ -        /*We shouldn't be trying to read past the current request body if we're -           seeking somewhere else.*/ -        OP_ASSERT(next_pos==end_pos); -        ret=op_http_conn_handle_response(_stream,_conn); -        if(OP_UNLIKELY(ret<0))return OP_EREAD; -        if(OP_UNLIKELY(ret>0)&&pipeline){ -          opus_int64 next_end; -          next_end=_conn->next_end; -          /*Our request timed out or the server closed the connection. -            Try re-connecting.*/ -          op_http_conn_close(_stream,_conn,&_stream->lru_head,1); -          /*Unless there's a bug, we should be able to convert -             (next_pos,next_end) into valid (_pos,_chunk_size) parameters.*/ -          OP_ASSERT(next_end<0 -           ||next_end-next_pos>=0&&next_end-next_pos<=OP_INT32_MAX); -          ret=op_http_conn_open_pos(_stream,_conn,next_pos, -           next_end<0?-1:(opus_int32)(next_end-next_pos)); -          if(OP_UNLIKELY(ret<0))return OP_EREAD; -        } -        else if(OP_UNLIKELY(ret!=0))return OP_EREAD; -      } -      pos=_conn->pos; -      end_pos=_conn->end_pos; -      content_length=_stream->content_length; -    } -    OP_ASSERT(end_pos>pos); -    _buf_size=OP_MIN(_buf_size,end_pos-pos); -  } -  nread=op_http_conn_read(_conn,(char *)_buf,_buf_size,1); -  if(OP_UNLIKELY(nread<0))return nread; -  pos+=nread; -  _conn->pos=pos; -  OP_ASSERT(end_pos<0||content_length>=0); -  /*TODO: If nrequests_left<=0, we can't make a new request, and there will be -     a big pause after we hit the end of the chunk while we open a new -     connection. -    It would be nice to be able to start that process now, but we have no way -     to do it in the background without blocking (even if we could start it, we -     have no guarantee the application will return control to us in a -     sufficiently timely manner to allow us to complete it, and this is -     uncommon enough that it's not worth using threads just for this).*/ -  if(end_pos>=0&&end_pos<content_length&&next_pos<0 -   &&pipeline&&OP_LIKELY(_conn->nrequests_left>0)){ -    opus_int64 request_thresh; -    opus_int32 chunk_size; -    /*Are we getting close to the end of the current response body? -      If so, we should request more data.*/ -    request_thresh=_stream->connect_rate*_conn->read_rate>>12; -    /*But don't commit ourselves too quickly.*/ -    chunk_size=_conn->chunk_size; -    if(chunk_size>=0)request_thresh=OP_MIN(chunk_size>>2,request_thresh); -    if(end_pos-pos<request_thresh){ -      ret=op_http_conn_send_request(_stream,_conn,end_pos,_conn->chunk_size,1); -      if(OP_UNLIKELY(ret<0))return OP_EREAD; -    } -  } -  return nread; -} - -static int op_http_stream_read(void *_stream, - unsigned char *_ptr,int _buf_size){ -  OpusHTTPStream *stream; -  ptrdiff_t       nread; -  opus_int64      size; -  opus_int64      pos; -  int             ci; -  stream=(OpusHTTPStream *)_stream; -  /*Check for an empty read.*/ -  if(_buf_size<=0)return 0; -  ci=stream->cur_conni; -  /*No current connection => EOF.*/ -  if(ci<0)return 0; -  pos=stream->conns[ci].pos; -  size=stream->content_length; -  /*Check for EOF.*/ -  if(size>=0){ -    if(pos>=size)return 0; -    /*Check for a short read.*/ -    if(_buf_size>size-pos)_buf_size=(int)(size-pos); -  } -  nread=op_http_conn_read_body(stream,stream->conns+ci,_ptr,_buf_size); -  if(OP_UNLIKELY(nread<=0)){ -    /*We hit an error or EOF. -      Either way, we're done with this connection.*/ -    op_http_conn_close(stream,stream->conns+ci,&stream->lru_head,1); -    stream->cur_conni=-1; -    stream->pos=pos; -  } -  return nread; -} - -/*Discard data until we reach the _target position. -  This destroys the contents of _stream->response.buf, as we need somewhere to -   read this data, and that is a convenient place. -  _just_read_ahead: Whether or not this is a plain fast-forward. -                    If 0, we need to issue a new request for a chunk at _target -                     and discard all the data from our current request(s). -                    Otherwise, we should be able to reach _target without -                     issuing any new requests. -  _target:          The stream position to which to read ahead.*/ -static int op_http_conn_read_ahead(OpusHTTPStream *_stream, - OpusHTTPConn *_conn,int _just_read_ahead,opus_int64 _target){ -  opus_int64 pos; -  opus_int64 end_pos; -  opus_int64 next_pos; -  opus_int64 next_end; -  ptrdiff_t  nread; -  int        ret; -  pos=_conn->pos; -  end_pos=_conn->end_pos; -  next_pos=_conn->next_pos; -  next_end=_conn->next_end; -  if(!_just_read_ahead){ -    /*We need to issue a new pipelined request. -      This is the only case where we allow more than one outstanding request -       at a time, so we need to reset next_pos (we'll restore it below if we -       did have an outstanding request).*/ -    OP_ASSERT(_stream->pipeline); -    _conn->next_pos=-1; -    ret=op_http_conn_send_request(_stream,_conn,_target, -     OP_PIPELINE_CHUNK_SIZE,0); -    if(OP_UNLIKELY(ret<0))return ret; -  } -  /*We can reach the target position by reading forward in the current chunk.*/ -  if(_just_read_ahead&&(end_pos<0||_target<end_pos))end_pos=_target; -  else if(next_pos>=0){ -    opus_int64 next_next_pos; -    opus_int64 next_next_end; -    /*We already have a request outstanding. -      Finish off the current chunk.*/ -    while(pos<end_pos){ -      nread=op_http_conn_read(_conn,_stream->response.buf, -       (int)OP_MIN(end_pos-pos,_stream->response.cbuf),1); -      /*We failed to read ahead.*/ -      if(nread<=0)return OP_FALSE; -      pos+=nread; -    } -    OP_ASSERT(pos==end_pos); -    if(_just_read_ahead){ -      next_next_pos=next_next_end=-1; -      end_pos=_target; -    } -    else{ -      OP_ASSERT(_conn->next_pos==_target); -      next_next_pos=_target; -      next_next_end=_conn->next_end; -      _conn->next_pos=next_pos; -      _conn->next_end=next_end; -      end_pos=next_end; -    } -    ret=op_http_conn_handle_response(_stream,_conn); -    if(OP_UNLIKELY(ret!=0))return OP_FALSE; -    _conn->next_pos=next_next_pos; -    _conn->next_end=next_next_end; -  } -  while(pos<end_pos){ -    nread=op_http_conn_read(_conn,_stream->response.buf, -     (int)OP_MIN(end_pos-pos,_stream->response.cbuf),1); -    /*We failed to read ahead.*/ -    if(nread<=0)return OP_FALSE; -    pos+=nread; -  } -  OP_ASSERT(pos==end_pos); -  if(!_just_read_ahead){ -    ret=op_http_conn_handle_response(_stream,_conn); -    if(OP_UNLIKELY(ret!=0))return OP_FALSE; -  } -  else _conn->pos=end_pos; -  OP_ASSERT(_conn->pos==_target); -  return 0; -} - -static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){ -  struct timeb     seek_time; -  OpusHTTPStream  *stream; -  OpusHTTPConn    *conn; -  OpusHTTPConn   **pnext; -  OpusHTTPConn    *close_conn; -  OpusHTTPConn   **close_pnext; -  opus_int64       content_length; -  opus_int64       pos; -  int              pipeline; -  int              ci; -  int              ret; -  stream=(OpusHTTPStream *)_stream; -  if(!stream->seekable)return -1; -  content_length=stream->content_length; -  /*If we're seekable, we should have gotten a Content-Length.*/ -  OP_ASSERT(content_length>=0); -  ci=stream->cur_conni; -  pos=ci<0?content_length:stream->conns[ci].pos; -  switch(_whence){ -    case SEEK_SET:{ -      /*Check for overflow:*/ -      if(_offset<0)return -1; -      pos=_offset; -    }break; -    case SEEK_CUR:{ -      /*Check for overflow:*/ -      if(_offset<-pos||_offset>OP_INT64_MAX-pos)return -1; -      pos+=_offset; -    }break; -    case SEEK_END:{ -      /*Check for overflow:*/ -      if(_offset>content_length||_offset<content_length-OP_INT64_MAX)return -1; -      pos=content_length-_offset; -    }break; -    default:return -1; -  } -  /*Mark when we deactivated the active connection.*/ -  if(ci>=0){ -    op_http_conn_read_rate_update(stream->conns+ci); -    *&seek_time=*&stream->conns[ci].read_time; -  } -  else ftime(&seek_time); -  /*If we seeked past the end of the stream, just disable the active -     connection.*/ -  if(pos>=content_length){ -    stream->cur_conni=-1; -    stream->pos=pos; -    return 0; -  } -  /*First try to find a connection we can use without waiting.*/ -  pnext=&stream->lru_head; -  conn=stream->lru_head; -  while(conn!=NULL){ -    opus_int64 conn_pos; -    opus_int64 end_pos; -    int        available; -    /*If this connection has been dormant too long or has made too many -       requests, close it. -      This is to prevent us from hitting server limits/firewall timeouts.*/ -    if(op_time_diff_ms(&seek_time,&conn->read_time)> -     OP_CONNECTION_IDLE_TIMEOUT_MS -     ||conn->nrequests_left<OP_PIPELINE_MIN_REQUESTS){ -      op_http_conn_close(stream,conn,pnext,1); -      conn=*pnext; -      continue; -    } -    available=op_http_conn_estimate_available(conn); -    conn_pos=conn->pos; -    end_pos=conn->end_pos; -    if(conn->next_pos>=0){ -      OP_ASSERT(end_pos>=0); -      OP_ASSERT(conn->next_pos==end_pos); -      end_pos=conn->next_end; -    } -    OP_ASSERT(end_pos<0||conn_pos<=end_pos); -    /*Can we quickly read ahead without issuing a new request or waiting for -       any more data? -      If we have an oustanding request, we'll over-estimate the amount of data -       it has available (because we'll count the response headers, too), but -       that probably doesn't matter.*/ -    if(conn_pos<=pos&&pos-conn_pos<=available&&(end_pos<0||pos<end_pos)){ -      /*Found a suitable connection to re-use.*/ -      ret=op_http_conn_read_ahead(stream,conn,1,pos); -      if(OP_UNLIKELY(ret<0)){ -        /*The connection might have become stale, so close it and keep going.*/ -        op_http_conn_close(stream,conn,pnext,1); -        conn=*pnext; -        continue; -      } -      /*Sucessfully resurrected this connection.*/ -      *pnext=conn->next; -      conn->next=stream->lru_head; -      stream->lru_head=conn; -      stream->cur_conni=conn-stream->conns; -      return 0; -    } -    pnext=&conn->next; -    conn=conn->next; -  } -  /*Chances are that didn't work, so now try to find one we can use by reading -     ahead a reasonable amount and/or by issuing a new request.*/ -  close_pnext=NULL; -  close_conn=NULL; -  pnext=&stream->lru_head; -  conn=stream->lru_head; -  pipeline=stream->pipeline; -  while(conn!=NULL){ -    opus_int64 conn_pos; -    opus_int64 end_pos; -    opus_int64 read_ahead_thresh; -    int        available; -    int        just_read_ahead; -    /*Dividing by 2048 instead of 1000 scales this by nearly 1/2, biasing away -       from connection re-use (and roughly compensating for the lag required to -       reopen the TCP window of a connection that's been idle). -      There's no overflow checking here, because it's vanishingly unlikely, and -       all it would do is cause us to make poor decisions.*/ -    read_ahead_thresh=OP_MAX(OP_READAHEAD_THRESH_MIN, -     stream->connect_rate*conn->read_rate>>11); -    available=op_http_conn_estimate_available(conn); -    conn_pos=conn->pos; -    end_pos=conn->end_pos; -    if(conn->next_pos>=0){ -      OP_ASSERT(end_pos>=0); -      OP_ASSERT(conn->next_pos==end_pos); -      end_pos=conn->next_end; -    } -    OP_ASSERT(end_pos<0||conn_pos<=end_pos); -    /*Can we quickly read ahead without issuing a new request?*/ -    just_read_ahead=conn_pos<=pos&&pos-conn_pos-available<=read_ahead_thresh -     &&(end_pos<0||pos<end_pos); -    if(just_read_ahead||pipeline&&end_pos>=0 -     &&end_pos-conn_pos-available<=read_ahead_thresh){ -      /*Found a suitable connection to re-use.*/ -      ret=op_http_conn_read_ahead(stream,conn,just_read_ahead,pos); -      if(OP_UNLIKELY(ret<0)){ -        /*The connection might have become stale, so close it and keep going.*/ -        op_http_conn_close(stream,conn,pnext,1); -        conn=*pnext; -        continue; -      } -      /*Sucessfully resurrected this connection.*/ -      *pnext=conn->next; -      conn->next=stream->lru_head; -      stream->lru_head=conn; -      stream->cur_conni=conn-stream->conns; -      return 0; -    } -    close_pnext=pnext; -    close_conn=conn; -    pnext=&conn->next; -    conn=conn->next; -  } -  /*No suitable connections. -    Open a new one.*/ -  if(stream->free_head==NULL){ -    /*All connections in use. -      Expire one of them (we should have already picked which one when scanning -       the list).*/ -    OP_ASSERT(close_conn!=NULL); -    OP_ASSERT(close_pnext!=NULL); -    op_http_conn_close(stream,close_conn,close_pnext,1); -  } -  OP_ASSERT(stream->free_head!=NULL); -  conn=stream->free_head; -  /*If we can pipeline, only request a chunk of data. -    If we're seeking now, there's a good chance we will want to seek again -     soon, and this avoids committing this connection to reading the rest of -     the stream. -    Particularly with SSL or proxies, issuing a new request on the same -     connection can be substantially faster than opening a new one. -    This also limits the amount of data the server will blast at us on this -     connection if we later seek elsewhere and start reading from a different -     connection.*/ -  ret=op_http_conn_open_pos(stream,conn,pos, -   pipeline?OP_PIPELINE_CHUNK_SIZE:-1); -  if(OP_UNLIKELY(ret<0)){ -    op_http_conn_close(stream,conn,&stream->lru_head,1); -    return -1; -  } -  return 0; -} - -static opus_int64 op_http_stream_tell(void *_stream){ -  OpusHTTPStream *stream; -  int             ci; -  stream=(OpusHTTPStream *)_stream; -  ci=stream->cur_conni; -  return ci<0?stream->pos:stream->conns[ci].pos; -} - -static int op_http_stream_close(void *_stream){ -  OpusHTTPStream *stream; -  stream=(OpusHTTPStream *)_stream; -  if(OP_LIKELY(stream!=NULL)){ -    op_http_stream_clear(stream); -    _ogg_free(stream); -  } -  return 0; -} - -static const OpusFileCallbacks OP_HTTP_CALLBACKS={ -  op_http_stream_read, -  op_http_stream_seek, -  op_http_stream_tell, -  op_http_stream_close -}; -#endif - -void opus_server_info_init(OpusServerInfo *_info){ -  _info->name=NULL; -  _info->description=NULL; -  _info->genre=NULL; -  _info->url=NULL; -  _info->server=NULL; -  _info->content_type=NULL; -  _info->bitrate_kbps=-1; -  _info->is_public=-1; -  _info->is_ssl=0; -} - -void opus_server_info_clear(OpusServerInfo *_info){ -  _ogg_free(_info->content_type); -  _ogg_free(_info->server); -  _ogg_free(_info->url); -  _ogg_free(_info->genre); -  _ogg_free(_info->description); -  _ogg_free(_info->name); -} - -/*The actual URL stream creation function. -  This one isn't extensible like the application-level interface, but because -   it isn't public, we're free to change it in the future.*/ -static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url, - int _skip_certificate_check,const char *_proxy_host,unsigned _proxy_port, - const char *_proxy_user,const char *_proxy_pass,OpusServerInfo *_info){ -  const char *path; -  /*Check to see if this is a valid file: URL.*/ -  path=op_parse_file_url(_url); -  if(path!=NULL){ -    char *unescaped_path; -    void *ret; -    unescaped_path=op_string_dup(path); -    if(OP_UNLIKELY(unescaped_path==NULL))return NULL; -    ret=op_fopen(_cb,op_unescape_url_component(unescaped_path),"rb"); -    _ogg_free(unescaped_path); -    return ret; -  } -#if defined(OP_ENABLE_HTTP) -  /*If not, try http/https.*/ -  else{ -    OpusHTTPStream *stream; -    int             ret; -    stream=(OpusHTTPStream *)_ogg_malloc(sizeof(*stream)); -    if(OP_UNLIKELY(stream==NULL))return NULL; -    op_http_stream_init(stream); -    ret=op_http_stream_open(stream,_url,_skip_certificate_check, -     _proxy_host,_proxy_port,_proxy_user,_proxy_pass,_info); -    if(OP_UNLIKELY(ret<0)){ -      op_http_stream_clear(stream); -      _ogg_free(stream); -      return NULL; -    } -    *_cb=*&OP_HTTP_CALLBACKS; -    return stream; -  } -#else -  (void)_skip_certificate_check; -  (void)_proxy_host; -  (void)_proxy_port; -  (void)_proxy_user; -  (void)_proxy_pass; -  (void)_info; -  return NULL; -#endif -} - -void *op_url_stream_vcreate(OpusFileCallbacks *_cb, - const char *_url,va_list _ap){ -  int             skip_certificate_check; -  const char     *proxy_host; -  opus_int32      proxy_port; -  const char     *proxy_user; -  const char     *proxy_pass; -  OpusServerInfo *pinfo; -  skip_certificate_check=0; -  proxy_host=NULL; -  proxy_port=8080; -  proxy_user=NULL; -  proxy_pass=NULL; -  pinfo=NULL; -  for(;;){ -    ptrdiff_t request; -    request=va_arg(_ap,char *)-(char *)NULL; -    /*If we hit NULL, we're done processing options.*/ -    if(!request)break; -    switch(request){ -      case OP_SSL_SKIP_CERTIFICATE_CHECK_REQUEST:{ -        skip_certificate_check=!!va_arg(_ap,opus_int32); -      }break; -      case OP_HTTP_PROXY_HOST_REQUEST:{ -        proxy_host=va_arg(_ap,const char *); -      }break; -      case OP_HTTP_PROXY_PORT_REQUEST:{ -        proxy_port=va_arg(_ap,opus_int32); -        if(proxy_port<0||proxy_port>(opus_int32)65535)return NULL; -      }break; -      case OP_HTTP_PROXY_USER_REQUEST:{ -        proxy_user=va_arg(_ap,const char *); -      }break; -      case OP_HTTP_PROXY_PASS_REQUEST:{ -        proxy_pass=va_arg(_ap,const char *); -      }break; -      case OP_GET_SERVER_INFO_REQUEST:{ -        pinfo=va_arg(_ap,OpusServerInfo *); -      }break; -      /*Some unknown option.*/ -      default:return NULL; -    } -  } -  /*If the caller has requested server information, proxy it to a local copy to -     simplify error handling.*/ -  if(pinfo!=NULL){ -    OpusServerInfo  info; -    void           *ret; -    opus_server_info_init(&info); -    ret=op_url_stream_create_impl(_cb,_url,skip_certificate_check, -     proxy_host,proxy_port,proxy_user,proxy_pass,&info); -    if(ret!=NULL)*pinfo=*&info; -    else opus_server_info_clear(&info); -    return ret; -  } -  return op_url_stream_create_impl(_cb,_url,skip_certificate_check, -   proxy_host,proxy_port,proxy_user,proxy_pass,NULL); -} - -void *op_url_stream_create(OpusFileCallbacks *_cb, - const char *_url,...){ -  va_list  ap; -  void    *ret; -  va_start(ap,_url); -  ret=op_url_stream_vcreate(_cb,_url,ap); -  va_end(ap); -  return ret; -} - -/*Convenience routines to open/test URLs in a single step.*/ - -OggOpusFile *op_vopen_url(const char *_url,int *_error,va_list _ap){ -  OpusFileCallbacks  cb; -  OggOpusFile       *of; -  void              *source; -  source=op_url_stream_vcreate(&cb,_url,_ap); -  if(OP_UNLIKELY(source==NULL)){ -    if(_error!=NULL)*_error=OP_EFAULT; -    return NULL; -  } -  of=op_open_callbacks(source,&cb,NULL,0,_error); -  if(OP_UNLIKELY(of==NULL))(*cb.close)(source); -  return of; -} - -OggOpusFile *op_open_url(const char *_url,int *_error,...){ -  OggOpusFile *ret; -  va_list      ap; -  va_start(ap,_error); -  ret=op_vopen_url(_url,_error,ap); -  va_end(ap); -  return ret; -} - -OggOpusFile *op_vtest_url(const char *_url,int *_error,va_list _ap){ -  OpusFileCallbacks  cb; -  OggOpusFile       *of; -  void              *source; -  source=op_url_stream_vcreate(&cb,_url,_ap); -  if(OP_UNLIKELY(source==NULL)){ -    if(_error!=NULL)*_error=OP_EFAULT; -    return NULL; -  } -  of=op_test_callbacks(source,&cb,NULL,0,_error); -  if(OP_UNLIKELY(of==NULL))(*cb.close)(source); -  return of; -} - -OggOpusFile *op_test_url(const char *_url,int *_error,...){ -  OggOpusFile *ret; -  va_list      ap; -  va_start(ap,_error); -  ret=op_vtest_url(_url,_error,ap); -  va_end(ap); -  return ret; -} diff --git a/src/opusfile-0.5/src/info.c b/src/opusfile-0.5/src/info.c deleted file mode 100644 index 6cf98516..00000000 --- a/src/opusfile-0.5/src/info.c +++ /dev/null @@ -1,683 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012                * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ********************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "internal.h" -#include <limits.h> -#include <string.h> - -static unsigned op_parse_uint16le(const unsigned char *_data){ -  return _data[0]|_data[1]<<8; -} - -static int op_parse_int16le(const unsigned char *_data){ -  int ret; -  ret=_data[0]|_data[1]<<8; -  return (ret^0x8000)-0x8000; -} - -static opus_uint32 op_parse_uint32le(const unsigned char *_data){ -  return _data[0]|_data[1]<<8|_data[2]<<16|_data[3]<<24; -} - -static opus_uint32 op_parse_uint32be(const unsigned char *_data){ -  return _data[3]|_data[2]<<8|_data[1]<<16|_data[0]<<24; -} - -int opus_head_parse(OpusHead *_head,const unsigned char *_data,size_t _len){ -  OpusHead head; -  if(_len<8)return OP_ENOTFORMAT; -  if(memcmp(_data,"OpusHead",8)!=0)return OP_ENOTFORMAT; -  if(_len<9)return OP_EBADHEADER; -  head.version=_data[8]; -  if(head.version>15)return OP_EVERSION; -  if(_len<19)return OP_EBADHEADER; -  head.channel_count=_data[9]; -  head.pre_skip=op_parse_uint16le(_data+10); -  head.input_sample_rate=op_parse_uint32le(_data+12); -  head.output_gain=op_parse_int16le(_data+16); -  head.mapping_family=_data[18]; -  if(head.mapping_family==0){ -    if(head.channel_count<1||head.channel_count>2)return OP_EBADHEADER; -    if(head.version<=1&&_len>19)return OP_EBADHEADER; -    head.stream_count=1; -    head.coupled_count=head.channel_count-1; -    if(_head!=NULL){ -      _head->mapping[0]=0; -      _head->mapping[1]=1; -    } -  } -  else if(head.mapping_family==1){ -    size_t size; -    int    ci; -    if(head.channel_count<1||head.channel_count>8)return OP_EBADHEADER; -    size=21+head.channel_count; -    if(_len<size||head.version<=1&&_len>size)return OP_EBADHEADER; -    head.stream_count=_data[19]; -    if(head.stream_count<1)return OP_EBADHEADER; -    head.coupled_count=_data[20]; -    if(head.coupled_count>head.stream_count)return OP_EBADHEADER; -    for(ci=0;ci<head.channel_count;ci++){ -      if(_data[21+ci]>=head.stream_count+head.coupled_count -       &&_data[21+ci]!=255){ -        return OP_EBADHEADER; -      } -    } -    if(_head!=NULL)memcpy(_head->mapping,_data+21,head.channel_count); -  } -  /*General purpose players should not attempt to play back content with -     channel mapping family 255.*/ -  else if(head.mapping_family==255)return OP_EIMPL; -  /*No other channel mapping families are currently defined.*/ -  else return OP_EBADHEADER; -  if(_head!=NULL)memcpy(_head,&head,head.mapping-(unsigned char *)&head); -  return 0; -} - -void opus_tags_init(OpusTags *_tags){ -  memset(_tags,0,sizeof(*_tags)); -} - -void opus_tags_clear(OpusTags *_tags){ -  int ci; -  for(ci=_tags->comments;ci-->0;)_ogg_free(_tags->user_comments[ci]); -  _ogg_free(_tags->user_comments); -  _ogg_free(_tags->comment_lengths); -  _ogg_free(_tags->vendor); -} - -/*Ensure there's room for up to _ncomments comments.*/ -static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){ -  char   **user_comments; -  int     *comment_lengths; -  size_t   size; -  if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT; -  size=sizeof(*_tags->comment_lengths)*(_ncomments+1); -  if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT; -  comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size); -  if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT; -  comment_lengths[_ncomments]=0; -  _tags->comment_lengths=comment_lengths; -  size=sizeof(*_tags->user_comments)*(_ncomments+1); -  if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT; -  user_comments=(char **)_ogg_realloc(_tags->user_comments,size); -  if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT; -  user_comments[_ncomments]=NULL; -  _tags->user_comments=user_comments; -  return 0; -} - -/*Duplicate a (possibly non-NUL terminated) string with a known length.*/ -static char *op_strdup_with_len(const char *_s,size_t _len){ -  size_t  size; -  char   *ret; -  size=sizeof(*ret)*(_len+1); -  if(OP_UNLIKELY(size<_len))return NULL; -  ret=(char *)_ogg_malloc(size); -  if(OP_LIKELY(ret!=NULL)){ -    ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len); -    ret[_len]='\0'; -  } -  return ret; -} - -/*The actual implementation of opus_tags_parse(). -  Unlike the public API, this function requires _tags to already be -   initialized, modifies its contents before success is guaranteed, and assumes -   the caller will clear it on error.*/ -static int opus_tags_parse_impl(OpusTags *_tags, - const unsigned char *_data,size_t _len){ -  opus_uint32 count; -  size_t      len; -  int         ncomments; -  int         ci; -  len=_len; -  if(len<8)return OP_ENOTFORMAT; -  if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT; -  if(len<16)return OP_EBADHEADER; -  _data+=8; -  len-=8; -  count=op_parse_uint32le(_data); -  _data+=4; -  len-=4; -  if(count>len)return OP_EBADHEADER; -  if(_tags!=NULL){ -    _tags->vendor=op_strdup_with_len((char *)_data,count); -    if(_tags->vendor==NULL)return OP_EFAULT; -  } -  _data+=count; -  len-=count; -  if(len<4)return OP_EBADHEADER; -  count=op_parse_uint32le(_data); -  _data+=4; -  len-=4; -  /*Check to make sure there's minimally sufficient data left in the packet.*/ -  if(count>len>>2)return OP_EBADHEADER; -  /*Check for overflow (the API limits this to an int).*/ -  if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT; -  if(_tags!=NULL){ -    int ret; -    ret=op_tags_ensure_capacity(_tags,count); -    if(ret<0)return ret; -  } -  ncomments=(int)count; -  for(ci=0;ci<ncomments;ci++){ -    /*Check to make sure there's minimally sufficient data left in the packet.*/ -    if((size_t)(ncomments-ci)>len>>2)return OP_EBADHEADER; -    count=op_parse_uint32le(_data); -    _data+=4; -    len-=4; -    if(count>len)return OP_EBADHEADER; -    /*Check for overflow (the API limits this to an int).*/ -    if(count>(opus_uint32)INT_MAX)return OP_EFAULT; -    if(_tags!=NULL){ -      _tags->user_comments[ci]=op_strdup_with_len((char *)_data,count); -      if(_tags->user_comments[ci]==NULL)return OP_EFAULT; -      _tags->comment_lengths[ci]=(int)count; -      _tags->comments=ci+1; -    } -    _data+=count; -    len-=count; -  } -  return 0; -} - -int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){ -  if(_tags!=NULL){ -    OpusTags tags; -    int      ret; -    opus_tags_init(&tags); -    ret=opus_tags_parse_impl(&tags,_data,_len); -    if(ret<0)opus_tags_clear(&tags); -    else *_tags=*&tags; -    return ret; -  } -  else return opus_tags_parse_impl(NULL,_data,_len); -} - -/*The actual implementation of opus_tags_copy(). -  Unlike the public API, this function requires _dst to already be -   initialized, modifies its contents before success is guaranteed, and assumes -   the caller will clear it on error.*/ -static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){ -  char *vendor; -  int   ncomments; -  int   ret; -  int   ci; -  vendor=_src->vendor; -  _dst->vendor=op_strdup_with_len(vendor,strlen(vendor)); -  if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT; -  ncomments=_src->comments; -  ret=op_tags_ensure_capacity(_dst,ncomments); -  if(OP_UNLIKELY(ret<0))return ret; -  for(ci=0;ci<ncomments;ci++){ -    int len; -    len=_src->comment_lengths[ci]; -    OP_ASSERT(len>=0); -    _dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len); -    if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT; -    _dst->comment_lengths[ci]=len; -    _dst->comments=ci+1; -  } -  return 0; -} - -int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){ -  OpusTags dst; -  int      ret; -  opus_tags_init(&dst); -  ret=opus_tags_copy_impl(&dst,_src); -  if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst); -  else *_dst=*&dst; -  return 0; -} - -int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){ -  char *comment; -  int   tag_len; -  int   value_len; -  int   ncomments; -  int   ret; -  ncomments=_tags->comments; -  ret=op_tags_ensure_capacity(_tags,ncomments+1); -  if(OP_UNLIKELY(ret<0))return ret; -  tag_len=strlen(_tag); -  value_len=strlen(_value); -  /*+2 for '=' and '\0'.*/ -  _tags->comment_lengths[ncomments]=0; -  _tags->user_comments[ncomments]=comment= -   (char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2)); -  if(OP_UNLIKELY(comment==NULL))return OP_EFAULT; -  _tags->comment_lengths[ncomments]=tag_len+value_len+1; -  memcpy(comment,_tag,sizeof(*comment)*tag_len); -  comment[tag_len]='='; -  memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1)); -  return 0; -} - -int opus_tags_add_comment(OpusTags *_tags,const char *_comment){ -  int comment_len; -  int ncomments; -  int ret; -  ncomments=_tags->comments; -  ret=op_tags_ensure_capacity(_tags,ncomments+1); -  if(OP_UNLIKELY(ret<0))return ret; -  comment_len=(int)strlen(_comment); -  _tags->comment_lengths[ncomments]=0; -  _tags->user_comments[ncomments]=op_strdup_with_len(_comment,comment_len); -  if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT; -  _tags->comment_lengths[ncomments]=comment_len; -  return 0; -} - -int opus_tagcompare(const char *_tag_name,const char *_comment){ -  return opus_tagncompare(_tag_name,strlen(_tag_name),_comment); -} - -int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){ -  int ret; -  OP_ASSERT(_tag_len>=0); -  ret=op_strncasecmp(_tag_name,_comment,_tag_len); -  return ret?ret:'='-_comment[_tag_len]; -} - -const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){ -  char **user_comments; -  int    tag_len; -  int    found; -  int    ncomments; -  int    ci; -  tag_len=strlen(_tag); -  ncomments=_tags->comments; -  user_comments=_tags->user_comments; -  found=0; -  for(ci=0;ci<ncomments;ci++){ -    if(!opus_tagncompare(_tag,tag_len,user_comments[ci])){ -      /*We return a pointer to the data, not a copy.*/ -      if(_count==found++)return user_comments[ci]+tag_len+1; -    } -  } -  /*Didn't find anything.*/ -  return NULL; -} - -int opus_tags_query_count(const OpusTags *_tags,const char *_tag){ -  char **user_comments; -  int    tag_len; -  int    found; -  int    ncomments; -  int    ci; -  tag_len=strlen(_tag); -  ncomments=_tags->comments; -  user_comments=_tags->user_comments; -  found=0; -  for(ci=0;ci<ncomments;ci++){ -    if(!opus_tagncompare(_tag,tag_len,user_comments[ci]))found++; -  } -  return found; -} - -int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){ -  char **comments; -  int    ncomments; -  int    ci; -  comments=_tags->user_comments; -  ncomments=_tags->comments; -  /*Look for the first valid R128_TRACK_GAIN tag and use that.*/ -  for(ci=0;ci<ncomments;ci++){ -    if(opus_tagncompare("R128_TRACK_GAIN",15,comments[ci])==0){ -      char       *p; -      opus_int32  gain_q8; -      int         negative; -      p=comments[ci]+16; -      negative=0; -      if(*p=='-'){ -        negative=-1; -        p++; -      } -      else if(*p=='+')p++; -      gain_q8=0; -      while(*p>='0'&&*p<='9'){ -        gain_q8=10*gain_q8+*p-'0'; -        if(gain_q8>32767-negative)break; -        p++; -      } -      /*This didn't look like a signed 16-bit decimal integer. -        Not a valid R128_TRACK_GAIN tag.*/ -      if(*p!='\0')continue; -      *_gain_q8=(int)(gain_q8+negative^negative); -      return 0; -    } -  } -  return OP_FALSE; -} - -static int op_is_jpeg(const unsigned char *_buf,size_t _buf_sz){ -  return _buf_sz>=11&&memcmp(_buf,"\xFF\xD8\xFF\xE0",4)==0 -   &&(_buf[4]<<8|_buf[5])>=16&&memcmp(_buf+6,"JFIF",5)==0; -} - -/*Tries to extract the width, height, bits per pixel, and palette size of a -   JPEG. -  On failure, simply leaves its outputs unmodified.*/ -static void op_extract_jpeg_params(const unsigned char *_buf,size_t _buf_sz, - opus_uint32 *_width,opus_uint32 *_height, - opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ -  if(op_is_jpeg(_buf,_buf_sz)){ -    size_t offs; -    offs=2; -    for(;;){ -      size_t segment_len; -      int    marker; -      while(offs<_buf_sz&&_buf[offs]!=0xFF)offs++; -      while(offs<_buf_sz&&_buf[offs]==0xFF)offs++; -      marker=_buf[offs]; -      offs++; -      /*If we hit EOI* (end of image), or another SOI* (start of image), -         or SOS (start of scan), then stop now.*/ -      if(offs>=_buf_sz||(marker>=0xD8&&marker<=0xDA))break; -      /*RST* (restart markers): skip (no segment length).*/ -      else if(marker>=0xD0&&marker<=0xD7)continue; -      /*Read the length of the marker segment.*/ -      if(_buf_sz-offs<2)break; -      segment_len=_buf[offs]<<8|_buf[offs+1]; -      if(segment_len<2||_buf_sz-offs<segment_len)break; -      if(marker==0xC0||(marker>0xC0&&marker<0xD0&&(marker&3)!=0)){ -        /*Found a SOFn (start of frame) marker segment:*/ -        if(segment_len>=8){ -          *_height=_buf[offs+3]<<8|_buf[offs+4]; -          *_width=_buf[offs+5]<<8|_buf[offs+6]; -          *_depth=_buf[offs+2]*_buf[offs+7]; -          *_colors=0; -          *_has_palette=0; -        } -        break; -      } -      /*Other markers: skip the whole marker segment.*/ -      offs+=segment_len; -    } -  } -} - -static int op_is_png(const unsigned char *_buf,size_t _buf_sz){ -  return _buf_sz>=8&&memcmp(_buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0; -} - -/*Tries to extract the width, height, bits per pixel, and palette size of a -   PNG. -  On failure, simply leaves its outputs unmodified.*/ -static void op_extract_png_params(const unsigned char *_buf,size_t _buf_sz, - opus_uint32 *_width,opus_uint32 *_height, - opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ -  if(op_is_png(_buf,_buf_sz)){ -    size_t offs; -    offs=8; -    while(_buf_sz-offs>=12){ -      ogg_uint32_t chunk_len; -      chunk_len=op_parse_uint32be(_buf+offs); -      if(chunk_len>_buf_sz-(offs+12))break; -      else if(chunk_len==13&&memcmp(_buf+offs+4,"IHDR",4)==0){ -        int color_type; -        *_width=op_parse_uint32be(_buf+offs+8); -        *_height=op_parse_uint32be(_buf+offs+12); -        color_type=_buf[offs+17]; -        if(color_type==3){ -          *_depth=24; -          *_has_palette=1; -        } -        else{ -          int sample_depth; -          sample_depth=_buf[offs+16]; -          if(color_type==0)*_depth=sample_depth; -          else if(color_type==2)*_depth=sample_depth*3; -          else if(color_type==4)*_depth=sample_depth*2; -          else if(color_type==6)*_depth=sample_depth*4; -          *_colors=0; -          *_has_palette=0; -          break; -        } -      } -      else if(*_has_palette>0&&memcmp(_buf+offs+4,"PLTE",4)==0){ -        *_colors=chunk_len/3; -        break; -      } -      offs+=12+chunk_len; -    } -  } -} - -static int op_is_gif(const unsigned char *_buf,size_t _buf_sz){ -  return _buf_sz>=6&&(memcmp(_buf,"GIF87a",6)==0||memcmp(_buf,"GIF89a",6)==0); -} - -/*Tries to extract the width, height, bits per pixel, and palette size of a -   GIF. -  On failure, simply leaves its outputs unmodified.*/ -static void op_extract_gif_params(const unsigned char *_buf,size_t _buf_sz, - opus_uint32 *_width,opus_uint32 *_height, - opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ -  if(op_is_gif(_buf,_buf_sz)&&_buf_sz>=14){ -    *_width=_buf[6]|_buf[7]<<8; -    *_height=_buf[8]|_buf[9]<<8; -    /*libFLAC hard-codes the depth to 24.*/ -    *_depth=24; -    *_colors=1<<((_buf[10]&7)+1); -    *_has_palette=1; -  } -} - -/*The actual implementation of opus_picture_tag_parse(). -  Unlike the public API, this function requires _pic to already be -   initialized, modifies its contents before success is guaranteed, and assumes -   the caller will clear it on error.*/ -static int opus_picture_tag_parse_impl(OpusPictureTag *_pic,const char *_tag, - unsigned char *_buf,size_t _buf_sz,size_t _base64_sz){ -  opus_int32   picture_type; -  opus_uint32  mime_type_length; -  char        *mime_type; -  opus_uint32  description_length; -  char        *description; -  opus_uint32  width; -  opus_uint32  height; -  opus_uint32  depth; -  opus_uint32  colors; -  opus_uint32  data_length; -  opus_uint32  file_width; -  opus_uint32  file_height; -  opus_uint32  file_depth; -  opus_uint32  file_colors; -  int          format; -  int          has_palette; -  int          colors_set; -  size_t       i; -  /*Decode the BASE64 data.*/ -  for(i=0;i<_base64_sz;i++){ -    opus_uint32 value; -    int         j; -    value=0; -    for(j=0;j<4;j++){ -      unsigned c; -      unsigned d; -      c=(unsigned char)_tag[4*i+j]; -      if(c=='+')d=62; -      else if(c=='/')d=63; -      else if(c>='0'&&c<='9')d=52+c-'0'; -      else if(c>='a'&&c<='z')d=26+c-'a'; -      else if(c>='A'&&c<='Z')d=c-'A'; -      else if(c=='='&&3*i+j>_buf_sz)d=0; -      else return OP_ENOTFORMAT; -      value=value<<6|d; -    } -    _buf[3*i]=(unsigned char)(value>>16); -    if(3*i+1<_buf_sz){ -      _buf[3*i+1]=(unsigned char)(value>>8); -      if(3*i+2<_buf_sz)_buf[3*i+2]=(unsigned char)value; -    } -  } -  i=0; -  picture_type=op_parse_uint32be(_buf+i); -  i+=4; -  /*Extract the MIME type.*/ -  mime_type_length=op_parse_uint32be(_buf+i); -  i+=4; -  if(mime_type_length>_buf_sz-32)return OP_ENOTFORMAT; -  mime_type=(char *)_ogg_malloc(sizeof(*_pic->mime_type)*(mime_type_length+1)); -  if(mime_type==NULL)return OP_EFAULT; -  memcpy(mime_type,_buf+i,sizeof(*mime_type)*mime_type_length); -  mime_type[mime_type_length]='\0'; -  _pic->mime_type=mime_type; -  i+=mime_type_length; -  /*Extract the description string.*/ -  description_length=op_parse_uint32be(_buf+i); -  i+=4; -  if(description_length>_buf_sz-mime_type_length-32)return OP_ENOTFORMAT; -  description= -   (char *)_ogg_malloc(sizeof(*_pic->mime_type)*(description_length+1)); -  if(description==NULL)return OP_EFAULT; -  memcpy(description,_buf+i,sizeof(*description)*description_length); -  description[description_length]='\0'; -  _pic->description=description; -  i+=description_length; -  /*Extract the remaining fields.*/ -  width=op_parse_uint32be(_buf+i); -  i+=4; -  height=op_parse_uint32be(_buf+i); -  i+=4; -  depth=op_parse_uint32be(_buf+i); -  i+=4; -  colors=op_parse_uint32be(_buf+i); -  i+=4; -  /*If one of these is set, they all must be, but colors==0 is a valid value.*/ -  colors_set=width!=0||height!=0||depth!=0||colors!=0; -  if(width==0||height==0||depth==0&&colors_set)return OP_ENOTFORMAT; -  data_length=op_parse_uint32be(_buf+i); -  i+=4; -  if(data_length>_buf_sz-i)return OP_ENOTFORMAT; -  /*Trim extraneous data so we don't copy it below.*/ -  _buf_sz=i+data_length; -  /*Attempt to determine the image format.*/ -  format=OP_PIC_FORMAT_UNKNOWN; -  if(mime_type_length==3&&strcmp(mime_type,"-->")==0){ -    format=OP_PIC_FORMAT_URL; -    /*Picture type 1 must be a 32x32 PNG.*/ -    if(picture_type==1&&(width!=0||height!=0)&&(width!=32||height!=32)){ -      return OP_ENOTFORMAT; -    } -    /*Append a terminating NUL for the convenience of our callers.*/ -    _buf[_buf_sz++]='\0'; -  } -  else{ -    if(mime_type_length==10 -     &&op_strncasecmp(mime_type,"image/jpeg",mime_type_length)==0){ -      if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG; -    } -    else if(mime_type_length==9 -     &&op_strncasecmp(mime_type,"image/png",mime_type_length)==0){ -      if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG; -    } -    else if(mime_type_length==9 -     &&op_strncasecmp(mime_type,"image/gif",mime_type_length)==0){ -      if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF; -    } -    else if(mime_type_length==0||(mime_type_length==6 -     &&op_strncasecmp(mime_type,"image/",mime_type_length)==0)){ -      if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG; -      else if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG; -      else if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF; -    } -    file_width=file_height=file_depth=file_colors=0; -    has_palette=-1; -    switch(format){ -      case OP_PIC_FORMAT_JPEG:{ -        op_extract_jpeg_params(_buf+i,data_length, -         &file_width,&file_height,&file_depth,&file_colors,&has_palette); -      }break; -      case OP_PIC_FORMAT_PNG:{ -        op_extract_png_params(_buf+i,data_length, -         &file_width,&file_height,&file_depth,&file_colors,&has_palette); -      }break; -      case OP_PIC_FORMAT_GIF:{ -        op_extract_gif_params(_buf+i,data_length, -         &file_width,&file_height,&file_depth,&file_colors,&has_palette); -      }break; -    } -    if(has_palette>=0){ -      /*If we successfully extracted these parameters from the image, override -         any declared values.*/ -      width=file_width; -      height=file_height; -      depth=file_depth; -      colors=file_colors; -    } -    /*Picture type 1 must be a 32x32 PNG.*/ -    if(picture_type==1&&(format!=OP_PIC_FORMAT_PNG||width!=32||height!=32)){ -      return OP_ENOTFORMAT; -    } -  } -  /*Adjust _buf_sz instead of using data_length to capture the terminating NUL -     for URLs.*/ -  _buf_sz-=i; -  memmove(_buf,_buf+i,sizeof(*_buf)*_buf_sz); -  _buf=(unsigned char *)_ogg_realloc(_buf,_buf_sz); -  if(_buf_sz>0&&_buf==NULL)return OP_EFAULT; -  _pic->type=picture_type; -  _pic->width=width; -  _pic->height=height; -  _pic->depth=depth; -  _pic->colors=colors; -  _pic->data_length=data_length; -  _pic->data=_buf; -  _pic->format=format; -  return 0; -} - -int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag){ -  OpusPictureTag  pic; -  unsigned char  *buf; -  size_t          base64_sz; -  size_t          buf_sz; -  size_t          tag_length; -  int             ret; -  if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23; -  /*Figure out how much BASE64-encoded data we have.*/ -  tag_length=strlen(_tag); -  if(tag_length&3)return OP_ENOTFORMAT; -  base64_sz=tag_length>>2; -  buf_sz=3*base64_sz; -  if(buf_sz<32)return OP_ENOTFORMAT; -  if(_tag[tag_length-1]=='=')buf_sz--; -  if(_tag[tag_length-2]=='=')buf_sz--; -  if(buf_sz<32)return OP_ENOTFORMAT; -  /*Allocate an extra byte to allow appending a terminating NUL to URL data.*/ -  buf=(unsigned char *)_ogg_malloc(sizeof(*buf)*(buf_sz+1)); -  if(buf==NULL)return OP_EFAULT; -  opus_picture_tag_init(&pic); -  ret=opus_picture_tag_parse_impl(&pic,_tag,buf,buf_sz,base64_sz); -  if(ret<0){ -    opus_picture_tag_clear(&pic); -    _ogg_free(buf); -  } -  else *_pic=*&pic; -  return ret; -} - -void opus_picture_tag_init(OpusPictureTag *_pic){ -  memset(_pic,0,sizeof(*_pic)); -} - -void opus_picture_tag_clear(OpusPictureTag *_pic){ -  _ogg_free(_pic->description); -  _ogg_free(_pic->mime_type); -  _ogg_free(_pic->data); -} diff --git a/src/opusfile-0.5/src/internal.c b/src/opusfile-0.5/src/internal.c deleted file mode 100644 index 96c80def..00000000 --- a/src/opusfile-0.5/src/internal.c +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012                * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ********************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "internal.h" - -#if defined(OP_ENABLE_ASSERTIONS) -void op_fatal_impl(const char *_str,const char *_file,int _line){ -  fprintf(stderr,"Fatal (internal) error in %s, line %i: %s\n", -   _file,_line,_str); -  abort(); -} -#endif - -/*A version of strncasecmp() that is guaranteed to only ignore the case of -   ASCII characters.*/ -int op_strncasecmp(const char *_a,const char *_b,int _n){ -  int i; -  for(i=0;i<_n;i++){ -    int a; -    int b; -    int d; -    a=_a[i]; -    b=_b[i]; -    if(a>='a'&&a<='z')a-='a'-'A'; -    if(b>='a'&&b<='z')b-='a'-'A'; -    d=a-b; -    if(d)return d; -  } -  return 0; -} diff --git a/src/opusfile-0.5/src/internal.h b/src/opusfile-0.5/src/internal.h deleted file mode 100644 index 08114919..00000000 --- a/src/opusfile-0.5/src/internal.h +++ /dev/null @@ -1,249 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012                * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ********************************************************************/ -#if !defined(_opusfile_internal_h) -# define _opusfile_internal_h (1) - -# if !defined(_REENTRANT) -#  define _REENTRANT -# endif -# if !defined(_GNU_SOURCE) -#  define _GNU_SOURCE -# endif -# if !defined(_LARGEFILE_SOURCE) -#  define _LARGEFILE_SOURCE -# endif -# if !defined(_LARGEFILE64_SOURCE) -#  define _LARGEFILE64_SOURCE -# endif -# if !defined(_FILE_OFFSET_BITS) -#  define _FILE_OFFSET_BITS 64 -# endif - -# include <stdlib.h> -# include <opusfile.h> - -typedef struct OggOpusLink OggOpusLink; - -# if defined(OP_FIXED_POINT) - -typedef opus_int16 op_sample; - -# else - -typedef float      op_sample; - -/*We're using this define to test for libopus 1.1 or later until libopus -   provides a better mechanism.*/ -#  if defined(OPUS_GET_EXPERT_FRAME_DURATION_REQUEST) -/*Enable soft clipping prevention in 16-bit decodes.*/ -#   define OP_SOFT_CLIP (1) -#  endif - -# endif - -# if OP_GNUC_PREREQ(4,2) -/*Disable excessive warnings about the order of operations.*/ -#  pragma GCC diagnostic ignored "-Wparentheses" -# elif defined(_MSC_VER) -/*Disable excessive warnings about the order of operations.*/ -#  pragma warning(disable:4554) -/*Disable warnings about "deprecated" POSIX functions.*/ -#  pragma warning(disable:4996) -# endif - -# if OP_GNUC_PREREQ(3,0) -/*Another alternative is -    (__builtin_constant_p(_x)?!!(_x):__builtin_expect(!!(_x),1)) -   but that evaluates _x multiple times, which may be bad.*/ -#  define OP_LIKELY(_x) (__builtin_expect(!!(_x),1)) -#  define OP_UNLIKELY(_x) (__builtin_expect(!!(_x),0)) -# else -#  define OP_LIKELY(_x)   (!!(_x)) -#  define OP_UNLIKELY(_x) (!!(_x)) -# endif - -# if defined(OP_ENABLE_ASSERTIONS) -#  if OP_GNUC_PREREQ(2,5)||__SUNPRO_C>=0x590 -__attribute__((noreturn)) -#  endif -void op_fatal_impl(const char *_str,const char *_file,int _line); - -#  define OP_FATAL(_str) (op_fatal_impl(_str,__FILE__,__LINE__)) - -#  define OP_ASSERT(_cond) \ -  do{ \ -    if(OP_UNLIKELY(!(_cond)))OP_FATAL("assertion failed: " #_cond); \ -  } \ -  while(0) -#  define OP_ALWAYS_TRUE(_cond) OP_ASSERT(_cond) - -# else -#  define OP_FATAL(_str) abort() -#  define OP_ASSERT(_cond) -#  define OP_ALWAYS_TRUE(_cond) ((void)(_cond)) -# endif - -# define OP_INT64_MAX (2*(((ogg_int64_t)1<<62)-1)|1) -# define OP_INT64_MIN (-OP_INT64_MAX-1) -# define OP_INT32_MAX (2*(((ogg_int32_t)1<<30)-1)|1) -# define OP_INT32_MIN (-OP_INT32_MAX-1) - -# define OP_MIN(_a,_b)        ((_a)<(_b)?(_a):(_b)) -# define OP_MAX(_a,_b)        ((_a)>(_b)?(_a):(_b)) -# define OP_CLAMP(_lo,_x,_hi) (OP_MAX(_lo,OP_MIN(_x,_hi))) - -/*Advance a file offset by the given amount, clamping against OP_INT64_MAX. -  This is used to advance a known offset by things like OP_CHUNK_SIZE or -   OP_PAGE_SIZE_MAX, while making sure to avoid signed overflow. -  It assumes that both _offset and _amount are non-negative.*/ -#define OP_ADV_OFFSET(_offset,_amount) \ - (OP_MIN(_offset,OP_INT64_MAX-(_amount))+(_amount)) - -/*The maximum channel count for any mapping we'll actually decode.*/ -# define OP_NCHANNELS_MAX (8) - -/*Initial state.*/ -# define  OP_NOTOPEN   (0) -/*We've found the first Opus stream in the first link.*/ -# define  OP_PARTOPEN  (1) -# define  OP_OPENED    (2) -/*We've found the first Opus stream in the current link.*/ -# define  OP_STREAMSET (3) -/*We've initialized the decoder for the chosen Opus stream in the current -   link.*/ -# define  OP_INITSET   (4) - -/*Information cached for a single link in a chained Ogg Opus file. -  We choose the first Opus stream encountered in each link to play back (and -   require at least one).*/ -struct OggOpusLink{ -  /*The byte offset of the first header page in this link.*/ -  opus_int64   offset; -  /*The byte offset of the first data page from the chosen Opus stream in this -     link (after the headers).*/ -  opus_int64   data_offset; -  /*The byte offset of the last page from the chosen Opus stream in this link. -    This is used when seeking to ensure we find a page before the last one, so -     that end-trimming calculations work properly. -    This is only valid for seekable sources.*/ -  opus_int64   end_offset; -  /*The granule position of the last sample. -    This is only valid for seekable sources.*/ -  ogg_int64_t  pcm_end; -  /*The granule position before the first sample.*/ -  ogg_int64_t  pcm_start; -  /*The serial number.*/ -  ogg_uint32_t serialno; -  /*The contents of the info header.*/ -  OpusHead     head; -  /*The contents of the comment header.*/ -  OpusTags     tags; -}; - -struct OggOpusFile{ -  /*The callbacks used to access the data source.*/ -  OpusFileCallbacks  callbacks; -  /*A FILE *, memory bufer, etc.*/ -  void              *source; -  /*Whether or not we can seek with this data source.*/ -  int                seekable; -  /*The number of links in this chained Ogg Opus file.*/ -  int                nlinks; -  /*The cached information from each link in a chained Ogg Opus file. -    If source isn't seekable (e.g., it's a pipe), only the current link -     appears.*/ -  OggOpusLink       *links; -  /*The number of serial numbers from a single link.*/ -  int                nserialnos; -  /*The capacity of the list of serial numbers from a single link.*/ -  int                cserialnos; -  /*Storage for the list of serial numbers from a single link.*/ -  ogg_uint32_t      *serialnos; -  /*This is the current offset of the data processed by the ogg_sync_state. -    After a seek, this should be set to the target offset so that we can track -     the byte offsets of subsequent pages. -    After a call to op_get_next_page(), this will point to the first byte after -     that page.*/ -  opus_int64         offset; -  /*The total size of this data source, or -1 if it's unseekable.*/ -  opus_int64         end; -  /*Used to locate pages in the data source.*/ -  ogg_sync_state     oy; -  /*One of OP_NOTOPEN, OP_PARTOPEN, OP_OPENED, OP_STREAMSET, OP_INITSET.*/ -  int                ready_state; -  /*The current link being played back.*/ -  int                cur_link; -  /*The number of decoded samples to discard from the start of decoding.*/ -  opus_int32         cur_discard_count; -  /*The granule position of the previous packet (current packet start time).*/ -  ogg_int64_t        prev_packet_gp; -  /*The number of bytes read since the last bitrate query, including framing.*/ -  opus_int64         bytes_tracked; -  /*The number of samples decoded since the last bitrate query.*/ -  ogg_int64_t        samples_tracked; -  /*Takes physical pages and welds them into a logical stream of packets.*/ -  ogg_stream_state   os; -  /*Re-timestamped packets from a single page. -    Buffering these relies on the undocumented libogg behavior that ogg_packet -     pointers remain valid until the next page is submitted to the -     ogg_stream_state they came from.*/ -  ogg_packet         op[255]; -  /*The index of the next packet to return.*/ -  int                op_pos; -  /*The total number of packets available.*/ -  int                op_count; -  /*Central working state for the packet-to-PCM decoder.*/ -  OpusMSDecoder     *od; -  /*The application-provided packet decode callback.*/ -  op_decode_cb_func  decode_cb; -  /*The application-provided packet decode callback context.*/ -  void              *decode_cb_ctx; -  /*The stream count used to initialize the decoder.*/ -  int                od_stream_count; -  /*The coupled stream count used to initialize the decoder.*/ -  int                od_coupled_count; -  /*The channel count used to initialize the decoder.*/ -  int                od_channel_count; -  /*The channel mapping used to initialize the decoder.*/ -  unsigned char      od_mapping[OP_NCHANNELS_MAX]; -  /*The buffered data for one decoded packet.*/ -  op_sample         *od_buffer; -  /*The current position in the decoded buffer.*/ -  int                od_buffer_pos; -  /*The number of valid samples in the decoded buffer.*/ -  int                od_buffer_size; -  /*The type of gain offset to apply. -    One of OP_HEADER_GAIN, OP_TRACK_GAIN, or OP_ABSOLUTE_GAIN.*/ -  int                gain_type; -  /*The offset to apply to the gain.*/ -  opus_int32         gain_offset_q8; -  /*Internal state for soft clipping and dithering float->short output.*/ -#if !defined(OP_FIXED_POINT) -# if defined(OP_SOFT_CLIP) -  float              clip_state[OP_NCHANNELS_MAX]; -# endif -  float              dither_a[OP_NCHANNELS_MAX*4]; -  float              dither_b[OP_NCHANNELS_MAX*4]; -  opus_uint32        dither_seed; -  int                dither_mute; -  int                dither_disabled; -  /*The number of channels represented by the internal state. -    This gets set to 0 whenever anything that would prevent state propagation -     occurs (switching between the float/short APIs, or between the -     stereo/multistream APIs).*/ -  int                state_channel_count; -#endif -}; - -int op_strncasecmp(const char *_a,const char *_b,int _n); - -#endif diff --git a/src/opusfile-0.5/src/opusfile.c b/src/opusfile-0.5/src/opusfile.c deleted file mode 100644 index 73b95de6..00000000 --- a/src/opusfile-0.5/src/opusfile.c +++ /dev/null @@ -1,3163 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012           * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ******************************************************************** - - function: stdio-based convenience library for opening/seeking/decoding - last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $ - - ********************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "internal.h" -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <limits.h> -#include <string.h> -#include <math.h> - -#include "opusfile.h" - -/*This implementation is largely based off of libvorbisfile. -  All of the Ogg bits work roughly the same, though I have made some -   "improvements" that have not been folded back there, yet.*/ - -/*A 'chained bitstream' is an Ogg Opus bitstream that contains more than one -   logical bitstream arranged end to end (the only form of Ogg multiplexing -   supported by this library. -  Grouping (parallel multiplexing) is not supported, except to the extent that -   if there are multiple logical Ogg streams in a single link of the chain, we -   will ignore all but the first Opus stream we find.*/ - -/*An Ogg Opus file can be played beginning to end (streamed) without worrying -   ahead of time about chaining (see opusdec from the opus-tools package). -  If we have the whole file, however, and want random access -   (seeking/scrubbing) or desire to know the total length/time of a file, we -   need to account for the possibility of chaining.*/ - -/*We can handle things a number of ways. -  We can determine the entire bitstream structure right off the bat, or find -   pieces on demand. -  This library determines and caches structure for the entire bitstream, but -   builds a virtual decoder on the fly when moving between links in the chain.*/ - -/*There are also different ways to implement seeking. -  Enough information exists in an Ogg bitstream to seek to sample-granularity -   positions in the output. -  Or, one can seek by picking some portion of the stream roughly in the desired -   area if we only want coarse navigation through the stream. -  We implement and expose both strategies.*/ - -/*The maximum number of bytes in a page (including the page headers).*/ -#define OP_PAGE_SIZE_MAX  (65307) -/*The default amount to seek backwards per step when trying to find the -   previous page. -  This must be at least as large as the maximum size of a page.*/ -#define OP_CHUNK_SIZE     (65536) -/*The maximum amount to seek backwards per step when trying to find the -   previous page.*/ -#define OP_CHUNK_SIZE_MAX (1024*(opus_int32)1024) -/*A smaller read size is needed for low-rate streaming.*/ -#define OP_READ_SIZE      (2048) - -int op_test(OpusHead *_head, - const unsigned char *_initial_data,size_t _initial_bytes){ -  ogg_sync_state  oy; -  char           *data; -  int             err; -  /*The first page of a normal Opus file will be at most 57 bytes (27 Ogg -     page header bytes + 1 lacing value + 21 Opus header bytes + 8 channel -     mapping bytes). -    It will be at least 47 bytes (27 Ogg page header bytes + 1 lacing value + -     19 Opus header bytes using channel mapping family 0). -    If we don't have at least that much data, give up now.*/ -  if(_initial_bytes<47)return OP_FALSE; -  /*Only proceed if we start with the magic OggS string. -    This is to prevent us spending a lot of time allocating memory and looking -     for Ogg pages in non-Ogg files.*/ -  if(memcmp(_initial_data,"OggS",4)!=0)return OP_ENOTFORMAT; -  ogg_sync_init(&oy); -  data=ogg_sync_buffer(&oy,_initial_bytes); -  if(data!=NULL){ -    ogg_stream_state os; -    ogg_page         og; -    int              ret; -    memcpy(data,_initial_data,_initial_bytes); -    ogg_sync_wrote(&oy,_initial_bytes); -    ogg_stream_init(&os,-1); -    err=OP_FALSE; -    do{ -      ogg_packet op; -      ret=ogg_sync_pageout(&oy,&og); -      /*Ignore holes.*/ -      if(ret<0)continue; -      /*Stop if we run out of data.*/ -      if(!ret)break; -      ogg_stream_reset_serialno(&os,ogg_page_serialno(&og)); -      ogg_stream_pagein(&os,&og); -      /*Only process the first packet on this page (if it's a BOS packet, -         it's required to be the only one).*/ -      if(ogg_stream_packetout(&os,&op)==1){ -        if(op.b_o_s){ -          ret=opus_head_parse(_head,op.packet,op.bytes); -          /*If this didn't look like Opus, keep going.*/ -          if(ret==OP_ENOTFORMAT)continue; -          /*Otherwise we're done, one way or another.*/ -          err=ret; -        } -        /*We finished parsing the headers. -          There is no Opus to be found.*/ -        else err=OP_ENOTFORMAT; -      } -    } -    while(err==OP_FALSE); -    ogg_stream_clear(&os); -  } -  else err=OP_EFAULT; -  ogg_sync_clear(&oy); -  return err; -} - -/*Many, many internal helpers. -  The intention is not to be confusing. -  Rampant duplication and monolithic function implementation (though we do have -   some large, omnibus functions still) would be harder to understand anyway. -  The high level functions are last. -  Begin grokking near the end of the file if you prefer to read things -   top-down.*/ - -/*The read/seek functions track absolute position within the stream.*/ - -/*Read a little more data from the file/pipe into the ogg_sync framer. -  _nbytes: The maximum number of bytes to read. -  Return: A positive number of bytes read on success, 0 on end-of-file, or a -           negative value on failure.*/ -static int op_get_data(OggOpusFile *_of,int _nbytes){ -  unsigned char *buffer; -  int            nbytes; -  OP_ASSERT(_nbytes>0); -  buffer=(unsigned char *)ogg_sync_buffer(&_of->oy,_nbytes); -  nbytes=(int)(*_of->callbacks.read)(_of->source,buffer,_nbytes); -  OP_ASSERT(nbytes<=_nbytes); -  if(OP_LIKELY(nbytes>0))ogg_sync_wrote(&_of->oy,nbytes); -  return nbytes; -} - -/*Save a tiny smidge of verbosity to make the code more readable.*/ -static int op_seek_helper(OggOpusFile *_of,opus_int64 _offset){ -  if(_offset==_of->offset)return 0; -  if(_of->callbacks.seek==NULL|| -   (*_of->callbacks.seek)(_of->source,_offset,SEEK_SET)){ -    return OP_EREAD; -  } -  _of->offset=_offset; -  ogg_sync_reset(&_of->oy); -  return 0; -} - -/*Get the current position indicator of the underlying source. -  This should be the same as the value reported by tell().*/ -static opus_int64 op_position(const OggOpusFile *_of){ -  /*The current position indicator is _not_ simply offset. -    We may also have unprocessed, buffered data in the sync state.*/ -  return _of->offset+_of->oy.fill-_of->oy.returned; -} - -/*From the head of the stream, get the next page. -  _boundary specifies if the function is allowed to fetch more data from the -   stream (and how much) or only use internally buffered data. -  _boundary: -1: Unbounded search. -              0: Read no additional data. -                 Use only cached data. -              n: Search for the start of a new page up to file position n. -  Return: n>=0:       Found a page at absolute offset n. -          OP_FALSE:   Hit the _boundary limit. -          OP_EREAD:   An underlying read operation failed. -          OP_BADLINK: We hit end-of-file before reaching _boundary.*/ -static opus_int64 op_get_next_page(OggOpusFile *_of,ogg_page *_og, - opus_int64 _boundary){ -  while(_boundary<=0||_of->offset<_boundary){ -    int more; -    more=ogg_sync_pageseek(&_of->oy,_og); -    /*Skipped (-more) bytes.*/ -    if(OP_UNLIKELY(more<0))_of->offset-=more; -    else if(more==0){ -      int read_nbytes; -      int ret; -      /*Send more paramedics.*/ -      if(!_boundary)return OP_FALSE; -      if(_boundary<0)read_nbytes=OP_READ_SIZE; -      else{ -        opus_int64 position; -        position=op_position(_of); -        if(position>=_boundary)return OP_FALSE; -        read_nbytes=(int)OP_MIN(_boundary-position,OP_READ_SIZE); -      } -      ret=op_get_data(_of,read_nbytes); -      if(OP_UNLIKELY(ret<0))return OP_EREAD; -      if(OP_UNLIKELY(ret==0)){ -        /*Only fail cleanly on EOF if we didn't have a known boundary. -          Otherwise, we should have been able to reach that boundary, and this -           is a fatal error.*/ -        return OP_UNLIKELY(_boundary<0)?OP_FALSE:OP_EBADLINK; -      } -    } -    else{ -      /*Got a page. -        Return the page start offset and advance the internal offset past the -         page end.*/ -      opus_int64 page_offset; -      page_offset=_of->offset; -      _of->offset+=more; -      OP_ASSERT(page_offset>=0); -      return page_offset; -    } -  } -  return OP_FALSE; -} - -static int op_add_serialno(const ogg_page *_og, - ogg_uint32_t **_serialnos,int *_nserialnos,int *_cserialnos){ -  ogg_uint32_t *serialnos; -  int           nserialnos; -  int           cserialnos; -  ogg_uint32_t s; -  s=ogg_page_serialno(_og); -  serialnos=*_serialnos; -  nserialnos=*_nserialnos; -  cserialnos=*_cserialnos; -  if(OP_UNLIKELY(nserialnos>=cserialnos)){ -    if(OP_UNLIKELY(cserialnos>INT_MAX-1>>1))return OP_EFAULT; -    cserialnos=2*cserialnos+1; -    OP_ASSERT(nserialnos<cserialnos); -    serialnos=(ogg_uint32_t *)_ogg_realloc(serialnos, -     sizeof(*serialnos)*cserialnos); -    if(OP_UNLIKELY(serialnos==NULL))return OP_EFAULT; -  } -  serialnos[nserialnos++]=s; -  *_serialnos=serialnos; -  *_nserialnos=nserialnos; -  *_cserialnos=cserialnos; -  return 0; -} - -/*Returns nonzero if found.*/ -static int op_lookup_serialno(ogg_uint32_t _s, - const ogg_uint32_t *_serialnos,int _nserialnos){ -  int i; -  for(i=0;i<_nserialnos&&_serialnos[i]!=_s;i++); -  return i<_nserialnos; -} - -static int op_lookup_page_serialno(const ogg_page *_og, - const ogg_uint32_t *_serialnos,int _nserialnos){ -  return op_lookup_serialno(ogg_page_serialno(_og),_serialnos,_nserialnos); -} - -typedef struct OpusSeekRecord OpusSeekRecord; - -/*We use this to remember the pages we found while enumerating the links of a -   chained stream. -  We keep track of the starting and ending offsets, as well as the point we -   started searching from, so we know where to bisect. -  We also keep the serial number, so we can tell if the page belonged to the -   current link or not, as well as the granule position, to aid in estimating -   the start of the link.*/ -struct OpusSeekRecord{ -  /*The earliest byte we know of such that reading forward from it causes -     capture to be regained at this page.*/ -  opus_int64   search_start; -  /*The offset of this page.*/ -  opus_int64   offset; -  /*The size of this page.*/ -  opus_int32   size; -  /*The serial number of this page.*/ -  ogg_uint32_t serialno; -  /*The granule position of this page.*/ -  ogg_int64_t  gp; -}; - -/*Find the last page beginning before _offset with a valid granule position. -  There is no '_boundary' parameter as it will always have to read more data. -  This is much dirtier than the above, as Ogg doesn't have any backward search -   linkage. -  This search prefers pages of the specified serial number. -  If a page of the specified serial number is spotted during the -   seek-back-and-read-forward, it will return the info of last page of the -   matching serial number, instead of the very last page, unless the very last -   page belongs to a different link than preferred serial number. -  If no page of the specified serial number is seen, it will return the info of -   the last page. -  [out] _sr:   Returns information about the page that was found on success. -  _offset:     The _offset before which to find a page. -               Any page returned will consist of data entirely before _offset. -  _serialno:   The preferred serial number. -               If a page with this serial number is found, it will be returned -                even if another page in the same link is found closer to -                _offset. -               This is purely opportunistic: there is no guarantee such a page -                will be found if it exists. -  _serialnos:  The list of serial numbers in the link that contains the -                preferred serial number. -  _nserialnos: The number of serial numbers in the current link. -  Return: 0 on success, or a negative value on failure. -          OP_EREAD:    Failed to read more data (error or EOF). -          OP_EBADLINK: We couldn't find a page even after seeking back to the -                        start of the stream.*/ -static int op_get_prev_page_serial(OggOpusFile *_of,OpusSeekRecord *_sr, - opus_int64 _offset,ogg_uint32_t _serialno, - const ogg_uint32_t *_serialnos,int _nserialnos){ -  OpusSeekRecord preferred_sr={0}; -  ogg_page       og; -  opus_int64     begin; -  opus_int64     end; -  opus_int64     original_end; -  opus_int32     chunk_size; -  int            preferred_found; -  original_end=end=begin=_offset; -  preferred_found=0; -  _offset=-1; -  chunk_size=OP_CHUNK_SIZE; -  do{ -    opus_int64 search_start; -    int        ret; -    OP_ASSERT(chunk_size>=OP_PAGE_SIZE_MAX); -    begin=OP_MAX(begin-chunk_size,0); -    ret=op_seek_helper(_of,begin); -    if(OP_UNLIKELY(ret<0))return ret; -    search_start=begin; -    while(_of->offset<end){ -      opus_int64   llret; -      ogg_uint32_t serialno; -      llret=op_get_next_page(_of,&og,end); -      if(OP_UNLIKELY(llret<OP_FALSE))return (int)llret; -      else if(llret==OP_FALSE)break; -      serialno=ogg_page_serialno(&og); -      /*Save the information for this page. -        We're not interested in the page itself... just the serial number, byte -         offset, page size, and granule position.*/ -      _sr->search_start=search_start; -      _sr->offset=_offset=llret; -      _sr->serialno=serialno; -      OP_ASSERT(_of->offset-_offset>=0); -      OP_ASSERT(_of->offset-_offset<=OP_PAGE_SIZE_MAX); -      _sr->size=(opus_int32)(_of->offset-_offset); -      _sr->gp=ogg_page_granulepos(&og); -      /*If this page is from the stream we're looking for, remember it.*/ -      if(serialno==_serialno){ -        preferred_found=1; -        *&preferred_sr=*_sr; -      } -      if(!op_lookup_serialno(serialno,_serialnos,_nserialnos)){ -        /*We fell off the end of the link, which means we seeked back too far -           and shouldn't have been looking in that link to begin with. -          If we found the preferred serial number, forget that we saw it.*/ -        preferred_found=0; -      } -      search_start=llret+1; -    } -    /*We started from the beginning of the stream and found nothing. -      This should be impossible unless the contents of the source changed out -       from under us after we read from it.*/ -    if(OP_UNLIKELY(!begin)&&OP_UNLIKELY(_offset<0))return OP_EBADLINK; -    /*Bump up the chunk size. -      This is mildly helpful when seeks are very expensive (http).*/ -    chunk_size=OP_MIN(2*chunk_size,OP_CHUNK_SIZE_MAX); -    /*Avoid quadratic complexity if we hit an invalid patch of the file.*/ -    end=OP_MIN(begin+OP_PAGE_SIZE_MAX-1,original_end); -  } -  while(_offset<0); -  if(preferred_found)*_sr=*&preferred_sr; -  return 0; -} - -/*Find the last page beginning before _offset with the given serial number and -   a valid granule position. -  Unlike the above search, this continues until it finds such a page, but does -   not stray outside the current link. -  We could implement it (inefficiently) by calling op_get_prev_page_serial() -   repeatedly until it returned a page that had both our preferred serial -   number and a valid granule position, but doing it with a separate function -   allows us to avoid repeatedly re-scanning valid pages from other streams as -   we seek-back-and-read-forward. -  [out] _gp:   Returns the granule position of the page that was found on -                success. -  _offset:     The _offset before which to find a page. -               Any page returned will consist of data entirely before _offset. -  _serialno:   The target serial number. -  _serialnos:  The list of serial numbers in the link that contains the -                preferred serial number. -  _nserialnos: The number of serial numbers in the current link. -  Return: The offset of the page on success, or a negative value on failure. -          OP_EREAD:    Failed to read more data (error or EOF). -          OP_EBADLINK: We couldn't find a page even after seeking back past the -                        beginning of the link.*/ -static opus_int64 op_get_last_page(OggOpusFile *_of,ogg_int64_t *_gp, - opus_int64 _offset,ogg_uint32_t _serialno, - const ogg_uint32_t *_serialnos,int _nserialnos){ -  ogg_page    og; -  ogg_int64_t gp; -  opus_int64  begin; -  opus_int64  end; -  opus_int64  original_end; -  opus_int32  chunk_size; -  /*The target serial number must belong to the current link.*/ -  OP_ASSERT(op_lookup_serialno(_serialno,_serialnos,_nserialnos)); -  original_end=end=begin=_offset; -  _offset=-1; -  /*We shouldn't have to initialize gp, but gcc is too dumb to figure out that -     ret>=0 implies we entered the if(page_gp!=-1) block at least once.*/ -  gp=-1; -  chunk_size=OP_CHUNK_SIZE; -  do{ -    int left_link; -    int ret; -    OP_ASSERT(chunk_size>=OP_PAGE_SIZE_MAX); -    begin=OP_MAX(begin-chunk_size,0); -    ret=op_seek_helper(_of,begin); -    if(OP_UNLIKELY(ret<0))return ret; -    left_link=0; -    while(_of->offset<end){ -      opus_int64   llret; -      ogg_uint32_t serialno; -      llret=op_get_next_page(_of,&og,end); -      if(OP_UNLIKELY(llret<OP_FALSE))return llret; -      else if(llret==OP_FALSE)break; -      serialno=ogg_page_serialno(&og); -      if(serialno==_serialno){ -        ogg_int64_t page_gp; -        /*The page is from the right stream...*/ -        page_gp=ogg_page_granulepos(&og); -        if(page_gp!=-1){ -          /*And has a valid granule position. -            Let's remember it.*/ -          _offset=llret; -          gp=page_gp; -        } -      } -      else if(OP_UNLIKELY(!op_lookup_serialno(serialno, -       _serialnos,_nserialnos))){ -        /*We fell off the start of the link, which means we don't need to keep -           seeking any farther back.*/ -        left_link=1; -      } -    } -    /*We started from at or before the beginning of the link and found nothing. -      This should be impossible unless the contents of the source changed out -       from under us after we read from it.*/ -    if((OP_UNLIKELY(left_link)||OP_UNLIKELY(!begin))&&OP_UNLIKELY(_offset<0)){ -      return OP_EBADLINK; -    } -    /*Bump up the chunk size. -      This is mildly helpful when seeks are very expensive (http).*/ -    chunk_size=OP_MIN(2*chunk_size,OP_CHUNK_SIZE_MAX); -    /*Avoid quadratic complexity if we hit an invalid patch of the file.*/ -    end=OP_MIN(begin+OP_PAGE_SIZE_MAX-1,original_end); -  } -  while(_offset<0); -  *_gp=gp; -  return _offset; -} - -/*Uses the local ogg_stream storage in _of. -  This is important for non-streaming input sources.*/ -static int op_fetch_headers_impl(OggOpusFile *_of,OpusHead *_head, - OpusTags *_tags,ogg_uint32_t **_serialnos,int *_nserialnos, - int *_cserialnos,ogg_page *_og){ -  ogg_packet op; -  int        ret; -  if(_serialnos!=NULL)*_nserialnos=0; -  /*Extract the serialnos of all BOS pages plus the first set of Opus headers -     we see in the link.*/ -  while(ogg_page_bos(_og)){ -    if(_serialnos!=NULL){ -      if(OP_UNLIKELY(op_lookup_page_serialno(_og,*_serialnos,*_nserialnos))){ -        /*A dupe serialnumber in an initial header packet set==invalid stream.*/ -        return OP_EBADHEADER; -      } -      ret=op_add_serialno(_og,_serialnos,_nserialnos,_cserialnos); -      if(OP_UNLIKELY(ret<0))return ret; -    } -    if(_of->ready_state<OP_STREAMSET){ -      /*We don't have an Opus stream in this link yet, so begin prospective -         stream setup. -        We need a stream to get packets.*/ -      ogg_stream_reset_serialno(&_of->os,ogg_page_serialno(_og)); -      ogg_stream_pagein(&_of->os,_og); -      if(OP_LIKELY(ogg_stream_packetout(&_of->os,&op)>0)){ -        ret=opus_head_parse(_head,op.packet,op.bytes); -        /*If it's just a stream type we don't recognize, ignore it.*/ -        if(ret==OP_ENOTFORMAT)continue; -        /*Everything else is fatal.*/ -        if(OP_UNLIKELY(ret<0))return ret; -        /*Found a valid Opus header. -          Continue setup.*/ -        _of->ready_state=OP_STREAMSET; -      } -    } -    /*Get the next page. -      No need to clamp the boundary offset against _of->end, as all errors -       become OP_ENOTFORMAT.*/ -    if(OP_UNLIKELY(op_get_next_page(_of,_og, -     OP_ADV_OFFSET(_of->offset,OP_CHUNK_SIZE))<0)){ -      return OP_ENOTFORMAT; -    } -    /*If this page also belongs to our Opus stream, submit it and break.*/ -    if(_of->ready_state==OP_STREAMSET -     &&_of->os.serialno==ogg_page_serialno(_og)){ -      ogg_stream_pagein(&_of->os,_og); -      break; -    } -  } -  if(OP_UNLIKELY(_of->ready_state!=OP_STREAMSET))return OP_ENOTFORMAT; -  /*Loop getting packets.*/ -  for(;;){ -    switch(ogg_stream_packetout(&_of->os,&op)){ -      case 0:{ -        /*Loop getting pages.*/ -        for(;;){ -          /*No need to clamp the boundary offset against _of->end, as all -             errors become OP_EBADHEADER.*/ -          if(OP_UNLIKELY(op_get_next_page(_of,_og, -           OP_ADV_OFFSET(_of->offset,OP_CHUNK_SIZE))<0)){ -            return OP_EBADHEADER; -          } -          /*If this page belongs to the correct stream, go parse it.*/ -          if(_of->os.serialno==ogg_page_serialno(_og)){ -            ogg_stream_pagein(&_of->os,_og); -            break; -          } -          /*If the link ends before we see the Opus comment header, abort.*/ -          if(OP_UNLIKELY(ogg_page_bos(_og)))return OP_EBADHEADER; -          /*Otherwise, keep looking.*/ -        } -      }break; -      /*We shouldn't get a hole in the headers!*/ -      case -1:return OP_EBADHEADER; -      default:{ -        /*Got a packet. -          It should be the comment header.*/ -        ret=opus_tags_parse(_tags,op.packet,op.bytes); -        if(OP_UNLIKELY(ret<0))return ret; -        /*Make sure the page terminated at the end of the comment header. -          If there is another packet on the page, or part of a packet, then -           reject the stream. -          Otherwise seekable sources won't be able to seek back to the start -           properly.*/ -        ret=ogg_stream_packetout(&_of->os,&op); -        if(OP_UNLIKELY(ret!=0) -         ||OP_UNLIKELY(_og->header[_og->header_len-1]==255)){ -          /*If we fail, the caller assumes our tags are uninitialized.*/ -          opus_tags_clear(_tags); -          return OP_EBADHEADER; -        } -        return 0; -      } -    } -  } -} - -static int op_fetch_headers(OggOpusFile *_of,OpusHead *_head, - OpusTags *_tags,ogg_uint32_t **_serialnos,int *_nserialnos, - int *_cserialnos,ogg_page *_og){ -  ogg_page og; -  int      ret; -  if(!_og){ -    /*No need to clamp the boundary offset against _of->end, as all errors -       become OP_ENOTFORMAT.*/ -    if(OP_UNLIKELY(op_get_next_page(_of,&og, -     OP_ADV_OFFSET(_of->offset,OP_CHUNK_SIZE))<0)){ -      return OP_ENOTFORMAT; -    } -    _og=&og; -  } -  _of->ready_state=OP_OPENED; -  ret=op_fetch_headers_impl(_of,_head,_tags,_serialnos,_nserialnos, -   _cserialnos,_og); -  /*Revert back from OP_STREAMSET to OP_OPENED on failure, to prevent -     double-free of the tags in an unseekable stream.*/ -  if(OP_UNLIKELY(ret<0))_of->ready_state=OP_OPENED; -  return ret; -} - -/*Granule position manipulation routines. -  A granule position is defined to be an unsigned 64-bit integer, with the -   special value -1 in two's complement indicating an unset or invalid granule -   position. -  We are not guaranteed to have an unsigned 64-bit type, so we construct the -   following routines that -   a) Properly order negative numbers as larger than positive numbers, and -   b) Check for underflow or overflow past the special -1 value. -  This lets us operate on the full, valid range of granule positions in a -   consistent and safe manner. -  This full range is organized into distinct regions: -   [ -1 (invalid) ][ 0 ... OP_INT64_MAX ][ OP_INT64_MIN ... -2 ][-1 (invalid) ] - -  No one should actually use granule positions so large that they're negative, -   even if they are technically valid, as very little software handles them -   correctly (including most of Xiph.Org's). -  This library also refuses to support durations so large they won't fit in a -   signed 64-bit integer (to avoid exposing this mess to the application, and -   to simplify a good deal of internal arithmetic), so the only way to use them -   successfully is if pcm_start is very large. -  This means there isn't anything you can do with negative granule positions -   that you couldn't have done with purely non-negative ones. -  The main purpose of these routines is to allow us to think very explicitly -   about the possible failure cases of all granule position manipulations.*/ - -/*Safely adds a small signed integer to a valid (not -1) granule position. -  The result can use the full 64-bit range of values (both positive and -   negative), but will fail on overflow (wrapping past -1; wrapping past -   OP_INT64_MAX is explicitly okay). -  [out] _dst_gp: The resulting granule position. -                 Only modified on success. -  _src_gp:       The granule position to add to. -                 This must not be -1. -  _delta:        The amount to add. -                 This is allowed to be up to 32 bits to support the maximum -                  duration of a single Ogg page (255 packets * 120 ms per -                  packet == 1,468,800 samples at 48 kHz). -  Return: 0 on success, or OP_EINVAL if the result would wrap around past -1.*/ -static int op_granpos_add(ogg_int64_t *_dst_gp,ogg_int64_t _src_gp, - opus_int32 _delta){ -  /*The code below handles this case correctly, but there's no reason we -     should ever be called with these values, so make sure we aren't.*/ -  OP_ASSERT(_src_gp!=-1); -  if(_delta>0){ -    /*Adding this amount to the granule position would overflow its 64-bit -       range.*/ -    if(OP_UNLIKELY(_src_gp<0)&&OP_UNLIKELY(_src_gp>=-1-_delta))return OP_EINVAL; -    if(OP_UNLIKELY(_src_gp>OP_INT64_MAX-_delta)){ -      /*Adding this amount to the granule position would overflow the positive -         half of its 64-bit range. -        Since signed overflow is undefined in C, do it in a way the compiler -         isn't allowed to screw up.*/ -      _delta-=(opus_int32)(OP_INT64_MAX-_src_gp)+1; -      _src_gp=OP_INT64_MIN; -    } -  } -  else if(_delta<0){ -    /*Subtracting this amount from the granule position would underflow its -       64-bit range.*/ -    if(_src_gp>=0&&OP_UNLIKELY(_src_gp<-_delta))return OP_EINVAL; -    if(OP_UNLIKELY(_src_gp<OP_INT64_MIN-_delta)){ -      /*Subtracting this amount from the granule position would underflow the -         negative half of its 64-bit range. -        Since signed underflow is undefined in C, do it in a way the compiler -         isn't allowed to screw up.*/ -      _delta+=(opus_int32)(_src_gp-OP_INT64_MIN)+1; -      _src_gp=OP_INT64_MAX; -    } -  } -  *_dst_gp=_src_gp+_delta; -  return 0; -} - -/*Safely computes the difference between two granule positions. -  The difference must fit in a signed 64-bit integer, or the function fails. -  It correctly handles the case where the granule position has wrapped around -   from positive values to negative ones. -  [out] _delta: The difference between the granule positions. -                Only modified on success. -  _gp_a:        The granule position to subtract from. -                This must not be -1. -  _gp_b:        The granule position to subtract. -                This must not be -1. -  Return: 0 on success, or OP_EINVAL if the result would not fit in a signed -           64-bit integer.*/ -static int op_granpos_diff(ogg_int64_t *_delta, - ogg_int64_t _gp_a,ogg_int64_t _gp_b){ -  int gp_a_negative; -  int gp_b_negative; -  /*The code below handles these cases correctly, but there's no reason we -     should ever be called with these values, so make sure we aren't.*/ -  OP_ASSERT(_gp_a!=-1); -  OP_ASSERT(_gp_b!=-1); -  gp_a_negative=OP_UNLIKELY(_gp_a<0); -  gp_b_negative=OP_UNLIKELY(_gp_b<0); -  if(OP_UNLIKELY(gp_a_negative^gp_b_negative)){ -    ogg_int64_t da; -    ogg_int64_t db; -    if(gp_a_negative){ -      /*_gp_a has wrapped to a negative value but _gp_b hasn't: the difference -         should be positive.*/ -      /*Step 1: Handle wrapping.*/ -      /*_gp_a < 0 => da < 0.*/ -      da=(OP_INT64_MIN-_gp_a)-1; -      /*_gp_b >= 0  => db >= 0.*/ -      db=OP_INT64_MAX-_gp_b; -      /*Step 2: Check for overflow.*/ -      if(OP_UNLIKELY(OP_INT64_MAX+da<db))return OP_EINVAL; -      *_delta=db-da; -    } -    else{ -      /*_gp_b has wrapped to a negative value but _gp_a hasn't: the difference -         should be negative.*/ -      /*Step 1: Handle wrapping.*/ -      /*_gp_a >= 0 => da <= 0*/ -      da=_gp_a+OP_INT64_MIN; -      /*_gp_b < 0 => db <= 0*/ -      db=OP_INT64_MIN-_gp_b; -      /*Step 2: Check for overflow.*/ -      if(OP_UNLIKELY(da<OP_INT64_MIN-db))return OP_EINVAL; -      *_delta=da+db; -    } -  } -  else *_delta=_gp_a-_gp_b; -  return 0; -} - -static int op_granpos_cmp(ogg_int64_t _gp_a,ogg_int64_t _gp_b){ -  /*The invalid granule position -1 should behave like NaN: neither greater -     than nor less than any other granule position, nor equal to any other -     granule position, including itself. -    However, that means there isn't anything we could sensibly return from this -     function for it.*/ -  OP_ASSERT(_gp_a!=-1); -  OP_ASSERT(_gp_b!=-1); -  /*Handle the wrapping cases.*/ -  if(OP_UNLIKELY(_gp_a<0)){ -    if(_gp_b>=0)return 1; -    /*Else fall through.*/ -  } -  else if(OP_UNLIKELY(_gp_b<0))return -1; -  /*No wrapping case.*/ -  return (_gp_a>_gp_b)-(_gp_b>_gp_a); -} - -/*Returns the duration of the packet (in samples at 48 kHz), or a negative -   value on error.*/ -static int op_get_packet_duration(const unsigned char *_data,int _len){ -  int nframes; -  int frame_size; -  int nsamples; -  nframes=opus_packet_get_nb_frames(_data,_len); -  if(OP_UNLIKELY(nframes<0))return OP_EBADPACKET; -  frame_size=opus_packet_get_samples_per_frame(_data,48000); -  nsamples=nframes*frame_size; -  if(OP_UNLIKELY(nsamples>120*48))return OP_EBADPACKET; -  return nsamples; -} - -/*This function more properly belongs in info.c, but we define it here to allow -   the static granule position manipulation functions to remain static.*/ -ogg_int64_t opus_granule_sample(const OpusHead *_head,ogg_int64_t _gp){ -  opus_int32 pre_skip; -  pre_skip=_head->pre_skip; -  if(_gp!=-1&&op_granpos_add(&_gp,_gp,-pre_skip))_gp=-1; -  return _gp; -} - -/*Grab all the packets currently in the stream state, and compute their -   durations. -  _of->op_count is set to the number of packets collected. -  [out] _durations: Returns the durations of the individual packets. -  Return: The total duration of all packets, or OP_HOLE if there was a hole.*/ -static opus_int32 op_collect_audio_packets(OggOpusFile *_of, - int _durations[255]){ -  opus_int32 total_duration; -  int        op_count; -  /*Count the durations of all packets in the page.*/ -  op_count=0; -  total_duration=0; -  for(;;){ -    int ret; -    /*This takes advantage of undocumented libogg behavior that returned -       ogg_packet buffers are valid at least until the next page is -       submitted. -      Relying on this is not too terrible, as _none_ of the Ogg memory -       ownership/lifetime rules are well-documented. -      But I can read its code and know this will work.*/ -    ret=ogg_stream_packetout(&_of->os,_of->op+op_count); -    if(!ret)break; -    if(OP_UNLIKELY(ret<0)){ -      /*We shouldn't get holes in the middle of pages.*/ -      OP_ASSERT(op_count==0); -      /*Set the return value and break out of the loop. -        We want to make sure op_count gets set to 0, because we've ingested a -         page, so any previously loaded packets are now invalid.*/ -      total_duration=OP_HOLE; -      break; -    } -    /*Unless libogg is broken, we can't get more than 255 packets from a -       single page.*/ -    OP_ASSERT(op_count<255); -    _durations[op_count]=op_get_packet_duration(_of->op[op_count].packet, -     _of->op[op_count].bytes); -    if(OP_LIKELY(_durations[op_count]>0)){ -      /*With at most 255 packets on a page, this can't overflow.*/ -      total_duration+=_durations[op_count++]; -    } -    /*Ignore packets with an invalid TOC sequence.*/ -    else if(op_count>0){ -      /*But save the granule position, if there was one.*/ -      _of->op[op_count-1].granulepos=_of->op[op_count].granulepos; -    } -  } -  _of->op_pos=0; -  _of->op_count=op_count; -  return total_duration; -} - -/*Starting from current cursor position, get the initial PCM offset of the next -   page. -  This also validates the granule position on the first page with a completed -   audio data packet, as required by the spec. -  If this link is completely empty (no pages with completed packets), then this -   function sets pcm_start=pcm_end=0 and returns the BOS page of the next link -   (if any). -  In the seekable case, we initialize pcm_end=-1 before calling this function, -   so that later we can detect that the link was empty before calling -   op_find_final_pcm_offset(). -  [inout] _link: The link for which to find pcm_start. -  [out] _og:     Returns the BOS page of the next link if this link was empty. -                 In the unseekable case, we can then feed this to -                  op_fetch_headers() to start the next link. -                 The caller may pass NULL (e.g., for seekable streams), in -                  which case this page will be discarded. -  Return: 0 on success, 1 if there is a buffered BOS page available, or a -           negative value on unrecoverable error.*/ -static int op_find_initial_pcm_offset(OggOpusFile *_of, - OggOpusLink *_link,ogg_page *_og){ -  ogg_page     og; -  ogg_int64_t  pcm_start; -  ogg_int64_t  prev_packet_gp; -  ogg_int64_t  cur_page_gp; -  ogg_uint32_t serialno; -  opus_int32   total_duration; -  int          durations[255]; -  int          cur_page_eos; -  int          op_count; -  int          pi; -  if(_og==NULL)_og=&og; -  serialno=_of->os.serialno; -  op_count=0; -  /*We shouldn't have to initialize total_duration, but gcc is too dumb to -     figure out that op_count>0 implies we've been through the whole loop at -     least once.*/ -  total_duration=0; -  do{ -    opus_int64 llret; -    llret=op_get_next_page(_of,_og,_of->end); -    /*We should get a page unless the file is truncated or mangled. -      Otherwise there are no audio data packets in the whole logical stream.*/ -    if(OP_UNLIKELY(llret<0)){ -      /*Fail if there was a read error.*/ -      if(llret<OP_FALSE)return (int)llret; -      /*Fail if the pre-skip is non-zero, since it's asking us to skip more -         samples than exist.*/ -      if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP; -      /*Set pcm_end and end_offset so we can skip the call to -         op_find_final_pcm_offset().*/ -      _link->pcm_start=_link->pcm_end=0; -      _link->end_offset=_link->data_offset; -      return 0; -    } -    /*Similarly, if we hit the next link in the chain, we've gone too far.*/ -    if(OP_UNLIKELY(ogg_page_bos(_og))){ -      if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP; -      /*Set pcm_end and end_offset so we can skip the call to -         op_find_final_pcm_offset().*/ -      _link->pcm_end=_link->pcm_start=0; -      _link->end_offset=_link->data_offset; -      /*Tell the caller we've got a buffered page for them.*/ -      return 1; -    } -    /*Ignore pages from other streams (not strictly necessary, because of the -       checks in ogg_stream_pagein(), but saves some work).*/ -    if(serialno!=(ogg_uint32_t)ogg_page_serialno(_og))continue; -    ogg_stream_pagein(&_of->os,_og); -    /*Bitrate tracking: add the header's bytes here. -      The body bytes are counted when we consume the packets.*/ -    _of->bytes_tracked+=_og->header_len; -    /*Count the durations of all packets in the page.*/ -    do total_duration=op_collect_audio_packets(_of,durations); -    /*Ignore holes.*/ -    while(OP_UNLIKELY(total_duration<0)); -    op_count=_of->op_count; -  } -  while(op_count<=0); -  /*We found the first page with a completed audio data packet: actually look -     at the granule position. -    RFC 3533 says, "A special value of -1 (in two's complement) indicates that -     no packets finish on this page," which does not say that a granule -     position that is NOT -1 indicates that some packets DO finish on that page -     (even though this was the intention, libogg itself violated this intention -     for years before we fixed it). -    The Ogg Opus specification only imposes its start-time requirements -     on the granule position of the first page with completed packets, -     so we ignore any set granule positions until then.*/ -  cur_page_gp=_of->op[op_count-1].granulepos; -  /*But getting a packet without a valid granule position on the page is not -     okay.*/ -  if(cur_page_gp==-1)return OP_EBADTIMESTAMP; -  cur_page_eos=_of->op[op_count-1].e_o_s; -  if(OP_LIKELY(!cur_page_eos)){ -    /*The EOS flag wasn't set. -      Work backwards from the provided granule position to get the starting PCM -       offset.*/ -    if(OP_UNLIKELY(op_granpos_add(&pcm_start,cur_page_gp,-total_duration)<0)){ -      /*The starting granule position MUST not be smaller than the amount of -         audio on the first page with completed packets.*/ -      return OP_EBADTIMESTAMP; -    } -  } -  else{ -    /*The first page with completed packets was also the last.*/ -    if(OP_LIKELY(op_granpos_add(&pcm_start,cur_page_gp,-total_duration)<0)){ -      /*If there's less audio on the page than indicated by the granule -         position, then we're doing end-trimming, and the starting PCM offset -         is zero by spec mandate.*/ -      pcm_start=0; -      /*However, the end-trimming MUST not ask us to trim more samples than -         exist after applying the pre-skip.*/ -      if(OP_UNLIKELY(op_granpos_cmp(cur_page_gp,_link->head.pre_skip)<0)){ -        return OP_EBADTIMESTAMP; -      } -    } -  } -  /*Timestamp the individual packets.*/ -  prev_packet_gp=pcm_start; -  for(pi=0;pi<op_count;pi++){ -    if(cur_page_eos){ -      ogg_int64_t diff=0; -      OP_ALWAYS_TRUE(!op_granpos_diff(&diff,cur_page_gp,prev_packet_gp)); -      diff=durations[pi]-diff; -      /*If we have samples to trim...*/ -      if(diff>0){ -        /*If we trimmed the entire packet, stop (the spec says encoders -           shouldn't do this, but we support it anyway).*/ -        if(OP_UNLIKELY(diff>durations[pi]))break; -        _of->op[pi].granulepos=prev_packet_gp=cur_page_gp; -        /*Move the EOS flag to this packet, if necessary, so we'll trim the -           samples.*/ -        _of->op[pi].e_o_s=1; -        continue; -      } -    } -    /*Update the granule position as normal.*/ -    OP_ALWAYS_TRUE(!op_granpos_add(&_of->op[pi].granulepos, -     prev_packet_gp,durations[pi])); -    prev_packet_gp=_of->op[pi].granulepos; -  } -  /*Update the packet count after end-trimming.*/ -  _of->op_count=pi; -  _of->cur_discard_count=_link->head.pre_skip; -  _of->prev_packet_gp=_link->pcm_start=pcm_start; -  return 0; -} - -/*Starting from current cursor position, get the final PCM offset of the -   previous page. -  This also validates the duration of the link, which, while not strictly -   required by the spec, we need to ensure duration calculations don't -   overflow. -  This is only done for seekable sources. -  We must validate that op_find_initial_pcm_offset() succeeded for this link -   before calling this function, otherwise it will scan the entire stream -   backwards until it reaches the start, and then fail.*/ -static int op_find_final_pcm_offset(OggOpusFile *_of, - const ogg_uint32_t *_serialnos,int _nserialnos,OggOpusLink *_link, - opus_int64 _offset,ogg_uint32_t _end_serialno,ogg_int64_t _end_gp, - ogg_int64_t *_total_duration){ -  ogg_int64_t  total_duration; -  ogg_int64_t  duration; -  ogg_uint32_t cur_serialno; -  /*For the time being, fetch end PCM offset the simple way.*/ -  cur_serialno=_link->serialno; -  if(_end_serialno!=cur_serialno||_end_gp==-1){ -    _offset=op_get_last_page(_of,&_end_gp,_offset, -     cur_serialno,_serialnos,_nserialnos); -    if(OP_UNLIKELY(_offset<0))return (int)_offset; -  } -  /*At worst we should have found the first page with completed packets.*/ -  if(OP_UNLIKELY(_offset<_link->data_offset))return OP_EBADLINK; -  /*This implementation requires that the difference between the first and last -     granule positions in each link be representable in a signed, 64-bit -     number, and that each link also have at least as many samples as the -     pre-skip requires.*/ -  if(OP_UNLIKELY(op_granpos_diff(&duration,_end_gp,_link->pcm_start)<0) -   ||OP_UNLIKELY(duration<_link->head.pre_skip)){ -    return OP_EBADTIMESTAMP; -  } -  /*We also require that the total duration be representable in a signed, -     64-bit number.*/ -  duration-=_link->head.pre_skip; -  total_duration=*_total_duration; -  if(OP_UNLIKELY(OP_INT64_MAX-duration<total_duration))return OP_EBADTIMESTAMP; -  *_total_duration=total_duration+duration; -  _link->pcm_end=_end_gp; -  _link->end_offset=_offset; -  return 0; -} - -/*Rescale the number _x from the range [0,_from] to [0,_to]. -  _from and _to must be positive.*/ -static opus_int64 op_rescale64(opus_int64 _x,opus_int64 _from,opus_int64 _to){ -  opus_int64 frac; -  opus_int64 ret; -  int        i; -  if(_x>=_from)return _to; -  if(_x<=0)return 0; -  frac=0; -  for(i=0;i<63;i++){ -    frac<<=1; -    OP_ASSERT(_x<=_from); -    if(_x>=_from>>1){ -      _x-=_from-_x; -      frac|=1; -    } -    else _x<<=1; -  } -  ret=0; -  for(i=0;i<63;i++){ -    if(frac&1)ret=(ret&_to&1)+(ret>>1)+(_to>>1); -    else ret>>=1; -    frac>>=1; -  } -  return ret; -} - -/*The minimum granule position spacing allowed for making predictions. -  This corresponds to about 1 second of audio at 48 kHz for both Opus and -   Vorbis, or one keyframe interval in Theora with the default keyframe spacing -   of 256.*/ -#define OP_GP_SPACING_MIN (48000) - -/*Try to estimate the location of the next link using the current seek -   records, assuming the initial granule position of any streams we've found is -   0.*/ -static opus_int64 op_predict_link_start(const OpusSeekRecord *_sr,int _nsr, - opus_int64 _searched,opus_int64 _end_searched,opus_int32 _bias){ -  opus_int64 bisect; -  int        sri; -  int        srj; -  /*Require that we be at least OP_CHUNK_SIZE from the end. -    We don't require that we be at least OP_CHUNK_SIZE from the beginning, -     because if we are we'll just scan forward without seeking.*/ -  _end_searched-=OP_CHUNK_SIZE; -  if(_searched>=_end_searched)return -1; -  bisect=_end_searched; -  for(sri=0;sri<_nsr;sri++){ -    ogg_int64_t  gp1; -    ogg_int64_t  gp2_min; -    ogg_uint32_t serialno1; -    opus_int64   offset1; -    /*If the granule position is negative, either it's invalid or we'd cause -       overflow.*/ -    gp1=_sr[sri].gp; -    if(gp1<0)continue; -    /*We require some minimum distance between granule positions to make an -       estimate. -      We don't actually know what granule position scheme is being used, -       because we have no idea what kind of stream these came from. -      Therefore we require a minimum spacing between them, with the -       expectation that while bitrates and granule position increments might -       vary locally in quite complex ways, they are globally smooth.*/ -    if(OP_UNLIKELY(op_granpos_add(&gp2_min,gp1,OP_GP_SPACING_MIN)<0)){ -      /*No granule position would satisfy us.*/ -      continue; -    } -    offset1=_sr[sri].offset; -    serialno1=_sr[sri].serialno; -    for(srj=sri;srj-->0;){ -      ogg_int64_t gp2; -      opus_int64  offset2; -      opus_int64  num; -      ogg_int64_t den; -      ogg_int64_t ipart; -      gp2=_sr[srj].gp; -      if(gp2<gp2_min)continue; -      /*Oh, and also make sure these came from the same stream.*/ -      if(_sr[srj].serialno!=serialno1)continue; -      offset2=_sr[srj].offset; -      /*For once, we can subtract with impunity.*/ -      den=gp2-gp1; -      ipart=gp2/den; -      num=offset2-offset1; -      OP_ASSERT(num>0); -      if(ipart>0&&(offset2-_searched)/ipart<num)continue; -      offset2-=ipart*num; -      gp2-=ipart*den; -      offset2-=op_rescale64(gp2,den,num)-_bias; -      if(offset2<_searched)continue; -      bisect=OP_MIN(bisect,offset2); -      break; -    } -  } -  return bisect>=_end_searched?-1:bisect; -} - -/*Finds each bitstream link, one at a time, using a bisection search. -  This has to begin by knowing the offset of the first link's initial page.*/ -static int op_bisect_forward_serialno(OggOpusFile *_of, - opus_int64 _searched,OpusSeekRecord *_sr,int _csr, - ogg_uint32_t **_serialnos,int *_nserialnos,int *_cserialnos){ -  ogg_page      og; -  OggOpusLink  *links; -  int           nlinks; -  int           clinks; -  ogg_uint32_t *serialnos; -  int           nserialnos; -  ogg_int64_t   total_duration; -  int           nsr; -  int           ret; -  links=_of->links; -  nlinks=clinks=_of->nlinks; -  total_duration=0; -  /*We start with one seek record, for the last page in the file. -    We build up a list of records for places we seek to during link -     enumeration. -    This list is kept sorted in reverse order. -    We only care about seek locations that were _not_ in the current link, -     therefore we can add them one at a time to the end of the list as we -     improve the lower bound on the location where the next link starts.*/ -  nsr=1; -  for(;;){ -    opus_int64  end_searched; -    opus_int64  bisect; -    opus_int64  next; -    opus_int64  last; -    ogg_int64_t end_offset=0; -    ogg_int64_t end_gp; -    int         sri; -    serialnos=*_serialnos; -    nserialnos=*_nserialnos; -    if(OP_UNLIKELY(nlinks>=clinks)){ -      if(OP_UNLIKELY(clinks>INT_MAX-1>>1))return OP_EFAULT; -      clinks=2*clinks+1; -      OP_ASSERT(nlinks<clinks); -      links=(OggOpusLink *)_ogg_realloc(links,sizeof(*links)*clinks); -      if(OP_UNLIKELY(links==NULL))return OP_EFAULT; -      _of->links=links; -    } -    /*Invariants: -      We have the headers and serial numbers for the link beginning at 'begin'. -      We have the offset and granule position of the last page in the file -       (potentially not a page we care about).*/ -    /*Scan the seek records we already have to save us some bisection.*/ -    for(sri=0;sri<nsr;sri++){ -      if(op_lookup_serialno(_sr[sri].serialno,serialnos,nserialnos))break; -    } -    /*Is the last page in our current list of serial numbers?*/ -    if(sri<=0)break; -    /*Last page wasn't found. -      We have at least one more link.*/ -    last=-1; -    end_searched=_sr[sri-1].search_start; -    next=_sr[sri-1].offset; -    end_gp=-1; -    if(sri<nsr){ -      _searched=_sr[sri].offset+_sr[sri].size; -      if(_sr[sri].serialno==links[nlinks-1].serialno){ -        end_gp=_sr[sri].gp; -        end_offset=_sr[sri].offset; -      } -    } -    nsr=sri; -    bisect=-1; -    /*If we've already found the end of at least one link, try to pick the -       first bisection point at twice the average link size. -      This is a good choice for files with lots of links that are all about the -       same size.*/ -    if(nlinks>1){ -      opus_int64 last_offset; -      opus_int64 avg_link_size; -      opus_int64 upper_limit; -      last_offset=links[nlinks-1].offset; -      avg_link_size=last_offset/(nlinks-1); -      upper_limit=end_searched-OP_CHUNK_SIZE-avg_link_size; -      if(OP_LIKELY(last_offset>_searched-avg_link_size) -       &&OP_LIKELY(last_offset<upper_limit)){ -        bisect=last_offset+avg_link_size; -        if(OP_LIKELY(bisect<upper_limit))bisect+=avg_link_size; -      } -    } -    /*We guard against garbage separating the last and first pages of two -       links below.*/ -    while(_searched<end_searched){ -      opus_int32 next_bias; -      /*If we don't have a better estimate, use simple bisection.*/ -      if(bisect==-1)bisect=_searched+(end_searched-_searched>>1); -      /*If we're within OP_CHUNK_SIZE of the start, scan forward.*/ -      if(bisect-_searched<OP_CHUNK_SIZE)bisect=_searched; -      /*Otherwise we're skipping data. -        Forget the end page, if we saw one, as we might miss a later one.*/ -      else end_gp=-1; -      ret=op_seek_helper(_of,bisect); -      if(OP_UNLIKELY(ret<0))return ret; -      last=op_get_next_page(_of,&og,_sr[nsr-1].offset); -      if(OP_UNLIKELY(last<OP_FALSE))return (int)last; -      next_bias=0; -      if(last==OP_FALSE)end_searched=bisect; -      else{ -        ogg_uint32_t serialno; -        ogg_int64_t  gp; -        serialno=ogg_page_serialno(&og); -        gp=ogg_page_granulepos(&og); -        if(!op_lookup_serialno(serialno,serialnos,nserialnos)){ -          end_searched=bisect; -          next=last; -          /*In reality we should always have enough room, but be paranoid.*/ -          if(OP_LIKELY(nsr<_csr)){ -            _sr[nsr].search_start=bisect; -            _sr[nsr].offset=last; -            OP_ASSERT(_of->offset-last>=0); -            OP_ASSERT(_of->offset-last<=OP_PAGE_SIZE_MAX); -            _sr[nsr].size=(opus_int32)(_of->offset-last); -            _sr[nsr].serialno=serialno; -            _sr[nsr].gp=gp; -            nsr++; -          } -        } -        else{ -          _searched=_of->offset; -          next_bias=OP_CHUNK_SIZE; -          if(serialno==links[nlinks-1].serialno){ -            /*This page was from the stream we want, remember it. -              If it's the last such page in the link, we won't have to go back -               looking for it later.*/ -            end_gp=gp; -            end_offset=last; -          } -        } -      } -      bisect=op_predict_link_start(_sr,nsr,_searched,end_searched,next_bias); -    } -    /*Bisection point found. -      Get the final granule position of the previous link, assuming -       op_find_initial_pcm_offset() didn't already determine the link was -       empty.*/ -    if(OP_LIKELY(links[nlinks-1].pcm_end==-1)){ -      if(end_gp==-1){ -        /*If we don't know where the end page is, we'll have to seek back and -           look for it, starting from the end of the link.*/ -        end_offset=next; -        /*Also forget the last page we read. -          It won't be available after the seek.*/ -        last=-1; -      } -      ret=op_find_final_pcm_offset(_of,serialnos,nserialnos, -       links+nlinks-1,end_offset,links[nlinks-1].serialno,end_gp, -       &total_duration); -      if(OP_UNLIKELY(ret<0))return ret; -    } -    if(last!=next){ -      /*The last page we read was not the first page the next link. -        Move the cursor position to the offset of that first page. -        This only performs an actual seek if the first page of the next link -         does not start at the end of the last page from the current Opus -         stream with a valid granule position.*/ -      ret=op_seek_helper(_of,next); -      if(OP_UNLIKELY(ret<0))return ret; -    } -    ret=op_fetch_headers(_of,&links[nlinks].head,&links[nlinks].tags, -     _serialnos,_nserialnos,_cserialnos,last!=next?NULL:&og); -    if(OP_UNLIKELY(ret<0))return ret; -    links[nlinks].offset=next; -    links[nlinks].data_offset=_of->offset; -    links[nlinks].serialno=_of->os.serialno; -    links[nlinks].pcm_end=-1; -    /*This might consume a page from the next link, however the next bisection -       always starts with a seek.*/ -    ret=op_find_initial_pcm_offset(_of,links+nlinks,NULL); -    if(OP_UNLIKELY(ret<0))return ret; -    _searched=_of->offset; -    /*Mark the current link count so it can be cleaned up on error.*/ -    _of->nlinks=++nlinks; -  } -  /*Last page is in the starting serialno list, so we've reached the last link. -    Now find the last granule position for it (if we didn't the first time we -     looked at the end of the stream, and if op_find_initial_pcm_offset() -     didn't already determine the link was empty).*/ -  if(OP_LIKELY(links[nlinks-1].pcm_end==-1)){ -    ret=op_find_final_pcm_offset(_of,serialnos,nserialnos, -     links+nlinks-1,_sr[0].offset,_sr[0].serialno,_sr[0].gp,&total_duration); -    if(OP_UNLIKELY(ret<0))return ret; -  } -  /*Trim back the links array if necessary.*/ -  links=(OggOpusLink *)_ogg_realloc(links,sizeof(*links)*nlinks); -  if(OP_LIKELY(links!=NULL))_of->links=links; -  /*We also don't need these anymore.*/ -  _ogg_free(*_serialnos); -  *_serialnos=NULL; -  *_cserialnos=*_nserialnos=0; -  return 0; -} - -static void op_update_gain(OggOpusFile *_of){ -  OpusHead   *head; -  opus_int32  gain_q8; -  int         li; -  /*If decode isn't ready, then we'll apply the gain when we initialize the -     decoder.*/ -  if(_of->ready_state<OP_INITSET)return; -  gain_q8=_of->gain_offset_q8; -  li=_of->seekable?_of->cur_link:0; -  head=&_of->links[li].head; -  /*We don't have to worry about overflow here because the header gain and -     track gain must lie in the range [-32768,32767], and the user-supplied -     offset has been pre-clamped to [-98302,98303].*/ -  switch(_of->gain_type){ -    case OP_TRACK_GAIN:{ -      int track_gain_q8; -      track_gain_q8=0; -      opus_tags_get_track_gain(&_of->links[li].tags,&track_gain_q8); -      gain_q8+=track_gain_q8; -    } -    /*Fall through.*/ -    case OP_HEADER_GAIN:gain_q8+=head->output_gain;break; -    case OP_ABSOLUTE_GAIN:break; -    default:OP_ASSERT(0); -  } -  gain_q8=OP_CLAMP(-32768,gain_q8,32767); -  OP_ASSERT(_of->od!=NULL); -#if defined(OPUS_SET_GAIN) -  opus_multistream_decoder_ctl(_of->od,OPUS_SET_GAIN(gain_q8)); -#else -/*A fallback that works with both float and fixed-point is a bunch of work, -   so just force people to use a sufficiently new version. -  This is deployed well enough at this point that this shouldn't be a burden.*/ -# error "libopus 1.0.1 or later required" -#endif -} - -static int op_make_decode_ready(OggOpusFile *_of){ -  const OpusHead *head; -  int             li; -  int             stream_count; -  int             coupled_count; -  int             channel_count; -  if(_of->ready_state>OP_STREAMSET)return 0; -  if(OP_UNLIKELY(_of->ready_state<OP_STREAMSET))return OP_EFAULT; -  li=_of->seekable?_of->cur_link:0; -  head=&_of->links[li].head; -  stream_count=head->stream_count; -  coupled_count=head->coupled_count; -  channel_count=head->channel_count; -  /*Check to see if the current decoder is compatible with the current link.*/ -  if(_of->od!=NULL&&_of->od_stream_count==stream_count -   &&_of->od_coupled_count==coupled_count&&_of->od_channel_count==channel_count -   &&memcmp(_of->od_mapping,head->mapping, -   sizeof(*head->mapping)*channel_count)==0){ -    opus_multistream_decoder_ctl(_of->od,OPUS_RESET_STATE); -  } -  else{ -    int err; -    opus_multistream_decoder_destroy(_of->od); -    _of->od=opus_multistream_decoder_create(48000,channel_count, -     stream_count,coupled_count,head->mapping,&err); -    if(_of->od==NULL)return OP_EFAULT; -    _of->od_stream_count=stream_count; -    _of->od_coupled_count=coupled_count; -    _of->od_channel_count=channel_count; -    memcpy(_of->od_mapping,head->mapping,sizeof(*head->mapping)*channel_count); -  } -  _of->ready_state=OP_INITSET; -  _of->bytes_tracked=0; -  _of->samples_tracked=0; -#if !defined(OP_FIXED_POINT) -  _of->state_channel_count=0; -  /*Use the serial number for the PRNG seed to get repeatable output for -     straight play-throughs.*/ -  _of->dither_seed=_of->links[li].serialno; -#endif -  op_update_gain(_of); -  return 0; -} - -static int op_open_seekable2_impl(OggOpusFile *_of){ -  /*64 seek records should be enough for anybody. -    Actually, with a bisection search in a 63-bit range down to OP_CHUNK_SIZE -     granularity, much more than enough.*/ -  OpusSeekRecord sr[64]; -  opus_int64     data_offset; -  int            ret; -  /*We can seek, so set out learning all about this file.*/ -  (*_of->callbacks.seek)(_of->source,0,SEEK_END); -  _of->offset=_of->end=(*_of->callbacks.tell)(_of->source); -  if(OP_UNLIKELY(_of->end<0))return OP_EREAD; -  data_offset=_of->links[0].data_offset; -  if(OP_UNLIKELY(_of->end<data_offset))return OP_EBADLINK; -  /*Get the offset of the last page of the physical bitstream, or, if we're -     lucky, the last Opus page of the first link, as most Ogg Opus files will -     contain a single logical bitstream.*/ -  ret=op_get_prev_page_serial(_of,sr,_of->end, -   _of->links[0].serialno,_of->serialnos,_of->nserialnos); -  if(OP_UNLIKELY(ret<0))return ret; -  /*If there's any trailing junk, forget about it.*/ -  _of->end=sr[0].offset+sr[0].size; -  if(OP_UNLIKELY(_of->end<data_offset))return OP_EBADLINK; -  /*Now enumerate the bitstream structure.*/ -  return op_bisect_forward_serialno(_of,data_offset,sr,sizeof(sr)/sizeof(*sr), -   &_of->serialnos,&_of->nserialnos,&_of->cserialnos); -} - -static int op_open_seekable2(OggOpusFile *_of){ -  ogg_sync_state    oy_start; -  ogg_stream_state  os_start; -  ogg_packet       *op_start; -  opus_int64        start_offset; -  int               start_op_count; -  int               ret; -  /*We're partially open and have a first link header state in storage in _of. -    Save off that stream state so we can come back to it. -    It would be simpler to just dump all this state and seek back to -     links[0].data_offset when we're done. -    But we do the extra work to allow us to seek back to _exactly_ the same -     stream position we're at now. -    This allows, e.g., the HTTP backend to continue reading from the original -     connection (if it's still available), instead of opening a new one. -    This means we can open and start playing a normal Opus file with a single -     link and reasonable packet sizes using only two HTTP requests.*/ -  start_op_count=_of->op_count; -  /*This is a bit too large to put on the stack unconditionally.*/ -  op_start=(ogg_packet *)_ogg_malloc(sizeof(*op_start)*start_op_count); -  if(op_start==NULL)return OP_EFAULT; -  *&oy_start=_of->oy; -  *&os_start=_of->os; -  start_offset=_of->offset; -  memcpy(op_start,_of->op,sizeof(*op_start)*start_op_count); -  OP_ASSERT((*_of->callbacks.tell)(_of->source)==op_position(_of)); -  ogg_sync_init(&_of->oy); -  ogg_stream_init(&_of->os,-1); -  ret=op_open_seekable2_impl(_of); -  /*Restore the old stream state.*/ -  ogg_stream_clear(&_of->os); -  ogg_sync_clear(&_of->oy); -  *&_of->oy=*&oy_start; -  *&_of->os=*&os_start; -  _of->offset=start_offset; -  _of->op_count=start_op_count; -  memcpy(_of->op,op_start,sizeof(*_of->op)*start_op_count); -  _ogg_free(op_start); -  _of->prev_packet_gp=_of->links[0].pcm_start; -  _of->cur_discard_count=_of->links[0].head.pre_skip; -  if(OP_UNLIKELY(ret<0))return ret; -  /*And restore the position indicator.*/ -  ret=(*_of->callbacks.seek)(_of->source,op_position(_of),SEEK_SET); -  return OP_UNLIKELY(ret<0)?OP_EREAD:0; -} - -/*Clear out the current logical bitstream decoder.*/ -static void op_decode_clear(OggOpusFile *_of){ -  /*We don't actually free the decoder. -    We might be able to re-use it for the next link.*/ -  _of->op_count=0; -  _of->od_buffer_size=0; -  _of->prev_packet_gp=-1; -  if(!_of->seekable){ -    OP_ASSERT(_of->ready_state>=OP_INITSET); -    opus_tags_clear(&_of->links[0].tags); -  } -  _of->ready_state=OP_OPENED; -} - -static void op_clear(OggOpusFile *_of){ -  OggOpusLink *links; -  _ogg_free(_of->od_buffer); -  if(_of->od!=NULL)opus_multistream_decoder_destroy(_of->od); -  links=_of->links; -  if(!_of->seekable){ -    if(_of->ready_state>OP_OPENED||_of->ready_state==OP_PARTOPEN){ -      opus_tags_clear(&links[0].tags); -    } -  } -  else if(OP_LIKELY(links!=NULL)){ -    int nlinks; -    int link; -    nlinks=_of->nlinks; -    for(link=0;link<nlinks;link++)opus_tags_clear(&links[link].tags); -  } -  _ogg_free(links); -  _ogg_free(_of->serialnos); -  ogg_stream_clear(&_of->os); -  ogg_sync_clear(&_of->oy); -  if(_of->callbacks.close!=NULL)(*_of->callbacks.close)(_of->source); -} - -static int op_open1(OggOpusFile *_of, - void *_source,const OpusFileCallbacks *_cb, - const unsigned char *_initial_data,size_t _initial_bytes){ -  ogg_page  og; -  ogg_page *pog; -  int       seekable; -  int       ret; -  memset(_of,0,sizeof(*_of)); -  _of->end=-1; -  _of->source=_source; -  *&_of->callbacks=*_cb; -  /*At a minimum, we need to be able to read data.*/ -  if(OP_UNLIKELY(_of->callbacks.read==NULL))return OP_EREAD; -  /*Initialize the framing state.*/ -  ogg_sync_init(&_of->oy); -  /*Perhaps some data was previously read into a buffer for testing against -     other stream types. -    Allow initialization from this previously read data (especially as we may -     be reading from a non-seekable stream). -    This requires copying it into a buffer allocated by ogg_sync_buffer() and -     doesn't support seeking, so this is not a good mechanism to use for -     decoding entire files from RAM.*/ -  if(_initial_bytes>0){ -    char *buffer; -    buffer=ogg_sync_buffer(&_of->oy,_initial_bytes); -    memcpy(buffer,_initial_data,_initial_bytes*sizeof(*buffer)); -    ogg_sync_wrote(&_of->oy,_initial_bytes); -  } -  /*Can we seek? -    Stevens suggests the seek test is portable.*/ -  seekable=_cb->seek!=NULL&&(*_cb->seek)(_source,0,SEEK_CUR)!=-1; -  /*If seek is implemented, tell must also be implemented.*/ -  if(seekable){ -    opus_int64 pos; -    if(OP_UNLIKELY(_of->callbacks.tell==NULL))return OP_EINVAL; -    pos=(*_of->callbacks.tell)(_of->source); -    /*If the current position is not equal to the initial bytes consumed, -       absolute seeking will not work.*/ -    if(OP_UNLIKELY(pos!=(opus_int64)_initial_bytes))return OP_EINVAL; -  } -  _of->seekable=seekable; -  /*Don't seek yet. -    Set up a 'single' (current) logical bitstream entry for partial open.*/ -  _of->links=(OggOpusLink *)_ogg_malloc(sizeof(*_of->links)); -  /*The serialno gets filled in later by op_fetch_headers().*/ -  ogg_stream_init(&_of->os,-1); -  pog=NULL; -  for(;;){ -    /*Fetch all BOS pages, store the Opus header and all seen serial numbers, -      and load subsequent Opus setup headers.*/ -    ret=op_fetch_headers(_of,&_of->links[0].head,&_of->links[0].tags, -     &_of->serialnos,&_of->nserialnos,&_of->cserialnos,pog); -    if(OP_UNLIKELY(ret<0))break; -    _of->nlinks=1; -    _of->links[0].offset=0; -    _of->links[0].data_offset=_of->offset; -    _of->links[0].pcm_end=-1; -    _of->links[0].serialno=_of->os.serialno; -    /*Fetch the initial PCM offset.*/ -    ret=op_find_initial_pcm_offset(_of,_of->links,&og); -    if(seekable||OP_LIKELY(ret<=0))break; -    /*This link was empty, but we already have the BOS page for the next one in -       og. -      We can't seek, so start processing the next link right now.*/ -    opus_tags_clear(&_of->links[0].tags); -    _of->nlinks=0; -    if(!seekable)_of->cur_link++; -    pog=&og; -  } -  if(OP_LIKELY(ret>=0))_of->ready_state=OP_PARTOPEN; -  return ret; -} - -static int op_open2(OggOpusFile *_of){ -  int ret; -  OP_ASSERT(_of->ready_state==OP_PARTOPEN); -  if(_of->seekable){ -    _of->ready_state=OP_OPENED; -    ret=op_open_seekable2(_of); -  } -  else ret=0; -  if(OP_LIKELY(ret>=0)){ -    /*We have buffered packets from op_find_initial_pcm_offset(). -      Move to OP_INITSET so we can use them.*/ -    _of->ready_state=OP_STREAMSET; -    ret=op_make_decode_ready(_of); -    if(OP_LIKELY(ret>=0))return 0; -  } -  /*Don't auto-close the stream on failure.*/ -  _of->callbacks.close=NULL; -  op_clear(_of); -  return ret; -} - -OggOpusFile *op_test_callbacks(void *_source,const OpusFileCallbacks *_cb, - const unsigned char *_initial_data,size_t _initial_bytes,int *_error){ -  OggOpusFile *of; -  int          ret; -  of=(OggOpusFile *)_ogg_malloc(sizeof(*of)); -  ret=OP_EFAULT; -  if(OP_LIKELY(of!=NULL)){ -    ret=op_open1(of,_source,_cb,_initial_data,_initial_bytes); -    if(OP_LIKELY(ret>=0)){ -      if(_error!=NULL)*_error=0; -      return of; -    } -    /*Don't auto-close the stream on failure.*/ -    of->callbacks.close=NULL; -    op_clear(of); -    _ogg_free(of); -  } -  if(_error!=NULL)*_error=ret; -  return NULL; -} - -OggOpusFile *op_open_callbacks(void *_source,const OpusFileCallbacks *_cb, - const unsigned char *_initial_data,size_t _initial_bytes,int *_error){ -  OggOpusFile *of; -  of=op_test_callbacks(_source,_cb,_initial_data,_initial_bytes,_error); -  if(OP_LIKELY(of!=NULL)){ -    int ret; -    ret=op_open2(of); -    if(OP_LIKELY(ret>=0))return of; -    if(_error!=NULL)*_error=ret; -    _ogg_free(of); -  } -  return NULL; -} - -/*Convenience routine to clean up from failure for the open functions that -   create their own streams.*/ -static OggOpusFile *op_open_close_on_failure(void *_source, - const OpusFileCallbacks *_cb,int *_error){ -  OggOpusFile *of; -  if(OP_UNLIKELY(_source==NULL)){ -    if(_error!=NULL)*_error=OP_EFAULT; -    return NULL; -  } -  of=op_open_callbacks(_source,_cb,NULL,0,_error); -  if(OP_UNLIKELY(of==NULL))(*_cb->close)(_source); -  return of; -} - -OggOpusFile *op_open_file(const char *_path,int *_error){ -  OpusFileCallbacks cb; -  return op_open_close_on_failure(op_fopen(&cb,_path,"rb"),&cb,_error); -} - -OggOpusFile *op_open_memory(const unsigned char *_data,size_t _size, - int *_error){ -  OpusFileCallbacks cb; -  return op_open_close_on_failure(op_mem_stream_create(&cb,_data,_size),&cb, -   _error); -} - -/*Convenience routine to clean up from failure for the open functions that -   create their own streams.*/ -static OggOpusFile *op_test_close_on_failure(void *_source, - const OpusFileCallbacks *_cb,int *_error){ -  OggOpusFile *of; -  if(OP_UNLIKELY(_source==NULL)){ -    if(_error!=NULL)*_error=OP_EFAULT; -    return NULL; -  } -  of=op_test_callbacks(_source,_cb,NULL,0,_error); -  if(OP_UNLIKELY(of==NULL))(*_cb->close)(_source); -  return of; -} - -OggOpusFile *op_test_file(const char *_path,int *_error){ -  OpusFileCallbacks cb; -  return op_test_close_on_failure(op_fopen(&cb,_path,"rb"),&cb,_error); -} - -OggOpusFile *op_test_memory(const unsigned char *_data,size_t _size, - int *_error){ -  OpusFileCallbacks cb; -  return op_test_close_on_failure(op_mem_stream_create(&cb,_data,_size),&cb, -   _error); -} - -int op_test_open(OggOpusFile *_of){ -  int ret; -  if(OP_UNLIKELY(_of->ready_state!=OP_PARTOPEN))return OP_EINVAL; -  ret=op_open2(_of); -  /*op_open2() will clear this structure on failure. -    Reset its contents to prevent double-frees in op_free().*/ -  if(OP_UNLIKELY(ret<0))memset(_of,0,sizeof(*_of)); -  return ret; -} - -void op_free(OggOpusFile *_of){ -  if(OP_LIKELY(_of!=NULL)){ -    op_clear(_of); -    _ogg_free(_of); -  } -} - -int op_seekable(const OggOpusFile *_of){ -  return _of->seekable; -} - -int op_link_count(const OggOpusFile *_of){ -  return _of->nlinks; -} - -ogg_uint32_t op_serialno(const OggOpusFile *_of,int _li){ -  if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1; -  if(!_of->seekable)_li=0; -  return _of->links[_li<0?_of->cur_link:_li].serialno; -} - -int op_channel_count(const OggOpusFile *_of,int _li){ -  return op_head(_of,_li)->channel_count; -} - -opus_int64 op_raw_total(const OggOpusFile *_of,int _li){ -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED) -   ||OP_UNLIKELY(!_of->seekable) -   ||OP_UNLIKELY(_li>=_of->nlinks)){ -    return OP_EINVAL; -  } -  if(_li<0)return _of->end-_of->links[0].offset; -  return (_li+1>=_of->nlinks?_of->end:_of->links[_li+1].offset) -   -_of->links[_li].offset; -} - -ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li){ -  OggOpusLink *links; -  ogg_int64_t  diff=0; -  int          nlinks; -  nlinks=_of->nlinks; -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED) -   ||OP_UNLIKELY(!_of->seekable) -   ||OP_UNLIKELY(_li>=nlinks)){ -    return OP_EINVAL; -  } -  links=_of->links; -  /*We verify that the granule position differences are larger than the -     pre-skip and that the total duration does not overflow during link -     enumeration, so we don't have to check here.*/ -  if(_li<0){ -    ogg_int64_t pcm_total; -    int         li; -    pcm_total=0; -    for(li=0;li<nlinks;li++){ -      OP_ALWAYS_TRUE(!op_granpos_diff(&diff, -       links[li].pcm_end,links[li].pcm_start)); -      pcm_total+=diff-links[li].head.pre_skip; -    } -    return pcm_total; -  } -  OP_ALWAYS_TRUE(!op_granpos_diff(&diff, -   links[_li].pcm_end,links[_li].pcm_start)); -  return diff-links[_li].head.pre_skip; -} - -const OpusHead *op_head(const OggOpusFile *_of,int _li){ -  if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1; -  if(!_of->seekable)_li=0; -  return &_of->links[_li<0?_of->cur_link:_li].head; -} - -const OpusTags *op_tags(const OggOpusFile *_of,int _li){ -  if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1; -  if(!_of->seekable){ -    if(_of->ready_state<OP_STREAMSET&&_of->ready_state!=OP_PARTOPEN){ -      return NULL; -    } -    _li=0; -  } -  else if(_li<0)_li=_of->ready_state>=OP_STREAMSET?_of->cur_link:0; -  return &_of->links[_li].tags; -} - -int op_current_link(const OggOpusFile *_of){ -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL; -  return _of->cur_link; -} - -/*Compute an average bitrate given a byte and sample count. -  Return: The bitrate in bits per second.*/ -static opus_int32 op_calc_bitrate(opus_int64 _bytes,ogg_int64_t _samples){ -  /*These rates are absurd, but let's handle them anyway.*/ -  if(OP_UNLIKELY(_bytes>(OP_INT64_MAX-(_samples>>1))/(48000*8))){ -    ogg_int64_t den; -    if(OP_UNLIKELY(_bytes/(OP_INT32_MAX/(48000*8))>=_samples)){ -      return OP_INT32_MAX; -    } -    den=_samples/(48000*8); -    return (opus_int32)((_bytes+(den>>1))/den); -  } -  if(OP_UNLIKELY(_samples<=0))return OP_INT32_MAX; -  /*This can't actually overflow in normal operation: even with a pre-skip of -     545 2.5 ms frames with 8 streams running at 1282*8+1 bytes per packet -     (1275 byte frames + Opus framing overhead + Ogg lacing values), that all -     produce a single sample of decoded output, we still don't top 45 Mbps. -    The only way to get bitrates larger than that is with excessive Opus -     padding, more encoded streams than output channels, or lots and lots of -     Ogg pages with no packets on them.*/ -  return (opus_int32)OP_MIN((_bytes*48000*8+(_samples>>1))/_samples, -   OP_INT32_MAX); -} - -opus_int32 op_bitrate(const OggOpusFile *_of,int _li){ -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED)||OP_UNLIKELY(!_of->seekable) -   ||OP_UNLIKELY(_li>=_of->nlinks)){ -    return OP_EINVAL; -  } -  return op_calc_bitrate(op_raw_total(_of,_li),op_pcm_total(_of,_li)); -} - -opus_int32 op_bitrate_instant(OggOpusFile *_of){ -  ogg_int64_t samples_tracked; -  opus_int32  ret; -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL; -  samples_tracked=_of->samples_tracked; -  if(OP_UNLIKELY(samples_tracked==0))return OP_FALSE; -  ret=op_calc_bitrate(_of->bytes_tracked,samples_tracked); -  _of->bytes_tracked=0; -  _of->samples_tracked=0; -  return ret; -} - -/*Fetch and process a page. -  This handles the case where we're at a bitstream boundary and dumps the -   decoding machine. -  If the decoding machine is unloaded, it loads it. -  It also keeps prev_packet_gp up to date (seek and read both use this; seek -   uses a special hack with _readp). -  Return: <0) Error, OP_HOLE (lost packet), or OP_EOF. -           0) Need more data (only if _readp==0). -           1) Got at least one audio data packet.*/ -static int op_fetch_and_process_page(OggOpusFile *_of, - ogg_page *_og,opus_int64 _page_pos,int _readp,int _spanp,int _ignore_holes){ -  OggOpusLink  *links; -  ogg_uint32_t  cur_serialno; -  int           seekable; -  int           cur_link; -  int           ret; -  /*We shouldn't get here if we have unprocessed packets.*/ -  OP_ASSERT(_of->ready_state<OP_INITSET||_of->op_pos>=_of->op_count); -  if(!_readp)return 0; -  seekable=_of->seekable; -  links=_of->links; -  cur_link=seekable?_of->cur_link:0; -  cur_serialno=links[cur_link].serialno; -  /*Handle one page.*/ -  for(;;){ -    ogg_page og; -    OP_ASSERT(_of->ready_state>=OP_OPENED); -    /*This loop is not strictly necessary, but there's no sense in doing the -       extra checks of the larger loop for the common case in a multiplexed -       bistream where the page is simply part of a different logical -       bitstream.*/ -    do{ -      /*If we were given a page to use, use it.*/ -      if(_og!=NULL){ -        *&og=*_og; -        _og=NULL; -      } -      /*Keep reading until we get a page with the correct serialno.*/ -      else _page_pos=op_get_next_page(_of,&og,_of->end); -      /*EOF: Leave uninitialized.*/ -      if(_page_pos<0)return _page_pos<OP_FALSE?(int)_page_pos:OP_EOF; -      if(OP_LIKELY(_of->ready_state>=OP_STREAMSET)){ -        if(cur_serialno!=(ogg_uint32_t)ogg_page_serialno(&og)){ -          /*Two possibilities: -             1) Another stream is multiplexed into this logical section, or*/ -          if(OP_LIKELY(!ogg_page_bos(&og)))continue; -          /* 2) Our decoding just traversed a bitstream boundary.*/ -          if(!_spanp)return OP_EOF; -          if(OP_LIKELY(_of->ready_state>=OP_INITSET))op_decode_clear(_of); -          break; -        } -      } -      /*Bitrate tracking: add the header's bytes here. -        The body bytes are counted when we consume the packets.*/ -      _of->bytes_tracked+=og.header_len; -    } -    while(0); -    /*Do we need to load a new machine before submitting the page? -      This is different in the seekable and non-seekable cases. -      In the seekable case, we already have all the header information loaded -       and cached. -      We just initialize the machine with it and continue on our merry way. -      In the non-seekable (streaming) case, we'll only be at a boundary if we -       just left the previous logical bitstream, and we're now nominally at the -       header of the next bitstream.*/ -    if(OP_UNLIKELY(_of->ready_state<OP_STREAMSET)){ -      if(seekable){ -        ogg_uint32_t serialno; -        int          nlinks; -        int          li; -        serialno=ogg_page_serialno(&og); -        /*Match the serialno to bitstream section. -          We use this rather than offset positions to avoid problems near -           logical bitstream boundaries.*/ -        nlinks=_of->nlinks; -        for(li=0;li<nlinks&&links[li].serialno!=serialno;li++); -        /*Not a desired Opus bitstream section. -          Keep trying.*/ -        if(li>=nlinks)continue; -        cur_serialno=serialno; -        _of->cur_link=cur_link=li; -        ogg_stream_reset_serialno(&_of->os,serialno); -        _of->ready_state=OP_STREAMSET; -        /*If we're at the start of this link, initialize the granule position -           and pre-skip tracking.*/ -        if(_page_pos<=links[cur_link].data_offset){ -          _of->prev_packet_gp=links[cur_link].pcm_start; -          _of->cur_discard_count=links[cur_link].head.pre_skip; -          /*Ignore a hole at the start of a new link (this is common for -             streams joined in the middle) or after seeking.*/ -          _ignore_holes=1; -        } -      } -      else{ -        do{ -          /*We're streaming. -            Fetch the two header packets, build the info struct.*/ -          ret=op_fetch_headers(_of,&links[0].head,&links[0].tags, -           NULL,NULL,NULL,&og); -          if(OP_UNLIKELY(ret<0))return ret; -          /*op_find_initial_pcm_offset() will suppress any initial hole for us, -             so no need to set _ignore_holes.*/ -          ret=op_find_initial_pcm_offset(_of,links,&og); -          if(OP_UNLIKELY(ret<0))return ret; -          _of->links[0].serialno=cur_serialno=_of->os.serialno; -          _of->cur_link++; -        } -        /*If the link was empty, keep going, because we already have the -           BOS page of the next one in og.*/ -        while(OP_UNLIKELY(ret>0)); -        /*If we didn't get any packets out of op_find_initial_pcm_offset(), -           keep going (this is possible if end-trimming trimmed them all).*/ -        if(_of->op_count<=0)continue; -        /*Otherwise, we're done.*/ -        ret=op_make_decode_ready(_of); -        if(OP_UNLIKELY(ret<0))return ret; -        return 1; -      } -    } -    /*The buffered page is the data we want, and we're ready for it. -      Add it to the stream state.*/ -    if(OP_UNLIKELY(_of->ready_state==OP_STREAMSET)){ -      ret=op_make_decode_ready(_of); -      if(OP_UNLIKELY(ret<0))return ret; -    } -    /*Extract all the packets from the current page.*/ -    ogg_stream_pagein(&_of->os,&og); -    if(OP_LIKELY(_of->ready_state>=OP_INITSET)){ -      opus_int32 total_duration; -      int        durations[255]; -      int        op_count; -      total_duration=op_collect_audio_packets(_of,durations); -      if(OP_UNLIKELY(total_duration<0)){ -        /*Drain the packets from the page anyway.*/ -        total_duration=op_collect_audio_packets(_of,durations); -        OP_ASSERT(total_duration>=0); -        /*Report holes to the caller.*/ -        if(!_ignore_holes)return OP_HOLE; -      } -      op_count=_of->op_count; -      /*If we found at least one audio data packet, compute per-packet granule -         positions for them.*/ -      if(op_count>0){ -        ogg_int64_t diff; -        ogg_int64_t prev_packet_gp; -        ogg_int64_t cur_packet_gp; -        ogg_int64_t cur_page_gp; -        int         cur_page_eos; -        int         pi; -        cur_page_gp=_of->op[op_count-1].granulepos; -        cur_page_eos=_of->op[op_count-1].e_o_s; -        prev_packet_gp=_of->prev_packet_gp; -        if(OP_UNLIKELY(prev_packet_gp==-1)){ -          opus_int32 cur_discard_count; -          /*This is the first call after a raw seek. -            Try to reconstruct prev_packet_gp from scratch.*/ -          OP_ASSERT(seekable); -          if(OP_UNLIKELY(cur_page_eos)){ -            /*If the first page we hit after our seek was the EOS page, and -               we didn't start from data_offset or before, we don't have -               enough information to do end-trimming. -              Proceed to the next link, rather than risk playing back some -               samples that shouldn't have been played.*/ -            _of->op_count=0; -            continue; -          } -          /*By default discard 80 ms of data after a seek, unless we seek -             into the pre-skip region.*/ -          cur_discard_count=80*48; -          cur_page_gp=_of->op[op_count-1].granulepos; -          /*Try to initialize prev_packet_gp. -            If the current page had packets but didn't have a granule -             position, or the granule position it had was too small (both -             illegal), just use the starting granule position for the link.*/ -          prev_packet_gp=links[cur_link].pcm_start; -          if(OP_LIKELY(cur_page_gp!=-1)){ -            op_granpos_add(&prev_packet_gp,cur_page_gp,-total_duration); -          } -          if(OP_LIKELY(!op_granpos_diff(&diff, -           prev_packet_gp,links[cur_link].pcm_start))){ -            opus_int32 pre_skip; -            /*If we start at the beginning of the pre-skip region, or we're -               at least 80 ms from the end of the pre-skip region, we discard -               to the end of the pre-skip region. -              Otherwise, we still use the 80 ms default, which will discard -               past the end of the pre-skip region.*/ -            pre_skip=links[cur_link].head.pre_skip; -            if(diff>=0&&diff<=OP_MAX(0,pre_skip-80*48)){ -              cur_discard_count=pre_skip-(int)diff; -            } -          } -          _of->cur_discard_count=cur_discard_count; -        } -        if(OP_UNLIKELY(cur_page_gp==-1)){ -          /*This page had completed packets but didn't have a valid granule -             position. -            This is illegal, but we'll try to handle it by continuing to count -             forwards from the previous page.*/ -          if(op_granpos_add(&cur_page_gp,prev_packet_gp,total_duration)<0){ -            /*The timestamp for this page overflowed.*/ -            cur_page_gp=links[cur_link].pcm_end; -          } -        } -        /*If we hit the last page, handle end-trimming.*/ -        if(OP_UNLIKELY(cur_page_eos) -         &&OP_LIKELY(!op_granpos_diff(&diff,cur_page_gp,prev_packet_gp)) -         &&OP_LIKELY(diff<total_duration)){ -          cur_packet_gp=prev_packet_gp; -          for(pi=0;pi<op_count;pi++){ -            diff=durations[pi]-diff; -            /*If we have samples to trim...*/ -            if(diff>0){ -              /*If we trimmed the entire packet, stop (the spec says encoders -                 shouldn't do this, but we support it anyway).*/ -              if(OP_UNLIKELY(diff>durations[pi]))break; -              cur_packet_gp=cur_page_gp; -              /*Move the EOS flag to this packet, if necessary, so we'll trim -                 the samples during decode.*/ -              _of->op[pi].e_o_s=1; -            } -            else{ -              /*Update the granule position as normal.*/ -              OP_ALWAYS_TRUE(!op_granpos_add(&cur_packet_gp, -               cur_packet_gp,durations[pi])); -            } -            _of->op[pi].granulepos=cur_packet_gp; -            OP_ALWAYS_TRUE(!op_granpos_diff(&diff,cur_page_gp,cur_packet_gp)); -          } -        } -        else{ -          /*Propagate timestamps to earlier packets. -            op_granpos_add(&prev_packet_gp,prev_packet_gp,total_duration) -             should succeed and give prev_packet_gp==cur_page_gp. -            But we don't bother to check that, as there isn't much we can do -             if it's not true. -            The only thing we guarantee is that the start and end granule -             positions of the packets are valid, and that they are monotonic -             within a page. -            They might be completely out of range for this link (we'll check -             that elsewhere), or non-monotonic between pages.*/ -          if(OP_UNLIKELY(op_granpos_add(&prev_packet_gp, -           cur_page_gp,-total_duration)<0)){ -            /*The starting timestamp for the first packet on this page -               underflowed. -              This is illegal, but we ignore it.*/ -            prev_packet_gp=0; -          } -          for(pi=0;pi<op_count;pi++){ -            if(OP_UNLIKELY(op_granpos_add(&cur_packet_gp, -             cur_page_gp,-total_duration)<0)){ -              /*The start timestamp for this packet underflowed. -                This is illegal, but we ignore it.*/ -              cur_packet_gp=0; -            } -            total_duration-=durations[pi]; -            OP_ASSERT(total_duration>=0); -            OP_ALWAYS_TRUE(!op_granpos_add(&cur_packet_gp, -             cur_packet_gp,durations[pi])); -            _of->op[pi].granulepos=cur_packet_gp; -          } -          OP_ASSERT(total_duration==0); -        } -        _of->prev_packet_gp=prev_packet_gp; -        _of->op_count=pi; -        /*If end-trimming didn't trim all the packets, we're done.*/ -        if(OP_LIKELY(pi>0))return 1; -      } -    } -  } -} - -int op_raw_seek(OggOpusFile *_of,opus_int64 _pos){ -  int ret; -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL; -  /*Don't dump the decoder state if we can't seek.*/ -  if(OP_UNLIKELY(!_of->seekable))return OP_ENOSEEK; -  if(OP_UNLIKELY(_pos<0)||OP_UNLIKELY(_pos>_of->end))return OP_EINVAL; -  /*Clear out any buffered, decoded data.*/ -  op_decode_clear(_of); -  _of->bytes_tracked=0; -  _of->samples_tracked=0; -  ret=op_seek_helper(_of,_pos); -  if(OP_UNLIKELY(ret<0))return OP_EREAD; -  ret=op_fetch_and_process_page(_of,NULL,-1,1,1,1); -  /*If we hit EOF, op_fetch_and_process_page() leaves us uninitialized. -    Instead, jump to the end.*/ -  if(ret==OP_EOF){ -    int cur_link; -    op_decode_clear(_of); -    cur_link=_of->nlinks-1; -    _of->cur_link=cur_link; -    _of->prev_packet_gp=_of->links[cur_link].pcm_end; -    _of->cur_discard_count=0; -    ret=0; -  } -  else if(ret>0)ret=0; -  return ret; -} - -/*Convert a PCM offset relative to the start of the whole stream to a granule -   position in an individual link.*/ -static ogg_int64_t op_get_granulepos(const OggOpusFile *_of, - ogg_int64_t _pcm_offset,int *_li){ -  const OggOpusLink *links; -  ogg_int64_t        duration=0; -  int                nlinks; -  int                li; -  OP_ASSERT(_pcm_offset>=0); -  nlinks=_of->nlinks; -  links=_of->links; -  for(li=0;OP_LIKELY(li<nlinks);li++){ -    ogg_int64_t pcm_start; -    opus_int32  pre_skip; -    pcm_start=links[li].pcm_start; -    pre_skip=links[li].head.pre_skip; -    OP_ALWAYS_TRUE(!op_granpos_diff(&duration,links[li].pcm_end,pcm_start)); -    duration-=pre_skip; -    if(_pcm_offset<duration){ -      _pcm_offset+=pre_skip; -      if(OP_UNLIKELY(pcm_start>OP_INT64_MAX-_pcm_offset)){ -        /*Adding this amount to the granule position would overflow the positive -           half of its 64-bit range. -          Since signed overflow is undefined in C, do it in a way the compiler -           isn't allowed to screw up.*/ -        _pcm_offset-=OP_INT64_MAX-pcm_start+1; -        pcm_start=OP_INT64_MIN; -      } -      pcm_start+=_pcm_offset; -      *_li=li; -      return pcm_start; -    } -    _pcm_offset-=duration; -  } -  return -1; -} - -/*This controls how close the target has to be to use the current stream -   position to subdivide the initial range. -  Two minutes seems to be a good default.*/ -#define OP_CUR_TIME_THRESH (120*48*(opus_int32)1000) - -/*Note: The OP_SMALL_FOOTPRINT #define doesn't (currently) save much code size, -   but it's meant to serve as documentation for portions of the seeking -   algorithm that are purely optional, to aid others learning from/porting this -   code to other contexts.*/ -/*#define OP_SMALL_FOOTPRINT (1)*/ - -/*Search within link _li for the page with the highest granule position -   preceding (or equal to) _target_gp. -  There is a danger here: missing pages or incorrect frame number information -   in the bitstream could make our task impossible. -  Account for that (and report it as an error condition).*/ -static int op_pcm_seek_page(OggOpusFile *_of, - ogg_int64_t _target_gp,int _li){ -  const OggOpusLink *link; -  ogg_page           og; -  ogg_int64_t        pcm_pre_skip=0; -  ogg_int64_t        pcm_start; -  ogg_int64_t        pcm_end; -  ogg_int64_t        best_gp; -  ogg_int64_t        diff=0; -  ogg_uint32_t       serialno; -  opus_int32         pre_skip; -  opus_int64         begin; -  opus_int64         end; -  opus_int64         boundary; -  opus_int64         best; -  opus_int64         page_offset; -  opus_int64         d0; -  opus_int64         d1; -  opus_int64         d2; -  int                force_bisect; -  int                ret; -  _of->bytes_tracked=0; -  _of->samples_tracked=0; -  link=_of->links+_li; -  best_gp=pcm_start=link->pcm_start; -  pcm_end=link->pcm_end; -  serialno=link->serialno; -  best=begin=link->data_offset; -  page_offset=-1; -  /*We discard the first 80 ms of data after a seek, so seek back that much -     farther. -    If we can't, simply seek to the beginning of the link.*/ -  if(OP_UNLIKELY(op_granpos_add(&_target_gp,_target_gp,-80*48)<0) -   ||OP_UNLIKELY(op_granpos_cmp(_target_gp,pcm_start)<0)){ -    _target_gp=pcm_start; -  } -  /*Special case seeking to the start of the link.*/ -  pre_skip=link->head.pre_skip; -  OP_ALWAYS_TRUE(!op_granpos_add(&pcm_pre_skip,pcm_start,pre_skip)); -  if(op_granpos_cmp(_target_gp,pcm_pre_skip)<0)end=boundary=begin; -  else{ -    end=boundary=link->end_offset; -#if !defined(OP_SMALL_FOOTPRINT) -    /*If we were decoding from this link, we can narrow the range a bit.*/ -    if(_li==_of->cur_link&&_of->ready_state>=OP_INITSET){ -      opus_int64 offset; -      int        op_count; -      op_count=_of->op_count; -      /*The only way the offset can be invalid _and_ we can fail the granule -         position checks below is if someone changed the contents of the last -         page since we read it. -        We'd be within our rights to just return OP_EBADLINK in that case, but -         we'll simply ignore the current position instead.*/ -      offset=_of->offset; -      if(op_count>0&&OP_LIKELY(offset<=end)){ -        ogg_int64_t gp; -        /*Make sure the timestamp is valid. -          The granule position might be -1 if we collected the packets from a -           page without a granule position after reporting a hole.*/ -        gp=_of->op[op_count-1].granulepos; -        if(OP_LIKELY(gp!=-1)&&OP_LIKELY(op_granpos_cmp(pcm_start,gp)<0) -         &&OP_LIKELY(op_granpos_cmp(pcm_end,gp)>0)){ -          OP_ALWAYS_TRUE(!op_granpos_diff(&diff,gp,_target_gp)); -          /*We only actually use the current time if either -            a) We can cut off at least half the range, or -            b) We're seeking sufficiently close to the current position that -                it's likely to be informative. -            Otherwise it appears using the whole link range to estimate the -             first seek location gives better results, on average.*/ -          if(diff<0){ -            OP_ASSERT(offset>=begin); -            if(offset-begin>=end-begin>>1||diff>-OP_CUR_TIME_THRESH){ -              best=begin=offset; -              best_gp=pcm_start=gp; -            } -          } -          else{ -            ogg_int64_t prev_page_gp=0; -            /*We might get lucky and already have the packet with the target -               buffered. -              Worth checking. -              For very small files (with all of the data in a single page, -               generally 1 second or less), we can loop them continuously -               without seeking at all.*/ -            OP_ALWAYS_TRUE(!op_granpos_add(&prev_page_gp,_of->op[0].granulepos, -             op_get_packet_duration(_of->op[0].packet,_of->op[0].bytes))); -            if(op_granpos_cmp(prev_page_gp,_target_gp)<=0){ -              /*Don't call op_decode_clear(), because it will dump our -                 packets.*/ -              _of->op_pos=0; -              _of->od_buffer_size=0; -              _of->prev_packet_gp=prev_page_gp; -              _of->ready_state=OP_STREAMSET; -              return op_make_decode_ready(_of); -            } -            /*No such luck. -              Check if we can cut off at least half the range, though.*/ -            if(offset-begin<=end-begin>>1||diff<OP_CUR_TIME_THRESH){ -              /*We really want the page start here, but this will do.*/ -              end=boundary=offset; -              pcm_end=gp; -            } -          } -        } -      } -    } -#endif -  } -  /*This code was originally based on the "new search algorithm by HB (Nicholas -     Vinen)" from libvorbisfile. -    It has been modified substantially since.*/ -  op_decode_clear(_of); -  /*Initialize the interval size history.*/ -  d2=d1=d0=end-begin; -  force_bisect=0; -  while(begin<end){ -    opus_int64 bisect; -    opus_int64 next_boundary; -    opus_int32 chunk_size; -    if(end-begin<OP_CHUNK_SIZE)bisect=begin; -    else{ -      /*Update the interval size history.*/ -      d0=d1>>1; -      d1=d2>>1; -      d2=end-begin>>1; -      if(force_bisect)bisect=begin+(end-begin>>1); -      else{ -        ogg_int64_t diff2=0; -        OP_ALWAYS_TRUE(!op_granpos_diff(&diff,_target_gp,pcm_start)); -        OP_ALWAYS_TRUE(!op_granpos_diff(&diff2,pcm_end,pcm_start)); -        /*Take a (pretty decent) guess.*/ -        bisect=begin+op_rescale64(diff,diff2,end-begin)-OP_CHUNK_SIZE; -      } -      if(bisect-OP_CHUNK_SIZE<begin)bisect=begin; -      force_bisect=0; -    } -    if(bisect!=_of->offset){ -      page_offset=-1; -      ret=op_seek_helper(_of,bisect); -      if(OP_UNLIKELY(ret<0))return ret; -    } -    chunk_size=OP_CHUNK_SIZE; -    next_boundary=boundary; -    while(begin<end){ -      page_offset=op_get_next_page(_of,&og,boundary); -      if(page_offset<0){ -        if(page_offset<OP_FALSE)return (int)page_offset; -        /*There are no more pages in our interval from our stream with a valid -           timestamp that start at position bisect or later.*/ -        /*If we scanned the whole interval, we're done.*/ -        if(bisect<=begin+1)end=begin; -        else{ -          /*Otherwise, back up one chunk.*/ -          bisect=OP_MAX(bisect-chunk_size,begin); -          ret=op_seek_helper(_of,bisect); -          if(OP_UNLIKELY(ret<0))return ret; -          /*Bump up the chunk size.*/ -          chunk_size=OP_MIN(2*chunk_size,OP_CHUNK_SIZE_MAX); -          /*If we did find a page from another stream or without a timestamp, -             don't read past it.*/ -          boundary=next_boundary; -        } -      } -      else{ -        ogg_int64_t gp; -        /*Save the offset of the first page we found after the seek, regardless -           of the stream it came from or whether or not it has a timestamp.*/ -        next_boundary=OP_MIN(page_offset,next_boundary); -        if(serialno!=(ogg_uint32_t)ogg_page_serialno(&og))continue; -        gp=ogg_page_granulepos(&og); -        if(gp==-1)continue; -        if(op_granpos_cmp(gp,_target_gp)<0){ -          /*We found a page that ends before our target. -            Advance to the raw offset of the next page.*/ -          begin=_of->offset; -          if(OP_UNLIKELY(op_granpos_cmp(pcm_start,gp)>0) -           ||OP_UNLIKELY(op_granpos_cmp(pcm_end,gp)<0)){ -            /*Don't let pcm_start get out of range! -              That could happen with an invalid timestamp.*/ -            break; -          } -          /*Save the byte offset of the end of the page with this granule -             position.*/ -          best=begin; -          best_gp=pcm_start=gp; -          OP_ALWAYS_TRUE(!op_granpos_diff(&diff,_target_gp,pcm_start)); -          /*If we're more than a second away from our target, break out and -             do another bisection.*/ -          if(diff>48000)break; -          /*Otherwise, keep scanning forward (do NOT use begin+1).*/ -          bisect=begin; -        } -        else{ -          /*We found a page that ends after our target.*/ -          /*If we scanned the whole interval before we found it, we're done.*/ -          if(bisect<=begin+1)end=begin; -          else{ -            end=bisect; -            /*In later iterations, don't read past the first page we found.*/ -            boundary=next_boundary; -            /*If we're not making much progress shrinking the interval size, -               start forcing straight bisection to limit the worst case.*/ -            force_bisect=end-begin>d0*2; -            /*Don't let pcm_end get out of range! -              That could happen with an invalid timestamp.*/ -            if(OP_LIKELY(op_granpos_cmp(pcm_end,gp)>0) -             &&OP_LIKELY(op_granpos_cmp(pcm_start,gp)<=0)){ -              pcm_end=gp; -            } -            break; -          } -        } -      } -    } -  } -  /*Found our page. -    Seek to the end of it and update prev_packet_gp. -    Our caller will set cur_discard_count. -    This is an easier case than op_raw_seek(), as we don't need to keep any -     packets from the page we found.*/ -  /*Seek, if necessary.*/ -  if(best!=page_offset){ -    page_offset=-1; -    ret=op_seek_helper(_of,best); -    if(OP_UNLIKELY(ret<0))return ret; -  } -  OP_ASSERT(op_granpos_cmp(best_gp,pcm_start)>=0); -  _of->cur_link=_li; -  _of->ready_state=OP_STREAMSET; -  _of->prev_packet_gp=best_gp; -  ogg_stream_reset_serialno(&_of->os,serialno); -  ret=op_fetch_and_process_page(_of,page_offset<0?NULL:&og,page_offset,1,0,1); -  if(OP_UNLIKELY(ret<=0))return OP_EBADLINK; -  /*Verify result.*/ -  if(OP_UNLIKELY(op_granpos_cmp(_of->prev_packet_gp,_target_gp)>0)){ -    return OP_EBADLINK; -  } -  return 0; -} - -int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){ -  const OggOpusLink *link; -  ogg_int64_t        pcm_start; -  ogg_int64_t        target_gp; -  ogg_int64_t        prev_packet_gp; -  ogg_int64_t        skip; -  ogg_int64_t        diff; -  int                op_count; -  int                op_pos; -  int                ret; -  int                li; -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL; -  if(OP_UNLIKELY(!_of->seekable))return OP_ENOSEEK; -  if(OP_UNLIKELY(_pcm_offset<0))return OP_EINVAL; -  target_gp=op_get_granulepos(_of,_pcm_offset,&li); -  if(OP_UNLIKELY(target_gp==-1))return OP_EINVAL; -  link=_of->links+li; -  pcm_start=link->pcm_start; -  OP_ALWAYS_TRUE(!op_granpos_diff(&_pcm_offset,target_gp,pcm_start)); -#if !defined(OP_SMALL_FOOTPRINT) -  /*For small (90 ms or less) forward seeks within the same link, just decode -     forward. -    This also optimizes the case of seeking to the current position.*/ -  if(li==_of->cur_link&&_of->ready_state>=OP_INITSET){ -    ogg_int64_t gp; -    gp=_of->prev_packet_gp; -    if(OP_LIKELY(gp!=-1)){ -      int nbuffered; -      nbuffered=OP_MAX(_of->od_buffer_size-_of->od_buffer_pos,0); -      OP_ALWAYS_TRUE(!op_granpos_add(&gp,gp,-nbuffered)); -      /*We do _not_ add cur_discard_count to gp. -        Otherwise the total amount to discard could grow without bound, and it -         would be better just to do a full seek.*/ -      if(OP_LIKELY(!op_granpos_diff(&diff,gp,pcm_start))){ -        ogg_int64_t discard_count; -        discard_count=_pcm_offset-diff; -        /*We use a threshold of 90 ms instead of 80, since 80 ms is the -           _minimum_ we would have discarded after a full seek. -          Assuming 20 ms frames (the default), we'd discard 90 ms on average.*/ -        if(discard_count>=0&&OP_UNLIKELY(discard_count<90*48)){ -          _of->cur_discard_count=(opus_int32)discard_count; -          return 0; -        } -      } -    } -  } -#endif -  ret=op_pcm_seek_page(_of,target_gp,li); -  if(OP_UNLIKELY(ret<0))return ret; -  /*Now skip samples until we actually get to our target.*/ -  /*Figure out where we should skip to.*/ -  if(_pcm_offset<=link->head.pre_skip)skip=0; -  else skip=OP_MAX(_pcm_offset-80*48,0); -  OP_ASSERT(_pcm_offset-skip>=0); -  OP_ASSERT(_pcm_offset-skip<OP_INT32_MAX-120*48); -  /*Skip packets until we find one with samples past our skip target.*/ -  for(;;){ -    op_count=_of->op_count; -    prev_packet_gp=_of->prev_packet_gp; -    for(op_pos=_of->op_pos;op_pos<op_count;op_pos++){ -      ogg_int64_t cur_packet_gp; -      cur_packet_gp=_of->op[op_pos].granulepos; -      if(OP_LIKELY(!op_granpos_diff(&diff,cur_packet_gp,pcm_start)) -       &&diff>skip){ -        break; -      } -      prev_packet_gp=cur_packet_gp; -    } -    _of->prev_packet_gp=prev_packet_gp; -    _of->op_pos=op_pos; -    if(op_pos<op_count)break; -    /*We skipped all the packets on this page. -      Fetch another.*/ -    ret=op_fetch_and_process_page(_of,NULL,-1,1,0,1); -    if(OP_UNLIKELY(ret<=0))return OP_EBADLINK; -  } -  OP_ALWAYS_TRUE(!op_granpos_diff(&diff,prev_packet_gp,pcm_start)); -  /*We skipped too far. -    Either the timestamps were illegal or there was a hole in the data.*/ -  if(diff>skip)return OP_EBADLINK; -  OP_ASSERT(_pcm_offset-diff<OP_INT32_MAX); -  /*TODO: If there are further holes/illegal timestamps, we still won't decode -     to the correct sample. -    However, at least op_pcm_tell() will report the correct value immediately -     after returning.*/ -  _of->cur_discard_count=(opus_int32)(_pcm_offset-diff); -  return 0; -} - -opus_int64 op_raw_tell(const OggOpusFile *_of){ -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL; -  return _of->offset; -} - -/*Convert a granule position from a given link to a PCM offset relative to the -   start of the whole stream. -  For unseekable sources, this gets reset to 0 at the beginning of each link.*/ -static ogg_int64_t op_get_pcm_offset(const OggOpusFile *_of, - ogg_int64_t _gp,int _li){ -  const OggOpusLink *links; -  ogg_int64_t        pcm_offset; -  ogg_int64_t        delta=0; -  int                li; -  links=_of->links; -  pcm_offset=0; -  OP_ASSERT(_li<_of->nlinks); -  for(li=0;li<_li;li++){ -    OP_ALWAYS_TRUE(!op_granpos_diff(&delta, -     links[li].pcm_end,links[li].pcm_start)); -    delta-=links[li].head.pre_skip; -    pcm_offset+=delta; -  } -  OP_ASSERT(_li>=0); -  if(_of->seekable&&OP_UNLIKELY(op_granpos_cmp(_gp,links[_li].pcm_end)>0)){ -    _gp=links[_li].pcm_end; -  } -  if(OP_LIKELY(op_granpos_cmp(_gp,links[_li].pcm_start)>0)){ -    if(OP_UNLIKELY(op_granpos_diff(&delta,_gp,links[_li].pcm_start)<0)){ -      /*This means an unseekable stream claimed to have a page from more than -         2 billion days after we joined.*/ -      OP_ASSERT(!_of->seekable); -      return OP_INT64_MAX; -    } -    if(delta<links[_li].head.pre_skip)delta=0; -    else delta-=links[_li].head.pre_skip; -    /*In the seekable case, _gp was limited by pcm_end. -      In the unseekable case, pcm_offset should be 0.*/ -    OP_ASSERT(pcm_offset<=OP_INT64_MAX-delta); -    pcm_offset+=delta; -  } -  return pcm_offset; -} - -ogg_int64_t op_pcm_tell(const OggOpusFile *_of){ -  ogg_int64_t gp; -  int         nbuffered; -  int         li; -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL; -  gp=_of->prev_packet_gp; -  if(gp==-1)return 0; -  nbuffered=OP_MAX(_of->od_buffer_size-_of->od_buffer_pos,0); -  OP_ALWAYS_TRUE(!op_granpos_add(&gp,gp,-nbuffered)); -  li=_of->seekable?_of->cur_link:0; -  if(op_granpos_add(&gp,gp,_of->cur_discard_count)<0){ -    gp=_of->links[li].pcm_end; -  } -  return op_get_pcm_offset(_of,gp,li); -} - -void op_set_decode_callback(OggOpusFile *_of, - op_decode_cb_func _decode_cb,void *_ctx){ -  _of->decode_cb=_decode_cb; -  _of->decode_cb_ctx=_ctx; -} - -int op_set_gain_offset(OggOpusFile *_of, - int _gain_type,opus_int32 _gain_offset_q8){ -  if(_gain_type!=OP_HEADER_GAIN&&_gain_type!=OP_TRACK_GAIN -   &&_gain_type!=OP_ABSOLUTE_GAIN){ -    return OP_EINVAL; -  } -  _of->gain_type=_gain_type; -  /*The sum of header gain and track gain lies in the range [-65536,65534]. -    These bounds allow the offset to set the final value to anywhere in the -     range [-32768,32767], which is what we'll clamp it to before applying.*/ -  _of->gain_offset_q8=OP_CLAMP(-98302,_gain_offset_q8,98303); -  op_update_gain(_of); -  return 0; -} - -void op_set_dither_enabled(OggOpusFile *_of,int _enabled){ -#if !defined(OP_FIXED_POINT) -  _of->dither_disabled=!_enabled; -  if(!_enabled)_of->dither_mute=65; -#endif -} - -/*Allocate the decoder scratch buffer. -  This is done lazily, since if the user provides large enough buffers, we'll -   never need it.*/ -static int op_init_buffer(OggOpusFile *_of){ -  int nchannels_max; -  if(_of->seekable){ -    const OggOpusLink *links; -    int                nlinks; -    int                li; -    links=_of->links; -    nlinks=_of->nlinks; -    nchannels_max=1; -    for(li=0;li<nlinks;li++){ -      nchannels_max=OP_MAX(nchannels_max,links[li].head.channel_count); -    } -  } -  else nchannels_max=OP_NCHANNELS_MAX; -  _of->od_buffer=(op_sample *)_ogg_malloc( -   sizeof(*_of->od_buffer)*nchannels_max*120*48); -  if(_of->od_buffer==NULL)return OP_EFAULT; -  return 0; -} - -/*Decode a single packet into the target buffer.*/ -static int op_decode(OggOpusFile *_of,op_sample *_pcm, - const ogg_packet *_op,int _nsamples,int _nchannels){ -  int ret; -  /*First we try using the application-provided decode callback.*/ -  if(_of->decode_cb!=NULL){ -#if defined(OP_FIXED_POINT) -    ret=(*_of->decode_cb)(_of->decode_cb_ctx,_of->od,_pcm,_op, -     _nsamples,_nchannels,OP_DEC_FORMAT_SHORT,_of->cur_link); -#else -    ret=(*_of->decode_cb)(_of->decode_cb_ctx,_of->od,_pcm,_op, -     _nsamples,_nchannels,OP_DEC_FORMAT_FLOAT,_of->cur_link); -#endif -  } -  else ret=OP_DEC_USE_DEFAULT; -  /*If the application didn't want to handle decoding, do it ourselves.*/ -  if(ret==OP_DEC_USE_DEFAULT){ -#if defined(OP_FIXED_POINT) -    ret=opus_multistream_decode(_of->od, -     _op->packet,_op->bytes,_pcm,_nsamples,0); -#else -    ret=opus_multistream_decode_float(_of->od, -     _op->packet,_op->bytes,_pcm,_nsamples,0); -#endif -    OP_ASSERT(ret<0||ret==_nsamples); -  } -  /*If the application returned a positive value other than 0 or -     OP_DEC_USE_DEFAULT, fail.*/ -  else if(OP_UNLIKELY(ret>0))return OP_EBADPACKET; -  if(OP_UNLIKELY(ret<0))return OP_EBADPACKET; -  return ret; -} - -/*Read more samples from the stream, using the same API as op_read() or -   op_read_float().*/ -static int op_read_native(OggOpusFile *_of, - op_sample *_pcm,int _buf_size,int *_li){ -  if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL; -  for(;;){ -    int ret; -    if(OP_LIKELY(_of->ready_state>=OP_INITSET)){ -      int nchannels; -      int od_buffer_pos; -      int nsamples; -      int op_pos; -      nchannels=_of->links[_of->seekable?_of->cur_link:0].head.channel_count; -      od_buffer_pos=_of->od_buffer_pos; -      nsamples=_of->od_buffer_size-od_buffer_pos; -      /*If we have buffered samples, return them.*/ -      if(nsamples>0){ -        if(nsamples*nchannels>_buf_size)nsamples=_buf_size/nchannels; -        memcpy(_pcm,_of->od_buffer+nchannels*od_buffer_pos, -         sizeof(*_pcm)*nchannels*nsamples); -        od_buffer_pos+=nsamples; -        _of->od_buffer_pos=od_buffer_pos; -        if(_li!=NULL)*_li=_of->cur_link; -        return nsamples; -      } -      /*If we have buffered packets, decode one.*/ -      op_pos=_of->op_pos; -      if(OP_LIKELY(op_pos<_of->op_count)){ -        const ogg_packet *pop; -        ogg_int64_t       diff; -        opus_int32        cur_discard_count; -        int               duration; -        int               trimmed_duration; -        pop=_of->op+op_pos++; -        _of->op_pos=op_pos; -        cur_discard_count=_of->cur_discard_count; -        duration=op_get_packet_duration(pop->packet,pop->bytes); -        /*We don't buffer packets with an invalid TOC sequence.*/ -        OP_ASSERT(duration>0); -        trimmed_duration=duration; -        /*Perform end-trimming.*/ -        if(OP_UNLIKELY(pop->e_o_s)){ -          if(OP_UNLIKELY(op_granpos_cmp(pop->granulepos, -           _of->prev_packet_gp)<=0)){ -            trimmed_duration=0; -          } -          else if(OP_LIKELY(!op_granpos_diff(&diff, -           pop->granulepos,_of->prev_packet_gp))){ -            trimmed_duration=(int)OP_MIN(diff,trimmed_duration); -          } -        } -        _of->prev_packet_gp=pop->granulepos; -        if(OP_UNLIKELY(duration*nchannels>_buf_size)){ -          op_sample *buf; -          /*If the user's buffer is too small, decode into a scratch buffer.*/ -          buf=_of->od_buffer; -          if(OP_UNLIKELY(buf==NULL)){ -            ret=op_init_buffer(_of); -            if(OP_UNLIKELY(ret<0))return ret; -            buf=_of->od_buffer; -          } -          ret=op_decode(_of,buf,pop,duration,nchannels); -          if(OP_UNLIKELY(ret<0))return ret; -          /*Perform pre-skip/pre-roll.*/ -          od_buffer_pos=(int)OP_MIN(trimmed_duration,cur_discard_count); -          cur_discard_count-=od_buffer_pos; -          _of->cur_discard_count=cur_discard_count; -          _of->od_buffer_pos=od_buffer_pos; -          _of->od_buffer_size=trimmed_duration; -          /*Update bitrate tracking based on the actual samples we used from -             what was decoded.*/ -          _of->bytes_tracked+=pop->bytes; -          _of->samples_tracked+=trimmed_duration-od_buffer_pos; -        } -        else{ -          /*Otherwise decode directly into the user's buffer.*/ -          ret=op_decode(_of,_pcm,pop,duration,nchannels); -          if(OP_UNLIKELY(ret<0))return ret; -          if(OP_LIKELY(trimmed_duration>0)){ -            /*Perform pre-skip/pre-roll.*/ -            od_buffer_pos=(int)OP_MIN(trimmed_duration,cur_discard_count); -            cur_discard_count-=od_buffer_pos; -            _of->cur_discard_count=cur_discard_count; -            trimmed_duration-=od_buffer_pos; -            if(OP_LIKELY(trimmed_duration>0) -             &&OP_UNLIKELY(od_buffer_pos>0)){ -              memmove(_pcm,_pcm+od_buffer_pos*nchannels, -               sizeof(*_pcm)*trimmed_duration*nchannels); -            } -            /*Update bitrate tracking based on the actual samples we used from -               what was decoded.*/ -            _of->bytes_tracked+=pop->bytes; -            _of->samples_tracked+=trimmed_duration; -            if(OP_LIKELY(trimmed_duration>0)){ -              if(_li!=NULL)*_li=_of->cur_link; -              return trimmed_duration; -            } -          } -        } -        /*Don't grab another page yet. -          This one might have more packets, or might have buffered data now.*/ -        continue; -      } -    } -    /*Suck in another page.*/ -    ret=op_fetch_and_process_page(_of,NULL,-1,1,1,0); -    if(OP_UNLIKELY(ret==OP_EOF)){ -      if(_li!=NULL)*_li=_of->cur_link; -      return 0; -    } -    if(OP_UNLIKELY(ret<0))return ret; -  } -} - -/*A generic filter to apply to the decoded audio data. -  _src is non-const because we will destructively modify the contents of the -   source buffer that we consume in some cases.*/ -typedef int (*op_read_filter_func)(OggOpusFile *_of,void *_dst,int _dst_sz, - op_sample *_src,int _nsamples,int _nchannels); - -/*Decode some samples and then apply a custom filter to them. -  This is used to convert to different output formats.*/ -static int op_filter_read_native(OggOpusFile *_of,void *_dst,int _dst_sz, - op_read_filter_func _filter,int *_li){ -  int ret; -  /*Ensure we have some decoded samples in our buffer.*/ -  ret=op_read_native(_of,NULL,0,_li); -  /*Now apply the filter to them.*/ -  if(OP_LIKELY(ret>=0)&&OP_LIKELY(_of->ready_state>=OP_INITSET)){ -    int od_buffer_pos; -    od_buffer_pos=_of->od_buffer_pos; -    ret=_of->od_buffer_size-od_buffer_pos; -    if(OP_LIKELY(ret>0)){ -      int nchannels; -      nchannels=_of->links[_of->seekable?_of->cur_link:0].head.channel_count; -      ret=(*_filter)(_of,_dst,_dst_sz, -       _of->od_buffer+nchannels*od_buffer_pos,ret,nchannels); -      OP_ASSERT(ret>=0); -      OP_ASSERT(ret<=_of->od_buffer_size-od_buffer_pos); -      od_buffer_pos+=ret; -      _of->od_buffer_pos=od_buffer_pos; -    } -  } -  return ret; -} - -#if !defined(OP_FIXED_POINT)||!defined(OP_DISABLE_FLOAT_API) - -/*Matrices for downmixing from the supported channel counts to stereo. -  The matrices with 5 or more channels are normalized to a total volume of 2.0, -   since most mixes sound too quiet if normalized to 1.0 (as there is generally -   little volume in the side/rear channels).*/ -static const float OP_STEREO_DOWNMIX[OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={ -  /*3.0*/ -  { -    {0.5858F,0.0F},{0.4142F,0.4142F},{0.0F,0.5858F} -  }, -  /*quadrophonic*/ -  { -    {0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F} -  }, -  /*5.0*/ -  { -    {0.651F,0.0F},{0.46F,0.46F},{0.0F,0.651F},{0.5636F,0.3254F}, -    {0.3254F,0.5636F} -  }, -  /*5.1*/ -  { -    {0.529F,0.0F},{0.3741F,0.3741F},{0.0F,0.529F},{0.4582F,0.2645F}, -    {0.2645F,0.4582F},{0.3741F,0.3741F} -  }, -  /*6.1*/ -  { -    {0.4553F,0.0F},{0.322F,0.322F},{0.0F,0.4553F},{0.3943F,0.2277F}, -    {0.2277F,0.3943F},{0.2788F,0.2788F},{0.322F,0.322F} -  }, -  /*7.1*/ -  { -    {0.3886F,0.0F},{0.2748F,0.2748F},{0.0F,0.3886F},{0.3366F,0.1943F}, -    {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F},{0.2748F,0.2748F} -  } -}; - -#endif - -#if defined(OP_FIXED_POINT) - -/*Matrices for downmixing from the supported channel counts to stereo. -  The matrices with 5 or more channels are normalized to a total volume of 2.0, -   since most mixes sound too quiet if normalized to 1.0 (as there is generally -   little volume in the side/rear channels). -  Hence we keep the coefficients in Q14, so the downmix values won't overflow a -   32-bit number.*/ -static const opus_int16 OP_STEREO_DOWNMIX_Q14 - [OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={ -  /*3.0*/ -  { -    {9598,0},{6786,6786},{0,9598} -  }, -  /*quadrophonic*/ -  { -    {6924,0},{0,6924},{5996,3464},{3464,5996} -  }, -  /*5.0*/ -  { -    {10666,0},{7537,7537},{0,10666},{9234,5331},{5331,9234} -  }, -  /*5.1*/ -  { -    {8668,0},{6129,6129},{0,8668},{7507,4335},{4335,7507},{6129,6129} -  }, -  /*6.1*/ -  { -    {7459,0},{5275,5275},{0,7459},{6460,3731},{3731,6460},{4568,4568}, -    {5275,5275} -  }, -  /*7.1*/ -  { -    {6368,0},{4502,4502},{0,6368},{5515,3183},{3183,5515},{5515,3183}, -    {3183,5515},{4502,4502} -  } -}; - -int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){ -  return op_read_native(_of,_pcm,_buf_size,_li); -} - -static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz, - op_sample *_src,int _nsamples,int _nchannels){ -  (void)_of; -  _nsamples=OP_MIN(_nsamples,_dst_sz>>1); -  if(_nchannels==2)memcpy(_dst,_src,_nsamples*2*sizeof(*_src)); -  else{ -    opus_int16 *dst; -    int         i; -    dst=(opus_int16 *)_dst; -    if(_nchannels==1){ -      for(i=0;i<_nsamples;i++)dst[2*i+0]=dst[2*i+1]=_src[i]; -    } -    else{ -      for(i=0;i<_nsamples;i++){ -        opus_int32 l; -        opus_int32 r; -        int        ci; -        l=r=0; -        for(ci=0;ci<_nchannels;ci++){ -          opus_int32 s; -          s=_src[_nchannels*i+ci]; -          l+=OP_STEREO_DOWNMIX_Q14[_nchannels-3][ci][0]*s; -          r+=OP_STEREO_DOWNMIX_Q14[_nchannels-3][ci][1]*s; -        } -        /*TODO: For 5 or more channels, we should do soft clipping here.*/ -        dst[2*i+0]=(opus_int16)OP_CLAMP(-32768,l+8192>>14,32767); -        dst[2*i+1]=(opus_int16)OP_CLAMP(-32768,r+8192>>14,32767); -      } -    } -  } -  return _nsamples; -} - -int op_read_stereo(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size){ -  return op_filter_read_native(_of,_pcm,_buf_size,op_stereo_filter,NULL); -} - -# if !defined(OP_DISABLE_FLOAT_API) - -static int op_short2float_filter(OggOpusFile *_of,void *_dst,int _dst_sz, - op_sample *_src,int _nsamples,int _nchannels){ -  float *dst; -  int    i; -  (void)_of; -  dst=(float *)_dst; -  if(OP_UNLIKELY(_nsamples*_nchannels>_dst_sz))_nsamples=_dst_sz/_nchannels; -  _dst_sz=_nsamples*_nchannels; -  for(i=0;i<_dst_sz;i++)dst[i]=(1.0F/32768)*_src[i]; -  return _nsamples; -} - -int op_read_float(OggOpusFile *_of,float *_pcm,int _buf_size,int *_li){ -  return op_filter_read_native(_of,_pcm,_buf_size,op_short2float_filter,_li); -} - -static int op_short2float_stereo_filter(OggOpusFile *_of, - void *_dst,int _dst_sz,op_sample *_src,int _nsamples,int _nchannels){ -  float *dst; -  int    i; -  dst=(float *)_dst; -  _nsamples=OP_MIN(_nsamples,_dst_sz>>1); -  if(_nchannels==1){ -    _nsamples=op_short2float_filter(_of,dst,_nsamples,_src,_nsamples,1); -    for(i=_nsamples;i-->0;)dst[2*i+0]=dst[2*i+1]=dst[i]; -  } -  else if(_nchannels<5){ -    /*For 3 or 4 channels, we can downmix in fixed point without risk of -       clipping.*/ -    if(_nchannels>2){ -      _nsamples=op_stereo_filter(_of,_src,_nsamples*2, -       _src,_nsamples,_nchannels); -    } -    return op_short2float_filter(_of,dst,_dst_sz,_src,_nsamples,2); -  } -  else{ -    /*For 5 or more channels, we convert to floats and then downmix (so that we -       don't risk clipping).*/ -    for(i=0;i<_nsamples;i++){ -      float l; -      float r; -      int   ci; -      l=r=0; -      for(ci=0;ci<_nchannels;ci++){ -        float s; -        s=(1.0F/32768)*_src[_nchannels*i+ci]; -        l+=OP_STEREO_DOWNMIX[_nchannels-3][ci][0]*s; -        r+=OP_STEREO_DOWNMIX[_nchannels-3][ci][1]*s; -      } -      dst[2*i+0]=l; -      dst[2*i+1]=r; -    } -  } -  return _nsamples; -} - -int op_read_float_stereo(OggOpusFile *_of,float *_pcm,int _buf_size){ -  return op_filter_read_native(_of,_pcm,_buf_size, -   op_short2float_stereo_filter,NULL); -} - -# endif - -#else - -# if defined(OP_HAVE_LRINTF) -#  include <math.h> -#  define op_float2int(_x) (lrintf(_x)) -# else -#  define op_float2int(_x) ((int)((_x)+((_x)<0?-0.5F:0.5F))) -# endif - -/*The dithering code here is adapted from opusdec, part of opus-tools. -  It was originally written by Greg Maxwell.*/ - -static opus_uint32 op_rand(opus_uint32 _seed){ -  return _seed*96314165+907633515&0xFFFFFFFFU; -} - -/*This implements 16-bit quantization with full triangular dither and IIR noise -   shaping. -  The noise shaping filters were designed by Sebastian Gesemann, and are based -   on the LAME ATH curves with flattening to limit their peak gain to 20 dB. -  Everyone else's noise shaping filters are mildly crazy. -  The 48 kHz version of this filter is just a warped version of the 44.1 kHz -   filter and probably could be improved by shifting the HF shelf up in -   frequency a little bit, since 48 kHz has a bit more room and being more -   conservative against bat-ears is probably more important than more noise -   suppression. -  This process can increase the peak level of the signal (in theory by the peak -   error of 1.5 +20 dB, though that is unobservably rare). -  To avoid clipping, the signal is attenuated by a couple thousandths of a dB. -  Initially, the approach taken here was to only attenuate by the 99.9th -   percentile, making clipping rare but not impossible (like SoX), but the -   limited gain of the filter means that the worst case was only two -   thousandths of a dB more, so this just uses the worst case. -  The attenuation is probably also helpful to prevent clipping in the DAC -   reconstruction filters or downstream resampling, in any case.*/ - -# define OP_GAIN (32753.0F) - -# define OP_PRNG_GAIN (1.0F/0xFFFFFFFF) - -/*48 kHz noise shaping filter, sd=2.34.*/ - -static const float OP_FCOEF_B[4]={ -  2.2374F,-0.7339F,-0.1251F,-0.6033F -}; - -static const float OP_FCOEF_A[4]={ -  0.9030F,0.0116F,-0.5853F,-0.2571F -}; - -static int op_float2short_filter(OggOpusFile *_of,void *_dst,int _dst_sz, - float *_src,int _nsamples,int _nchannels){ -  opus_int16 *dst; -  int         ci; -  int         i; -  dst=(opus_int16 *)_dst; -  if(OP_UNLIKELY(_nsamples*_nchannels>_dst_sz))_nsamples=_dst_sz/_nchannels; -# if defined(OP_SOFT_CLIP) -  if(_of->state_channel_count!=_nchannels){ -    for(ci=0;ci<_nchannels;ci++)_of->clip_state[ci]=0; -  } -  opus_pcm_soft_clip(_src,_nsamples,_nchannels,_of->clip_state); -# endif -  if(_of->dither_disabled){ -    for(i=0;i<_nchannels*_nsamples;i++){ -      dst[i]=op_float2int(OP_CLAMP(-32768,32768.0F*_src[i],32767)); -    } -  } -  else{ -    opus_uint32 seed; -    int         mute; -    seed=_of->dither_seed; -    mute=_of->dither_mute; -    if(_of->state_channel_count!=_nchannels)mute=65; -    /*In order to avoid replacing digital silence with quiet dither noise, we -       mute if the output has been silent for a while.*/ -    if(mute>64)memset(_of->dither_a,0,sizeof(*_of->dither_a)*4*_nchannels); -    for(i=0;i<_nsamples;i++){ -      int silent; -      silent=1; -      for(ci=0;ci<_nchannels;ci++){ -        float r; -        float s; -        float err; -        int   si; -        int   j; -        s=_src[_nchannels*i+ci]; -        silent&=s==0; -        s*=OP_GAIN; -        err=0; -        for(j=0;j<4;j++){ -          err+=OP_FCOEF_B[j]*_of->dither_b[ci*4+j] -           -OP_FCOEF_A[j]*_of->dither_a[ci*4+j]; -        } -        for(j=3;j-->0;)_of->dither_a[ci*4+j+1]=_of->dither_a[ci*4+j]; -        for(j=3;j-->0;)_of->dither_b[ci*4+j+1]=_of->dither_b[ci*4+j]; -        _of->dither_a[ci*4]=err; -        s-=err; -        if(mute>16)r=0; -        else{ -          seed=op_rand(seed); -          r=seed*OP_PRNG_GAIN; -          seed=op_rand(seed); -          r-=seed*OP_PRNG_GAIN; -        } -        /*Clamp in float out of paranoia that the input will be > 96 dBFS and -           wrap if the integer is clamped.*/ -        si=op_float2int(OP_CLAMP(-32768,s+r,32767)); -        dst[_nchannels*i+ci]=(opus_int16)si; -        /*Including clipping in the noise shaping is generally disastrous: the -           futile effort to restore the clipped energy results in more clipping. -          However, small amounts---at the level which could normally be created -           by dither and rounding---are harmless and can even reduce clipping -           somewhat due to the clipping sometimes reducing the dither + rounding -           error.*/ -        _of->dither_b[ci*4]=mute>16?0:OP_CLAMP(-1.5F,si-s,1.5F); -      } -      mute++; -      if(!silent)mute=0; -    } -    _of->dither_mute=OP_MIN(mute,65); -    _of->dither_seed=seed; -  } -  _of->state_channel_count=_nchannels; -  return _nsamples; -} - -int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){ -  return op_filter_read_native(_of,_pcm,_buf_size,op_float2short_filter,_li); -} - -int op_read_float(OggOpusFile *_of,float *_pcm,int _buf_size,int *_li){ -  _of->state_channel_count=0; -  return op_read_native(_of,_pcm,_buf_size,_li); -} - -static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz, - op_sample *_src,int _nsamples,int _nchannels){ -  (void)_of; -  _nsamples=OP_MIN(_nsamples,_dst_sz>>1); -  if(_nchannels==2)memcpy(_dst,_src,_nsamples*2*sizeof(*_src)); -  else{ -    float *dst; -    int    i; -    dst=(float *)_dst; -    if(_nchannels==1){ -      for(i=0;i<_nsamples;i++)dst[2*i+0]=dst[2*i+1]=_src[i]; -    } -    else{ -      for(i=0;i<_nsamples;i++){ -        float l; -        float r; -        int   ci; -        l=r=0; -        for(ci=0;ci<_nchannels;ci++){ -          l+=OP_STEREO_DOWNMIX[_nchannels-3][ci][0]*_src[_nchannels*i+ci]; -          r+=OP_STEREO_DOWNMIX[_nchannels-3][ci][1]*_src[_nchannels*i+ci]; -        } -        dst[2*i+0]=l; -        dst[2*i+1]=r; -      } -    } -  } -  return _nsamples; -} - -static int op_float2short_stereo_filter(OggOpusFile *_of, - void *_dst,int _dst_sz,op_sample *_src,int _nsamples,int _nchannels){ -  opus_int16 *dst; -  dst=(opus_int16 *)_dst; -  if(_nchannels==1){ -    int i; -    _nsamples=op_float2short_filter(_of,dst,_dst_sz>>1,_src,_nsamples,1); -    for(i=_nsamples;i-->0;)dst[2*i+0]=dst[2*i+1]=dst[i]; -  } -  else{ -    if(_nchannels>2){ -      _nsamples=OP_MIN(_nsamples,_dst_sz>>1); -      _nsamples=op_stereo_filter(_of,_src,_nsamples*2, -       _src,_nsamples,_nchannels); -    } -    _nsamples=op_float2short_filter(_of,dst,_dst_sz,_src,_nsamples,2); -  } -  return _nsamples; -} - -int op_read_stereo(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size){ -  return op_filter_read_native(_of,_pcm,_buf_size, -   op_float2short_stereo_filter,NULL); -} - -int op_read_float_stereo(OggOpusFile *_of,float *_pcm,int _buf_size){ -  _of->state_channel_count=0; -  return op_filter_read_native(_of,_pcm,_buf_size,op_stereo_filter,NULL); -} - -#endif diff --git a/src/opusfile-0.5/src/stream.c b/src/opusfile-0.5/src/stream.c deleted file mode 100644 index 0238a6b3..00000000 --- a/src/opusfile-0.5/src/stream.c +++ /dev/null @@ -1,366 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012           * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ******************************************************************** - - function: stdio-based convenience library for opening/seeking/decoding - last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $ - - ********************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "internal.h" -#include <sys/types.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#if defined(_WIN32) -# include <io.h> -#endif - -typedef struct OpusMemStream OpusMemStream; - -#define OP_MEM_SIZE_MAX (~(size_t)0>>1) -#define OP_MEM_DIFF_MAX ((ptrdiff_t)OP_MEM_SIZE_MAX) - -/*The context information needed to read from a block of memory as if it were a -   file.*/ -struct OpusMemStream{ -  /*The block of memory to read from.*/ -  const unsigned char *data; -  /*The total size of the block. -    This must be at most OP_MEM_SIZE_MAX to prevent signed overflow while -     seeking.*/ -  ptrdiff_t            size; -  /*The current file position. -    This is allowed to be set arbitrarily greater than size (i.e., past the end -     of the block, though we will not read data past the end of the block), but -     is not allowed to be negative (i.e., before the beginning of the block).*/ -  ptrdiff_t            pos; -}; - -static int op_fread(void *_stream,unsigned char *_ptr,int _buf_size){ -  FILE   *stream; -  size_t  ret; -  /*Check for empty read.*/ -  if(_buf_size<=0)return 0; -  stream=(FILE *)_stream; -  ret=fread(_ptr,1,_buf_size,stream); -  OP_ASSERT(ret<=(size_t)_buf_size); -  /*If ret==0 and !feof(stream), there was a read error.*/ -  return ret>0||feof(stream)?(int)ret:OP_EREAD; -} - -static int op_fseek(void *_stream,opus_int64 _offset,int _whence){ -#if defined(_WIN32) -  /*_fseeki64() is not exposed until MSCVCRT80. -    This is the default starting with MSVC 2005 (_MSC_VER>=1400), but we want -     to allow linking against older MSVCRT versions for compatibility back to -     XP without installing extra runtime libraries. -    i686-pc-mingw32 does not have fseeko() and requires -     __MSVCRT_VERSION__>=0x800 for _fseeki64(), which screws up linking with -     other libraries (that don't use MSVCRT80 from MSVC 2005 by default). -    i686-w64-mingw32 does have fseeko() and respects _FILE_OFFSET_BITS, but I -     don't know how to detect that at compile time. -    We could just use fseeko64() (which is available in both), but its -     implemented using fgetpos()/fsetpos() just like this code, except without -     the overflow checking, so we prefer our version.*/ -  opus_int64 pos; -  /*We don't use fpos_t directly because it might be a struct if __STDC__ is -     non-zero or _INTEGRAL_MAX_BITS < 64. -    I'm not certain when the latter is true, but someone could in theory set -     the former. -    Either way, it should be binary compatible with a normal 64-bit int (this -     assumption is not portable, but I believe it is true for MSVCRT).*/ -  OP_ASSERT(sizeof(pos)==sizeof(fpos_t)); -  /*Translate the seek to an absolute one.*/ -  if(_whence==SEEK_CUR){ -    int ret; -    ret=fgetpos((FILE *)_stream,(fpos_t *)&pos); -    if(ret)return ret; -  } -  else if(_whence==SEEK_END)pos=_filelengthi64(_fileno((FILE *)_stream)); -  else if(_whence==SEEK_SET)pos=0; -  else return -1; -  /*Check for errors or overflow.*/ -  if(pos<0||_offset<-pos||_offset>OP_INT64_MAX-pos)return -1; -  pos+=_offset; -  return fsetpos((FILE *)_stream,(fpos_t *)&pos); -#else -  /*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer -     it except on Windows.*/ -  return fseeko((FILE *)_stream,(off_t)_offset,_whence); -#endif -} - -static opus_int64 op_ftell(void *_stream){ -#if defined(_WIN32) -  /*_ftelli64() is not exposed until MSCVCRT80, and ftello()/ftello64() have -     the same problems as fseeko()/fseeko64() in MingW. -    See above for a more detailed explanation.*/ -  opus_int64 pos; -  OP_ASSERT(sizeof(pos)==sizeof(fpos_t)); -  return fgetpos((FILE *)_stream,(fpos_t *)&pos)?-1:pos; -#else -  /*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer -     it except on Windows.*/ -  return ftello((FILE *)_stream); -#endif -} - -static const OpusFileCallbacks OP_FILE_CALLBACKS={ -  op_fread, -  op_fseek, -  op_ftell, -  (op_close_func)fclose -}; - -#if defined(_WIN32) -# include <stddef.h> -# include <errno.h> - -/*Windows doesn't accept UTF-8 by default, and we don't have a wchar_t API, -   so if we just pass the path to fopen(), then there'd be no way for a user -   of our API to open a Unicode filename. -  Instead, we translate from UTF-8 to UTF-16 and use Windows' wchar_t API. -  This makes this API more consistent with platforms where the character set -   used by fopen is the same as used on disk, which is generally UTF-8, and -   with our metadata API, which always uses UTF-8.*/ -static wchar_t *op_utf8_to_utf16(const char *_src){ -  wchar_t *dst; -  size_t   len; -  len=strlen(_src); -  /*Worst-case output is 1 wide character per 1 input character.*/ -  dst=(wchar_t *)_ogg_malloc(sizeof(*dst)*(len+1)); -  if(dst!=NULL){ -    size_t si; -    size_t di; -    for(di=si=0;si<len;si++){ -      int c0; -      c0=(unsigned char)_src[si]; -      if(!(c0&0x80)){ -        /*Start byte says this is a 1-byte sequence.*/ -        dst[di++]=(wchar_t)c0; -        continue; -      } -      else{ -        int c1; -        /*This is safe, because c0 was not 0 and _src is NUL-terminated.*/ -        c1=(unsigned char)_src[si+1]; -        if((c1&0xC0)==0x80){ -          /*Found at least one continuation byte.*/ -          if((c0&0xE0)==0xC0){ -            wchar_t w; -            /*Start byte says this is a 2-byte sequence.*/ -            w=(c0&0x1F)<<6|c1&0x3F; -            if(w>=0x80U){ -              /*This is a 2-byte sequence that is not overlong.*/ -              dst[di++]=w; -              si++; -              continue; -            } -          } -          else{ -            int c2; -            /*This is safe, because c1 was not 0 and _src is NUL-terminated.*/ -            c2=(unsigned char)_src[si+2]; -            if((c2&0xC0)==0x80){ -              /*Found at least two continuation bytes.*/ -              if((c0&0xF0)==0xE0){ -                wchar_t w; -                /*Start byte says this is a 3-byte sequence.*/ -                w=(c0&0xF)<<12|(c1&0x3F)<<6|c2&0x3F; -                if(w>=0x800U&&(w<0xD800||w>=0xE000)&&w<0xFFFE){ -                  /*This is a 3-byte sequence that is not overlong, not a -                     UTF-16 surrogate pair value, and not a 'not a character' -                     value.*/ -                  dst[di++]=w; -                  si+=2; -                  continue; -                } -              } -              else{ -                int c3; -                /*This is safe, because c2 was not 0 and _src is -                   NUL-terminated.*/ -                c3=(unsigned char)_src[si+3]; -                if((c3&0xC0)==0x80){ -                  /*Found at least three continuation bytes.*/ -                  if((c0&0xF8)==0xF0){ -                    opus_uint32 w; -                    /*Start byte says this is a 4-byte sequence.*/ -                    w=(c0&7)<<18|(c1&0x3F)<<12|(c2&0x3F)<<6&(c3&0x3F); -                    if(w>=0x10000U&&w<0x110000U){ -                      /*This is a 4-byte sequence that is not overlong and not -                         greater than the largest valid Unicode code point. -                        Convert it to a surrogate pair.*/ -                      w-=0x10000; -                      dst[di++]=(wchar_t)(0xD800+(w>>10)); -                      dst[di++]=(wchar_t)(0xDC00+(w&0x3FF)); -                      si+=3; -                      continue; -                    } -                  } -                } -              } -            } -          } -        } -      } -      /*If we got here, we encountered an illegal UTF-8 sequence.*/ -      _ogg_free(dst); -      return NULL; -    } -    OP_ASSERT(di<=len); -    dst[di]='\0'; -  } -  return dst; -} - -#endif - -void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){ -  FILE *fp; -#if !defined(_WIN32) -  fp=fopen(_path,_mode); -#else -  fp=NULL; -  if(_path==NULL||_mode==NULL)errno=EINVAL; -  else{ -    wchar_t *wpath; -    wchar_t *wmode; -    wpath=op_utf8_to_utf16(_path); -    wmode=op_utf8_to_utf16(_mode); -    if(wmode==NULL)errno=EINVAL; -    else if(wpath==NULL)errno=ENOENT; -    else fp=_wfopen(wpath,wmode); -    _ogg_free(wmode); -    _ogg_free(wpath); -  } -#endif -  if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS; -  return fp; -} - -void *op_fdopen(OpusFileCallbacks *_cb,int _fd,const char *_mode){ -  FILE *fp; -  fp=fdopen(_fd,_mode); -  if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS; -  return fp; -} - -void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode, - void *_stream){ -  FILE *fp; -#if !defined(_WIN32) -  fp=freopen(_path,_mode,(FILE *)_stream); -#else -  fp=NULL; -  if(_path==NULL||_mode==NULL)errno=EINVAL; -  else{ -    wchar_t *wpath; -    wchar_t *wmode; -    wpath=op_utf8_to_utf16(_path); -    wmode=op_utf8_to_utf16(_mode); -    if(wmode==NULL)errno=EINVAL; -    else if(wpath==NULL)errno=ENOENT; -    else fp=_wfreopen(wpath,wmode,(FILE *)_stream); -    _ogg_free(wmode); -    _ogg_free(wpath); -  } -#endif -  if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS; -  return fp; -} - -static int op_mem_read(void *_stream,unsigned char *_ptr,int _buf_size){ -  OpusMemStream *stream; -  ptrdiff_t      size; -  ptrdiff_t      pos; -  stream=(OpusMemStream *)_stream; -  /*Check for empty read.*/ -  if(_buf_size<=0)return 0; -  size=stream->size; -  pos=stream->pos; -  /*Check for EOF.*/ -  if(pos>=size)return 0; -  /*Check for a short read.*/ -  _buf_size=(int)OP_MIN(size-pos,_buf_size); -  memcpy(_ptr,stream->data+pos,_buf_size); -  pos+=_buf_size; -  stream->pos=pos; -  return _buf_size; -} - -static int op_mem_seek(void *_stream,opus_int64 _offset,int _whence){ -  OpusMemStream *stream; -  ptrdiff_t      pos; -  stream=(OpusMemStream *)_stream; -  pos=stream->pos; -  OP_ASSERT(pos>=0); -  switch(_whence){ -    case SEEK_SET:{ -      /*Check for overflow:*/ -      if(_offset<0||_offset>OP_MEM_DIFF_MAX)return -1; -      pos=(ptrdiff_t)_offset; -    }break; -    case SEEK_CUR:{ -      /*Check for overflow:*/ -      if(_offset<-pos||_offset>OP_MEM_DIFF_MAX-pos)return -1; -      pos=(ptrdiff_t)(pos+_offset); -    }break; -    case SEEK_END:{ -      ptrdiff_t size; -      size=stream->size; -      OP_ASSERT(size>=0); -      /*Check for overflow:*/ -      if(_offset>size||_offset<size-OP_MEM_DIFF_MAX)return -1; -      pos=(ptrdiff_t)(size-_offset); -    }break; -    default:return -1; -  } -  stream->pos=pos; -  return 0; -} - -static opus_int64 op_mem_tell(void *_stream){ -  OpusMemStream *stream; -  stream=(OpusMemStream *)_stream; -  return (ogg_int64_t)stream->pos; -} - -static int op_mem_close(void *_stream){ -  _ogg_free(_stream); -  return 0; -} - -static const OpusFileCallbacks OP_MEM_CALLBACKS={ -  op_mem_read, -  op_mem_seek, -  op_mem_tell, -  op_mem_close -}; - -void *op_mem_stream_create(OpusFileCallbacks *_cb, - const unsigned char *_data,size_t _size){ -  OpusMemStream *stream; -  if(_size>OP_MEM_SIZE_MAX)return NULL; -  stream=(OpusMemStream *)_ogg_malloc(sizeof(*stream)); -  if(stream!=NULL){ -    *_cb=*&OP_MEM_CALLBACKS; -    stream->data=_data; -    stream->size=_size; -    stream->pos=0; -  } -  return stream; -} diff --git a/src/opusfile-0.5/src/wincerts.c b/src/opusfile-0.5/src/wincerts.c deleted file mode 100644 index b0e35aa3..00000000 --- a/src/opusfile-0.5/src/wincerts.c +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2013                * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ********************************************************************/ - -/*This should really be part of OpenSSL, but there's been a patch [1] sitting -   in their bugtracker for over two years that implements this, without any -   action, so I'm giving up and re-implementing it locally. - -  [1] <http://rt.openssl.org/Ticket/Display.html?id=2158>*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "internal.h" -#if defined(OP_ENABLE_HTTP)&&defined(_WIN32) -/*You must include windows.h before wincrypt.h and x509.h.*/ -# define WIN32_LEAN_AND_MEAN -# define WIN32_EXTRA_LEAN -# include <windows.h> -/*You must include wincrypt.h before x509.h, too, or X509_NAME doesn't get -   defined properly.*/ -# include <wincrypt.h> -# include <openssl/ssl.h> -# include <openssl/err.h> -# include <openssl/x509.h> - -static int op_capi_new(X509_LOOKUP *_lu){ -  HCERTSTORE h_store; -  h_store=CertOpenStore(CERT_STORE_PROV_SYSTEM_A,0,0, -   CERT_STORE_OPEN_EXISTING_FLAG|CERT_STORE_READONLY_FLAG| -   CERT_SYSTEM_STORE_CURRENT_USER|CERT_STORE_SHARE_CONTEXT_FLAG,"ROOT"); -  if(h_store!=NULL){ -    _lu->method_data=(char *)h_store; -    return 1; -  } -  return 0; -} - -static void op_capi_free(X509_LOOKUP *_lu){ -  HCERTSTORE h_store; -  h_store=(HCERTSTORE)_lu->method_data; -# if defined(OP_ENABLE_ASSERTIONS) -  OP_ALWAYS_TRUE(CertCloseStore(h_store,CERT_CLOSE_STORE_CHECK_FLAG)); -# else -  CertCloseStore(h_store,0); -# endif -} - -static int op_capi_retrieve_by_subject(X509_LOOKUP *_lu,int _type, - X509_NAME *_name,X509_OBJECT *_ret){ -  X509_OBJECT *obj; -  CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); -  obj=X509_OBJECT_retrieve_by_subject(_lu->store_ctx->objs,_type,_name); -  CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); -  if(obj!=NULL){ -    _ret->type=obj->type; -    memcpy(&_ret->data,&obj->data,sizeof(_ret->data)); -    return 1; -  } -  return 0; -} - -static int op_capi_get_by_subject(X509_LOOKUP *_lu,int _type,X509_NAME *_name, - X509_OBJECT *_ret){ -  HCERTSTORE h_store; -  if(_name==NULL)return 0; -  if(_name->bytes==NULL||_name->bytes->length<=0||_name->modified){ -    if(i2d_X509_NAME(_name,NULL)<0)return 0; -    OP_ASSERT(_name->bytes->length>0); -  } -  h_store=(HCERTSTORE)_lu->method_data; -  switch(_type){ -    case X509_LU_X509:{ -      CERT_NAME_BLOB  find_para; -      PCCERT_CONTEXT  cert; -      X509           *x; -      int             ret; -      /*Although X509_NAME contains a canon_enc field, that "canonical" [1] -         encoding was just made up by OpenSSL. -        It doesn't correspond to any actual standard, and since it drops the -         initial sequence header, won't be recognized by the Crypto API. -        The assumption here is that CertFindCertificateInStore() will allow any -         appropriate variations in the encoding when it does its comparison. -        This is, however, emphatically not true under Wine, which just compares -         the encodings with memcmp(). -        Most of the time things work anyway, though, and there isn't really -         anything we can do to make the situation better. - -        [1] A "canonical form" is defined as the one where, if you locked 10 -         mathematicians in a room and asked them to come up with a -         representation for something, it's the answer that 9 of them would -         give you back. -        I don't think OpenSSL's encoding qualifies.*/ -      find_para.cbData=_name->bytes->length; -      find_para.pbData=(unsigned char *)_name->bytes->data; -      cert=CertFindCertificateInStore(h_store,X509_ASN_ENCODING,0, -       CERT_FIND_SUBJECT_NAME,&find_para,NULL); -      if(cert==NULL)return 0; -      x=d2i_X509(NULL,(const unsigned char **)&cert->pbCertEncoded, -       cert->cbCertEncoded); -      CertFreeCertificateContext(cert); -      if(x==NULL)return 0; -      ret=X509_STORE_add_cert(_lu->store_ctx,x); -      X509_free(x); -      if(ret)return op_capi_retrieve_by_subject(_lu,_type,_name,_ret); -    }break; -    case X509_LU_CRL:{ -      CERT_INFO      cert_info; -      CERT_CONTEXT   find_para; -      PCCRL_CONTEXT  crl; -      X509_CRL      *x; -      int            ret; -      ret=op_capi_retrieve_by_subject(_lu,_type,_name,_ret); -      if(ret>0)return ret; -      memset(&cert_info,0,sizeof(cert_info)); -      cert_info.Issuer.cbData=_name->bytes->length; -      cert_info.Issuer.pbData=(unsigned char *)_name->bytes->data; -      memset(&find_para,0,sizeof(find_para)); -      find_para.pCertInfo=&cert_info; -      crl=CertFindCRLInStore(h_store,0,0,CRL_FIND_ISSUED_BY,&find_para,NULL); -      if(crl==NULL)return 0; -      x=d2i_X509_CRL(NULL,(const unsigned char **)&crl->pbCrlEncoded, -       crl->cbCrlEncoded); -      CertFreeCRLContext(crl); -      if(x==NULL)return 0; -      ret=X509_STORE_add_crl(_lu->store_ctx,x); -      X509_CRL_free(x); -      if(ret)return op_capi_retrieve_by_subject(_lu,_type,_name,_ret); -    }break; -  } -  return 0; -} - -/*This is not const because OpenSSL doesn't allow it, even though it won't -   write to it.*/ -static X509_LOOKUP_METHOD X509_LOOKUP_CAPI={ -  "Load Crypto API store into cache", -  op_capi_new, -  op_capi_free, -  NULL, -  NULL, -  NULL, -  op_capi_get_by_subject, -  NULL, -  NULL, -  NULL -}; - -int SSL_CTX_set_default_verify_paths_win32(SSL_CTX *_ssl_ctx){ -  X509_STORE  *store; -  X509_LOOKUP *lu; -  /*We intentionally do not add the normal default paths, as they are usually -     wrong, and are just asking to be used as an exploit vector.*/ -  store=SSL_CTX_get_cert_store(_ssl_ctx); -  OP_ASSERT(store!=NULL); -  lu=X509_STORE_add_lookup(store,&X509_LOOKUP_CAPI); -  if(lu==NULL)return 0; -  ERR_clear_error(); -  return 1; -} - -#endif diff --git a/src/opusfile-0.5/src/winerrno.h b/src/opusfile-0.5/src/winerrno.h deleted file mode 100644 index 32a90b4e..00000000 --- a/src/opusfile-0.5/src/winerrno.h +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************** - *                                                                  * - * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       * - *                                                                  * - * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012                * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - *                                                                  * - ********************************************************************/ -#if !defined(_opusfile_winerrno_h) -# define _opusfile_winerrno_h (1) - -# include <errno.h> -# include <winerror.h> - -/*These conflict with the MSVC errno.h definitions, but we don't need to use -   the original ones in any file that deals with sockets. -  We could map the WSA errors to the errno.h ones (most of which are only -   available on sufficiently new versions of MSVC), but they aren't ordered the -   same, and given how rarely we actually look at the values, I don't think -   it's worth a lookup table.*/ -# undef EWOULDBLOCK -# undef EINPROGRESS -# undef EALREADY -# undef ENOTSOCK -# undef EDESTADDRREQ -# undef EMSGSIZE -# undef EPROTOTYPE -# undef ENOPROTOOPT -# undef EPROTONOSUPPORT -# undef EOPNOTSUPP -# undef EAFNOSUPPORT -# undef EADDRINUSE -# undef EADDRNOTAVAIL -# undef ENETDOWN -# undef ENETUNREACH -# undef ENETRESET -# undef ECONNABORTED -# undef ECONNRESET -# undef ENOBUFS -# undef EISCONN -# undef ENOTCONN -# undef ETIMEDOUT -# undef ECONNREFUSED -# undef ELOOP -# undef ENAMETOOLONG -# undef EHOSTUNREACH -# undef ENOTEMPTY - -# define EWOULDBLOCK     (WSAEWOULDBLOCK-WSABASEERR) -# define EINPROGRESS     (WSAEINPROGRESS-WSABASEERR) -# define EALREADY        (WSAEALREADY-WSABASEERR) -# define ENOTSOCK        (WSAENOTSOCK-WSABASEERR) -# define EDESTADDRREQ    (WSAEDESTADDRREQ-WSABASEERR) -# define EMSGSIZE        (WSAEMSGSIZE-WSABASEERR) -# define EPROTOTYPE      (WSAEPROTOTYPE-WSABASEERR) -# define ENOPROTOOPT     (WSAENOPROTOOPT-WSABASEERR) -# define EPROTONOSUPPORT (WSAEPROTONOSUPPORT-WSABASEERR) -# define ESOCKTNOSUPPORT (WSAESOCKTNOSUPPORT-WSABASEERR) -# define EOPNOTSUPP      (WSAEOPNOTSUPP-WSABASEERR) -# define EPFNOSUPPORT    (WSAEPFNOSUPPORT-WSABASEERR) -# define EAFNOSUPPORT    (WSAEAFNOSUPPORT-WSABASEERR) -# define EADDRINUSE      (WSAEADDRINUSE-WSABASEERR) -# define EADDRNOTAVAIL   (WSAEADDRNOTAVAIL-WSABASEERR) -# define ENETDOWN        (WSAENETDOWN-WSABASEERR) -# define ENETUNREACH     (WSAENETUNREACH-WSABASEERR) -# define ENETRESET       (WSAENETRESET-WSABASEERR) -# define ECONNABORTED    (WSAECONNABORTED-WSABASEERR) -# define ECONNRESET      (WSAECONNRESET-WSABASEERR) -# define ENOBUFS         (WSAENOBUFS-WSABASEERR) -# define EISCONN         (WSAEISCONN-WSABASEERR) -# define ENOTCONN        (WSAENOTCONN-WSABASEERR) -# define ESHUTDOWN       (WSAESHUTDOWN-WSABASEERR) -# define ETOOMANYREFS    (WSAETOOMANYREFS-WSABASEERR) -# define ETIMEDOUT       (WSAETIMEDOUT-WSABASEERR) -# define ECONNREFUSED    (WSAECONNREFUSED-WSABASEERR) -# define ELOOP           (WSAELOOP-WSABASEERR) -# define ENAMETOOLONG    (WSAENAMETOOLONG-WSABASEERR) -# define EHOSTDOWN       (WSAEHOSTDOWN-WSABASEERR) -# define EHOSTUNREACH    (WSAEHOSTUNREACH-WSABASEERR) -# define ENOTEMPTY       (WSAENOTEMPTY-WSABASEERR) -# define EPROCLIM        (WSAEPROCLIM-WSABASEERR) -# define EUSERS          (WSAEUSERS-WSABASEERR) -# define EDQUOT          (WSAEDQUOT-WSABASEERR) -# define ESTALE          (WSAESTALE-WSABASEERR) -# define EREMOTE         (WSAEREMOTE-WSABASEERR) - -#endif  | 
