~ubuntu-branches/ubuntu/raring/flac/raring

« back to all changes in this revision

Viewing changes to src/libFLAC/ogg_decoder_aspect.c

  • Committer: Bazaar Package Importer
  • Author(s): Joshua Kwan
  • Date: 2007-05-29 22:56:36 UTC
  • mto: (8.1.1 lenny)
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: james.westby@ubuntu.com-20070529225636-p8lkii0r0kp50pns
Tags: upstream-1.1.4
ImportĀ upstreamĀ versionĀ 1.1.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* libFLAC - Free Lossless Audio Codec
 
2
 * Copyright (C) 2002,2003,2004,2005,2006,2007  Josh Coalson
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions
 
6
 * are met:
 
7
 *
 
8
 * - Redistributions of source code must retain the above copyright
 
9
 * notice, this list of conditions and the following disclaimer.
 
10
 *
 
11
 * - Redistributions in binary form must reproduce the above copyright
 
12
 * notice, this list of conditions and the following disclaimer in the
 
13
 * documentation and/or other materials provided with the distribution.
 
14
 *
 
15
 * - Neither the name of the Xiph.org Foundation nor the names of its
 
16
 * contributors may be used to endorse or promote products derived from
 
17
 * this software without specific prior written permission.
 
18
 *
 
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
20
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
21
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
22
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
 
23
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
24
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
25
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
26
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 
27
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 
28
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
29
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
30
 */
 
31
 
 
32
#if HAVE_CONFIG_H
 
33
#  include <config.h>
 
34
#endif
 
35
 
 
36
#include <string.h> /* for memcpy() */
 
37
#include "FLAC/assert.h"
 
38
#include "private/ogg_decoder_aspect.h"
 
39
#include "private/ogg_mapping.h"
 
40
 
 
41
#ifdef max
 
42
#undef max
 
43
#endif
 
44
#define max(x,y) ((x)>(y)?(x):(y))
 
45
 
 
46
/***********************************************************************
 
47
 *
 
48
 * Public class methods
 
49
 *
 
50
 ***********************************************************************/
 
51
 
 
52
FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect)
 
53
{
 
54
        /* we will determine the serial number later if necessary */
 
55
        if(ogg_stream_init(&aspect->stream_state, aspect->serial_number) != 0)
 
56
                return false;
 
57
 
 
58
        if(ogg_sync_init(&aspect->sync_state) != 0)
 
59
                return false;
 
60
 
 
61
        aspect->version_major = ~(0u);
 
62
        aspect->version_minor = ~(0u);
 
63
 
 
64
        aspect->need_serial_number = aspect->use_first_serial_number;
 
65
 
 
66
        aspect->end_of_stream = false;
 
67
        aspect->have_working_page = false;
 
68
 
 
69
        return true;
 
70
}
 
71
 
 
72
void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect)
 
73
{
 
74
        (void)ogg_sync_clear(&aspect->sync_state);
 
75
        (void)ogg_stream_clear(&aspect->stream_state);
 
76
}
 
77
 
 
78
void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value)
 
79
{
 
80
        aspect->use_first_serial_number = false;
 
81
        aspect->serial_number = value;
 
82
}
 
83
 
 
84
void FLAC__ogg_decoder_aspect_set_defaults(FLAC__OggDecoderAspect *aspect)
 
85
{
 
86
        aspect->use_first_serial_number = true;
 
87
}
 
88
 
 
89
void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect)
 
90
{
 
91
        (void)ogg_stream_reset(&aspect->stream_state);
 
92
        (void)ogg_sync_reset(&aspect->sync_state);
 
93
        aspect->end_of_stream = false;
 
94
        aspect->have_working_page = false;
 
95
}
 
96
 
 
97
void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect)
 
98
{
 
99
        FLAC__ogg_decoder_aspect_flush(aspect);
 
100
 
 
101
        if(aspect->use_first_serial_number)
 
102
                aspect->need_serial_number = true;
 
103
}
 
104
 
 
105
FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, const FLAC__StreamDecoder *decoder, void *client_data)
 
