blob: bebd7f0618df2b519cd8c5f4fa604ad9bb2dfedb [file] [log] [blame]
Josh Coalsone6d52b02004-01-17 03:52:59 +00001/* libOggFLAC - Free Lossless Audio Codec + Ogg library
Josh Coalson0395dac2006-04-25 06:59:33 +00002 * Copyright (C) 2004,2005,2006 Josh Coalson
Josh Coalsone6d52b02004-01-17 03:52:59 +00003 *
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
Josh Coalsonb1ec7962006-05-24 04:41:36 +000032#if HAVE_CONFIG_H
33# include <config.h>
34#endif
35
Josh Coalsone6d52b02004-01-17 03:52:59 +000036#include <stdlib.h> /* for malloc() */
37#include <string.h> /* for memcmp(), memcpy() */
38#include "FLAC/assert.h"
39#include "private/ogg_helper.h"
40#include "protected/seekable_stream_encoder.h"
41
42
43static FLAC__bool full_read_(OggFLAC__SeekableStreamEncoder *encoder, FLAC__byte *buffer, unsigned bytes, OggFLAC__SeekableStreamEncoderReadCallback read_callback, void *client_data)
44{
45 while(bytes > 0) {
46 unsigned bytes_read = bytes;
47 switch(read_callback(encoder, buffer, &bytes_read, client_data)) {
48 case OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_CONTINUE:
49 bytes -= bytes_read;
50 buffer += bytes_read;
51 break;
52 case OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_END_OF_STREAM:
53 if(bytes_read == 0) {
54 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
55 return false;
56 }
57 bytes -= bytes_read;
58 buffer += bytes_read;
59 break;
60 case OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_ABORT:
61 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_READ_ERROR;
62 return false;
63 default:
64 /* double protection: */
65 FLAC__ASSERT(0);
66 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_READ_ERROR;
67 return false;
68 }
69 }
70
71 return true;
72}
73
74void simple_ogg_page__init(ogg_page *page)
75{
76 page->header = 0;
77 page->header_len = 0;
78 page->body = 0;
79 page->body_len = 0;
80}
81
82void simple_ogg_page__clear(ogg_page *page)
83{
84 if(page->header)
85 free(page->header);
86 if(page->body)
87 free(page->body);
88 simple_ogg_page__init(page);
89}
90
91FLAC__bool simple_ogg_page__get_at(OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, OggFLAC__SeekableStreamEncoderSeekCallback seek_callback, OggFLAC__SeekableStreamEncoderReadCallback read_callback, void *client_data)
92{
93 static const unsigned OGG_HEADER_FIXED_PORTION_LEN = 27;
94 static const unsigned OGG_MAX_HEADER_LEN = 27/*OGG_HEADER_FIXED_PORTION_LEN*/ + 255;
95 FLAC__byte crc[4];
96
Josh Coalsonf3d64292004-01-18 00:03:48 +000097 FLAC__ASSERT(page->header == 0);
98 FLAC__ASSERT(page->header_len == 0);
99 FLAC__ASSERT(page->body == 0);
100 FLAC__ASSERT(page->body_len == 0);
Josh Coalsone6d52b02004-01-17 03:52:59 +0000101
102 /* move the stream pointer to the supposed beginning of the page */
103 if(seek_callback(encoder, position, client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) {
104 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR;
105 return false;
106 }
107
108 /* allocate space for the page header */
Josh Coalson80171dc2004-12-30 03:57:13 +0000109 if(0 == (page->header = (unsigned char *)malloc(OGG_MAX_HEADER_LEN))) {
Josh Coalsone6d52b02004-01-17 03:52:59 +0000110 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
111 return false;
112 }
113
114 /* read in the fixed part of the page header (up to but not including
115 * the segment table */
116 if(!full_read_(encoder, page->header, OGG_HEADER_FIXED_PORTION_LEN, read_callback, client_data))
117 return false;
118
119 page->header_len = OGG_HEADER_FIXED_PORTION_LEN + page->header[26];
120
121 /* check to see if it's a correct, "simple" page (one packet only) */
122 if(
123 memcmp(page->header, "OggS", 4) || /* doesn't start with OggS */
124 (page->header[5] & 0x01) || /* continued packet */
125 memcmp(page->header+6, "\0\0\0\0\0\0\0\0", 8) || /* granulepos is non-zero */
126 page->header[26] == 0 /* packet is 0-size */
127 ) {
128 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
129 return false;
130 }
131
132 /* read in the segment table */
133 if(!full_read_(encoder, page->header + OGG_HEADER_FIXED_PORTION_LEN, page->header[26], read_callback, client_data))
134 return false;
135
136 {
137 unsigned i;
138
139 /* check to see that it specifies a single packet */
140 for(i = 0; i < (unsigned)page->header[26] - 1; i++) {
141 if(page->header[i + OGG_HEADER_FIXED_PORTION_LEN] != 255) {
142 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
143 return false;
144 }
145 }
146
147 page->body_len = 255 * i + page->header[i];
148 }
149
150 /* allocate space for the page body */
Josh Coalson80171dc2004-12-30 03:57:13 +0000151 if(0 == (page->body = (unsigned char *)malloc(page->body_len))) {
Josh Coalsone6d52b02004-01-17 03:52:59 +0000152 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
153 return false;
154 }
155
156 /* read in the page body */
157 if(!full_read_(encoder, page->body, page->body_len, read_callback, client_data))
158 return false;
159
160 /* check the CRC */
161 memcpy(crc, page->header+22, 4);
162 ogg_page_checksum_set(page);
163 if(memcmp(crc, page->header+22, 4)) {
164 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
165 return false;
166 }
167
168 return true;
169}
170
171FLAC__bool simple_ogg_page__set_at(OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, OggFLAC__SeekableStreamEncoderSeekCallback seek_callback, OggFLAC__SeekableStreamEncoderWriteCallback write_callback, void *client_data)
172{
173 FLAC__ASSERT(page->header != 0);
174 FLAC__ASSERT(page->header_len != 0);
175 FLAC__ASSERT(page->body != 0);
176 FLAC__ASSERT(page->body_len != 0);
177
178 /* move the stream pointer to the supposed beginning of the page */
179 if(seek_callback(encoder, position, client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) {
180 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR;
181 return false;
182 }
183
184 ogg_page_checksum_set(page);
185
186 /* re-write the page */
187 if(write_callback(encoder, page->header, page->header_len, 0, 0, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
188 encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR;
189 return false;
190 }
191
192 return true;
193}