106
{
 
107
        static const size_t OGG_BYTES_CHUNK = 8192;
 
108
        const size_t bytes_requested = *bytes;
 
109
 
 
110
        /*
 
111
         * The FLAC decoding API uses pull-based reads, whereas Ogg decoding
 
112
         * is push-based.  In libFLAC, when you ask to decode a frame, the
 
113
         * decoder will eventually call the read callback to supply some data,
 
114
         * but how much it asks for depends on how much free space it has in
 
115
         * its internal buffer.  It does not try to grow its internal buffer
 
116
         * to accomodate a whole frame because then the internal buffer size
 
117
         * could not be limited, which is necessary in embedded applications.
 
118
         *
 
119
         * Ogg however grows its internal buffer until a whole page is present;
 
120
         * only then can you get decoded data out.  So we can't just ask for
 
121
         * the same number of bytes from Ogg, then pass what's decoded down to
 
122
         * libFLAC.  If what libFLAC is asking for will not contain a whole
 
123
         * page, then we will get no data from ogg_sync_pageout(), and at the
 
124
         * same time cannot just read more data from the client for the purpose
 
125
         * of getting a whole decoded page because the decoded size might be
 
126
         * larger than libFLAC's internal buffer.
 
127
         *
 
128
         * Instead, whenever this read callback wrapper is called, we will
 
129
         * continually request data from the client until we have at least one
 
130
         * page, and manage pages internally so that we can send pieces of
 
131
         * pages down to libFLAC in such a way that we obey its size
 
132
         * requirement.  To limit the amount of callbacks, we will always try
 
133
         * to read in enough pages to return the full number of bytes
 
134
         * requested.
 
135
         */
 
136
        *bytes = 0;
 
137
        while (*bytes < bytes_requested && !aspect->end_of_stream) {
 
138
                if (aspect->have_working_page) {
 
139
                        if (aspect->have_working_packet) {
 
140
                                size_t n = bytes_requested - *bytes;
 
141
                                if ((size_t)aspect->working_packet.bytes <= n) {
 
142
                                        /* the rest of the packet will fit in the buffer */
 
143
                                        n = aspect->working_packet.bytes;
 
144
                                        memcpy(buffer, aspect->working_packet.packet, n);
 
145
                                        *bytes += n;
 
146
                                        buffer += n;
 
147
                                        aspect->have_working_packet = false;
 
148
                                }
 
149
                                else {
 
150
                                        /* only n bytes of the packet will fit in the buffer */
 
151
                                        memcpy(buffer, aspect->working_packet.packet, n);
 
152
                                        *bytes += n;
 
153
                                        buffer += n;
 
154
                                        aspect->working_packet.packet += n;
 
155
                                        aspect->working_packet.bytes -= n;
 
156
                                }
 
157
                        }
 
158
                        else {
 
159
                                /* try and get another packet */
 
160
                                const int ret = ogg_stream_packetout(&aspect->stream_state, &aspect->working_packet);
 
161
                                if (ret > 0) {
 
162
                                        aspect->have_working_packet = true;
 
163
                                        /* if it is the first header packet, check for magic and a supported Ogg FLAC mapping version */
 
164
                                        if (aspect->working_packet.bytes > 0 && aspect->working_packet.packet[0] == FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE) {
 
165
                                                const FLAC__byte *b = aspect->working_packet.packet;
 
166
                                                const unsigned header_length =
 
167
                                                        FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH +
 
168
                                                        FLAC__OGG_MAPPING_MAGIC_LENGTH +
 
169
                                                        FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH +
 
170
                                                        FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH +
 
171
                                                        FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH;
 
172
                                                if (aspect->working_packet.bytes < (long)header_length)
 
173
                                                        return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC;
 
174
                                                b += FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH;
 
175
                                                if (memcmp(b, FLAC__OGG_MAPPING_MAGIC, FLAC__OGG_MAPPING_MAGIC_LENGTH))
 
176
                                                        return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC;
 
177
                                                b += FLAC__OGG_MAPPING_MAGIC_LENGTH;
 
178
                                                aspect->version_major = (unsigned)(*b);
 
179
                                                b += FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH;
 
180
                                                aspect->version_minor = (unsigned)(*b);
 
181
                                                if (aspect->version_major != 1)
 
182
                                                        return FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION;
 
183
                                                aspect->working_packet.packet += header_length;
 
184
                                                aspect->working_packet.bytes -= header_length;
 
185
                                        }
 
186
                                }
 
187
                                else if (ret == 0) {
 
188
                                        aspect->have_working_page = false;
 
189
                                }
 
190
                                else { /* ret < 0 */
 
191
                                        /* lost sync, we'll leave the working page for the next call */
 
192
                                        return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC;
 
193
                                }
 
194
                        }
 
195
                }
 
196
                else {
 
197
                        /* try and get another page */
 
198
                        const int ret = ogg_sync_pageout(&aspect->sync_state, &aspect->working_page);
 
199
                        if (ret > 0) {
 
200
                                /* got a page, grab the serial number if necessary */
 
201
                                if(aspect->need_serial_number) {
 
202
                                        aspect->stream_state.serialno = aspect->serial_number = ogg_page_serialno(&aspect->working_page);
 
203
                                        aspect->need_serial_number = false;
 
204
                                }
 
205
                                if(ogg_stream_pagein(&aspect->stream_state, &aspect->working_page) == 0) {
 
206
                                        aspect->have_working_page = true;
 
207
                                        aspect->have_working_packet = false;
 
208
                                }
 
209
                                /* else do nothing, could be a page from another stream */
 
210
                        }
 
211
                        else if (ret == 0) {
 
212
                                /* need more data */
 
213
                                const size_t ogg_bytes_to_read = max(bytes_requested - *bytes, OGG_BYTES_CHUNK);
 
214
                                char *oggbuf = ogg_sync_buffer(&aspect->sync_state, ogg_bytes_to_read);
 
215
 
 
216
                                if(0 == oggbuf) {
 
217
                                        return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR;
 
218
                                }
 
219
                                else {
 
220
                                        size_t ogg_bytes_read = ogg_bytes_to_read;
 
221
 
 
222
                                        switch(read_callback(decoder, (FLAC__byte*)oggbuf, &ogg_bytes_read, client_data)) {
 
223
                                                case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK:
 
224
                                                        break;
 
225
                                                case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM:
 
226
                                                        aspect->end_of_stream = true;
 
227
                                                        break;
 
228
                                                case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT:
 
229
                                                        return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT;
 
230
                                                default:
 
231
                                                        FLAC__ASSERT(0);
 
232
                                        }
 
233
 
 
234
                                        if(ogg_sync_wrote(&aspect->sync_state, ogg_bytes_read) < 0) {
 
235
                                                /* double protection; this will happen if the read callback returns more bytes than the max requested, which would overflow Ogg's internal buffer */
 
236
                                                FLAC__ASSERT(0);
 
237
                                                return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR;
 
238
                                        }
 
239
                                }
 
240
                        }
 
241
                        else { /* ret < 0 */
 
242
                                /* lost sync, bail out */
 
243
                                return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC;
 
244
                        }
 
245
                }
 
246
        }
 
247
 
 
248
        if (aspect->end_of_stream && *bytes == 0) {
 
249
                return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM;
 
250
        }
 
251
 
 
252
        return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK;
 
253
}