encode.c 86.8 KB
Newer Older
Josh Coalson's avatar
Josh Coalson committed
1
/* flac - Command-line FLAC encoder/decoder
Josh Coalson's avatar
Josh Coalson committed
2
 * Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson
Josh Coalson's avatar
Josh Coalson committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#if defined _WIN32 && !defined __CYGWIN__
/* where MSVC puts unlink() */
# include <io.h>
#else
# include <unistd.h>
#endif
25
#include <limits.h> /* for LONG_MAX */
26
#include <math.h> /* for floor() */
Josh Coalson's avatar
Josh Coalson committed
27
#include <stdio.h> /* for FILE etc. */
Josh Coalson's avatar
Josh Coalson committed
28
#include <stdlib.h> /* for malloc */
Josh Coalson's avatar
Josh Coalson committed
29
30
#include <string.h> /* for strcmp() */
#include "FLAC/all.h"
31
#include "share/grabbag.h"
Josh Coalson's avatar
Josh Coalson committed
32
#include "encode.h"
33
34
35
36
37

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

38
#ifdef FLAC__HAS_OGG
39
#include "OggFLAC/stream_encoder.h"
40
#include "OggFLAC/file_encoder.h"
41
#endif
Josh Coalson's avatar
Josh Coalson committed
42

Josh Coalson's avatar
Josh Coalson committed
43
44
45
46
#ifdef min
#undef min
#endif
#define min(x,y) ((x)<(y)?(x):(y))
47
48
49
50
#ifdef max
#undef max
#endif
#define max(x,y) ((x)>(y)?(x):(y))
Josh Coalson's avatar
Josh Coalson committed
51

Josh Coalson's avatar
Josh Coalson committed
52
/* this MUST be >= 588 so that sector aligning can take place with one read */
Josh Coalson's avatar
Josh Coalson committed
53
54
55
#define CHUNK_OF_SAMPLES 2048

typedef struct {
Josh Coalson's avatar
Josh Coalson committed
56
57
58
#ifdef FLAC__HAS_OGG
	FLAC__bool use_ogg;
#endif
Josh Coalson's avatar
Josh Coalson committed
59
	FLAC__bool verify;
60
	FLAC__bool is_stdout;
Josh Coalson's avatar
Josh Coalson committed
61
62
63
	const char *inbasefilename;
	const char *outfilename;

64
	FLAC__uint64 skip;
65
	FLAC__uint64 until; /* a value of 0 mean end-of-stream (i.e. --until=-0) */
66
67
68
69
	FLAC__bool replay_gain;
	unsigned channels;
	unsigned bits_per_sample;
	unsigned sample_rate;
Josh Coalson's avatar
Josh Coalson committed
70
71
72
73
	FLAC__uint64 unencoded_size;
	FLAC__uint64 total_samples_to_encode;
	FLAC__uint64 bytes_written;
	FLAC__uint64 samples_written;
Josh Coalson's avatar
Josh Coalson committed
74
75
	unsigned blocksize;
	unsigned stats_mask;
Josh Coalson's avatar
Josh Coalson committed
76

77
	/*
78
79
	 * We use *.stream for encoding to stdout
	 * We use *.file for encoding to a regular file
80
	 */
Josh Coalson's avatar
Josh Coalson committed
81
	union {
82
83
84
85
		union {
			FLAC__StreamEncoder *stream;
			FLAC__FileEncoder *file;
		} flac;
86
#ifdef FLAC__HAS_OGG
87
88
		union {
			OggFLAC__StreamEncoder *stream;
89
			OggFLAC__FileEncoder *file;
90
		} ogg;
91
#endif
Josh Coalson's avatar
Josh Coalson committed
92
93
94
95
96
97
	} encoder;

	FILE *fin;
	FILE *fout;
	FLAC__StreamMetadata *seek_table_template;
} EncoderSession;
Josh Coalson's avatar
Josh Coalson committed
98
99


100
static FLAC__bool is_big_endian_host_;
Josh Coalson's avatar
Josh Coalson committed
101

102
103
104
105
static unsigned char ucbuffer_[CHUNK_OF_SAMPLES*FLAC__MAX_CHANNELS*((FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE+7)/8)];
static signed char *scbuffer_ = (signed char *)ucbuffer_;
static FLAC__uint16 *usbuffer_ = (FLAC__uint16 *)ucbuffer_;
static FLAC__int16 *ssbuffer_ = (FLAC__int16 *)ucbuffer_;
Josh Coalson's avatar
Josh Coalson committed
106

107
108
109
110
static FLAC__int32 in_[FLAC__MAX_CHANNELS][CHUNK_OF_SAMPLES];
static FLAC__int32 *input_[FLAC__MAX_CHANNELS];


111
112
113
114
115
116
117
118
119
/*
 * unpublished debug routines from the FLAC libs
 */
extern FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__file_encoder_disable_constant_subframes(FLAC__FileEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__file_encoder_disable_fixed_subframes(FLAC__FileEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__file_encoder_disable_verbatim_subframes(FLAC__FileEncoder *encoder, FLAC__bool value);
120
#ifdef FLAC__HAS_OGG
121
122
123
extern FLAC__bool OggFLAC__stream_encoder_disable_constant_subframes(OggFLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool OggFLAC__stream_encoder_disable_fixed_subframes(OggFLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool OggFLAC__stream_encoder_disable_verbatim_subframes(OggFLAC__StreamEncoder *encoder, FLAC__bool value);
124
125
126
extern FLAC__bool OggFLAC__file_encoder_disable_constant_subframes(OggFLAC__FileEncoder *encoder, FLAC__bool value);
extern FLAC__bool OggFLAC__file_encoder_disable_fixed_subframes(OggFLAC__FileEncoder *encoder, FLAC__bool value);
extern FLAC__bool OggFLAC__file_encoder_disable_verbatim_subframes(OggFLAC__FileEncoder *encoder, FLAC__bool value);
127
#endif
128

129
130
131
/*
 * local routines
 */
132
static FLAC__bool EncoderSession_construct(EncoderSession *e, FLAC__bool use_ogg, FLAC__bool verify, FILE *infile, const char *infilename, const char *outfilename);
133
134
135
136
137
static void EncoderSession_destroy(EncoderSession *e);
static int EncoderSession_finish_ok(EncoderSession *e, int info_align_carry, int info_align_zero);
static int EncoderSession_finish_error(EncoderSession *e);
static FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate);
static FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const buffer[], unsigned samples);
138
static FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, FLAC__StreamMetadata *cuesheet, EncoderSession *e);
139
static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, unsigned sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input);
140
static void format_input(FLAC__int32 *dest[], unsigned wide_samples, FLAC__bool is_big_endian, FLAC__bool is_unsigned_samples, unsigned channels, unsigned bps);
141
142
#ifdef FLAC__HAS_OGG
static FLAC__StreamEncoderWriteStatus ogg_stream_encoder_write_callback(const OggFLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
143
static void ogg_stream_encoder_metadata_callback(const OggFLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data);
144
static void ogg_file_encoder_progress_callback(const OggFLAC__FileEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data);
145
146
#endif
static FLAC__StreamEncoderWriteStatus flac_stream_encoder_write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
Josh Coalson's avatar
Josh Coalson committed
147
148
static void flac_stream_encoder_metadata_callback(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data);
static void flac_file_encoder_progress_callback(const FLAC__FileEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data);
149
static FLAC__bool parse_cuesheet_(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset);
Josh Coalson's avatar
Josh Coalson committed
150
static void print_stats(const EncoderSession *encoder_session);
151
152
static void print_error_with_state(const EncoderSession *e, const char *message);
static void print_verify_error(EncoderSession *e);
153
154
155
156
157
static FLAC__bool read_little_endian_uint16(FILE *f, FLAC__uint16 *val, FLAC__bool eof_ok, const char *fn);
static FLAC__bool read_little_endian_uint32(FILE *f, FLAC__uint32 *val, FLAC__bool eof_ok, const char *fn);
static FLAC__bool read_big_endian_uint16(FILE *f, FLAC__uint16 *val, FLAC__bool eof_ok, const char *fn);
static FLAC__bool read_big_endian_uint32(FILE *f, FLAC__uint32 *val, FLAC__bool eof_ok, const char *fn);
static FLAC__bool read_sane_extended(FILE *f, FLAC__uint32 *val, FLAC__bool eof_ok, const char *fn);
158
static FLAC__bool fskip_ahead(FILE *f, FLAC__uint64 offset);
Josh Coalson's avatar
Josh Coalson committed
159

160
161
162
/*
 * public routines
 */
163
int flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options, FLAC__bool is_aifc)
164
{
Josh Coalson's avatar
Josh Coalson committed
165
	EncoderSession encoder_session;
166
167
168
169
170
	FLAC__uint16 x;
	FLAC__uint32 xx;
	unsigned int channels= 0U, bps= 0U, sample_rate= 0U, sample_frames= 0U;
	FLAC__bool got_comm_chunk= false, got_ssnd_chunk= false;
	int info_align_carry= -1, info_align_zero= -1;
171
	FLAC__bool is_big_endian_pcm = true;
172
173
174
175
176

	(void)infilesize; /* silence compiler warning about unused parameter */
	(void)lookahead; /* silence compiler warning about unused parameter */
	(void)lookahead_length; /* silence compiler warning about unused parameter */

177
178
179
180
181
182
183
184
185
186
187
188
189
190
	if(!
		EncoderSession_construct(
			&encoder_session,
#ifdef FLAC__HAS_OGG
			options.common.use_ogg,
#else
			/*use_ogg=*/false,
#endif
			options.common.verify,
			infile,
			infilename,
			outfilename
		)
	)
Josh Coalson's avatar
Josh Coalson committed
191
		return 1;
192
193
194

	/* lookahead[] already has "FORMxxxxAIFF", do sub-chunks */

195
	while(1) {
196
197
198
199
200
		size_t c= 0U;
		char chunk_id[4];

		/* chunk identifier; really conservative about behavior of fread() and feof() */
		if(feof(infile) || ((c= fread(chunk_id, 1U, 4U, infile)), c==0U && feof(infile)))
201
			break;
202
		else if(c<4U || feof(infile)) {
203
			flac__utils_printf(stderr, 1, "%s: ERROR: incomplete chunk identifier\n", encoder_session.inbasefilename);
204
			return EncoderSession_finish_error(&encoder_session);
205
206
		}

207
		if(got_comm_chunk==false && !memcmp(chunk_id, "COMM", 4)) { /* common chunk */
208
			unsigned long skip;
209
			const FLAC__uint32 minimum_comm_size = (is_aifc? 22 : 18);
210

211
			/* COMM chunk size */
212
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
213
				return EncoderSession_finish_error(&encoder_session);
214
215
			else if(xx<minimum_comm_size) {
				flac__utils_printf(stderr, 1, "%s: ERROR: non-standard %s 'COMM' chunk has length = %u\n", encoder_session.inbasefilename, is_aifc? "AIFF-C" : "AIFF", (unsigned int)xx);
216
				return EncoderSession_finish_error(&encoder_session);
217
			}
218
219
			else if(!is_aifc && xx!=minimum_comm_size) {
				flac__utils_printf(stderr, 1, "%s: WARNING: non-standard %s 'COMM' chunk has length = %u, expected %u\n", encoder_session.inbasefilename, is_aifc? "AIFF-C" : "AIFF", (unsigned int)xx, minimum_comm_size);
220
			}
221
			skip= (xx-minimum_comm_size)+(xx & 1U);
222

223
			/* number of channels */
224
			if(!read_big_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
225
226
				return EncoderSession_finish_error(&encoder_session);
			else if(x==0U || x>FLAC__MAX_CHANNELS) {
227
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u\n", encoder_session.inbasefilename, (unsigned int)x);
228
				return EncoderSession_finish_error(&encoder_session);
229
			}
230
			else if(options.common.sector_align && x!=2U) {
231
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, (unsigned int)x);
232
				return EncoderSession_finish_error(&encoder_session);
233
			}
234
			channels= x;
235

236
			/* number of sample frames */
237
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
238
239
240
241
				return EncoderSession_finish_error(&encoder_session);
			sample_frames= xx;

			/* bits per sample */
242
			if(!read_big_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
243
244
				return EncoderSession_finish_error(&encoder_session);
			else if(x!=8U && x!=16U && x!=24U) {
245
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported bits per sample %u\n", encoder_session.inbasefilename, (unsigned int)x);
246
				return EncoderSession_finish_error(&encoder_session);
247
			}
248
			else if(options.common.sector_align && x!=16U) {
249
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u bits per sample, must be 16 for --sector-align\n", encoder_session.inbasefilename, (unsigned int)x);
250
251
252
				return EncoderSession_finish_error(&encoder_session);
			}
			bps= x;
253

254
			/* sample rate */
255
			if(!read_sane_extended(infile, &xx, false, encoder_session.inbasefilename))
256
257
				return EncoderSession_finish_error(&encoder_session);
			else if(!FLAC__format_sample_rate_is_valid(xx)) {
258
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported sample rate %u\n", encoder_session.inbasefilename, (unsigned int)xx);
259
260
261
				return EncoderSession_finish_error(&encoder_session);
			}
			else if(options.common.sector_align && xx!=44100U) {
262
				flac__utils_printf(stderr, 1, "%s: ERROR: file's sample rate is %u, must be 44100 for --sector-align\n", encoder_session.inbasefilename, (unsigned int)xx);
263
				return EncoderSession_finish_error(&encoder_session);
264
			}
265
			sample_rate= xx;
266

267
268
269
270
271
272
273
274
275
276
277
278
279
280
			/* check compression type for AIFF-C */
			if(is_aifc) {
				if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
					return EncoderSession_finish_error(&encoder_session);
				if(xx == 0x736F7774) /* "sowt" */
					is_big_endian_pcm = false;
				else if(xx == 0x4E4F4E45) /* "NONE" */
					; /* nothing to do, we already default to big-endian */
				else {
					flac__utils_printf(stderr, 1, "%s: ERROR: can't handle AIFF-C compression type \"%c%c%c%c\"\n", encoder_session.inbasefilename, (char)(xx>>24), (char)((xx>>16)&8), (char)((xx>>8)&8), (char)(xx&8));
					return EncoderSession_finish_error(&encoder_session);
				}
			}

281
			/* skip any extra data in the COMM chunk */
282
283
284
			if(!fskip_ahead(infile, skip)) {
				flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping extra COMM data\n", encoder_session.inbasefilename);
				return EncoderSession_finish_error(&encoder_session);
285
286
			}

287
288
289
290
291
292
293
294
295
			/*
			 * now that we know the sample rate, canonicalize the
			 * --skip string to a number of samples:
			 */
			flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, sample_rate);
			FLAC__ASSERT(options.common.skip_specification.value.samples >= 0);
			encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples;
			FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0);

296
297
			got_comm_chunk= true;
		}
298
		else if(got_ssnd_chunk==false && !memcmp(chunk_id, "SSND", 4)) { /* sound data chunk */
299
300
			unsigned int offset= 0U, block_size= 0U, align_remainder= 0U, data_bytes;
			size_t bytes_per_frame= channels*(bps>>3);
301
			FLAC__uint64 total_samples_in_input, trim = 0;
302
303
			FLAC__bool pad= false;

304
			if(got_comm_chunk==false) {
305
				flac__utils_printf(stderr, 1, "%s: ERROR: got 'SSND' chunk before 'COMM' chunk\n", encoder_session.inbasefilename);
306
				return EncoderSession_finish_error(&encoder_session);
307
308
			}

309
			/* SSND chunk size */
310
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
311
312
313
				return EncoderSession_finish_error(&encoder_session);
			data_bytes= xx;
			pad= (data_bytes & 1U) ? true : false;
314
			data_bytes-= 8U; /* discount the offset and block size fields */
315

316
			/* offset */
317
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
318
319
				return EncoderSession_finish_error(&encoder_session);
			offset= xx;
320
			data_bytes-= offset;
321

322
			/* block size */
323
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
324
325
				return EncoderSession_finish_error(&encoder_session);
			else if(xx!=0U) {
326
				flac__utils_printf(stderr, 1, "%s: ERROR: block size is %u; must be 0\n", encoder_session.inbasefilename, (unsigned int)xx);
327
				return EncoderSession_finish_error(&encoder_session);
328
			}
329
			block_size= xx;
330

331
332
333
			/* skip any SSND offset bytes */
			FLAC__ASSERT(offset<=LONG_MAX);
			if(!fskip_ahead(infile, offset)) {
334
335
336
337
338
339
340
341
				flac__utils_printf(stderr, 1, "%s: ERROR: skipping offset in SSND chunk\n", encoder_session.inbasefilename);
				return EncoderSession_finish_error(&encoder_session);
			}
			if(data_bytes!=(sample_frames*bytes_per_frame)) {
				flac__utils_printf(stderr, 1, "%s: ERROR: SSND chunk size inconsistent with sample frame count\n", encoder_session.inbasefilename);
				return EncoderSession_finish_error(&encoder_session);
			}

342
343
344
345
346
347
348
349
			/* *options.common.align_reservoir_samples will be 0 unless --sector-align is used */
			FLAC__ASSERT(options.common.sector_align || *options.common.align_reservoir_samples == 0);
			total_samples_in_input = data_bytes / bytes_per_frame + *options.common.align_reservoir_samples;

			/*
			 * now that we know the input size, canonicalize the
			 * --until string to an absolute sample number:
			 */
350
			if(!canonicalize_until_specification(&options.common.until_specification, encoder_session.inbasefilename, sample_rate, encoder_session.skip, total_samples_in_input))
351
352
353
354
				return EncoderSession_finish_error(&encoder_session);
			encoder_session.until = (FLAC__uint64)options.common.until_specification.value.samples;
			FLAC__ASSERT(!options.common.sector_align || encoder_session.until == 0);

355
			if(encoder_session.skip>0U) {
356
357
358
				if(!fskip_ahead(infile, encoder_session.skip*bytes_per_frame)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
359
360
361
				}
			}

362
363
364
			data_bytes-= (unsigned int)encoder_session.skip*bytes_per_frame; /*@@@ WATCHOUT: 4GB limit */
			encoder_session.total_samples_to_encode= total_samples_in_input - encoder_session.skip;
			if(encoder_session.until > 0) {
365
				trim = total_samples_in_input - encoder_session.until;
366
367
368
369
370
				FLAC__ASSERT(total_samples_in_input > 0);
				FLAC__ASSERT(!options.common.sector_align);
				data_bytes-= (unsigned int)trim*bytes_per_frame;
				encoder_session.total_samples_to_encode-= trim;
			}
371
372
373
374
375
376
377
			if(options.common.sector_align) {
				align_remainder= (unsigned int)(encoder_session.total_samples_to_encode % 588U);
				if(options.common.is_last_file)
					encoder_session.total_samples_to_encode+= (588U-align_remainder); /* will pad with zeroes */
				else
					encoder_session.total_samples_to_encode-= align_remainder; /* will stop short and carry over to next file */
			}
378

379
380
			/* +54 for the size of the AIFF headers; this is just an estimate for the progress indicator and doesn't need to be exact */
			encoder_session.unencoded_size= encoder_session.total_samples_to_encode*bytes_per_frame+54;
381

382
383
			if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate))
				return EncoderSession_finish_error(&encoder_session);
384
385

			/* first do any samples in the reservoir */
386
			if(options.common.sector_align && *options.common.align_reservoir_samples>0U) {
387

388
389
390
				if(!EncoderSession_process(&encoder_session, (const FLAC__int32 *const *)options.common.align_reservoir, *options.common.align_reservoir_samples)) {
					print_error_with_state(&encoder_session, "ERROR during encoding");
					return EncoderSession_finish_error(&encoder_session);
391
392
393
394
				}
			}

			/* decrement the data_bytes counter if we need to align the file */
395
			if(options.common.sector_align) {
396
397
398
399
400
401
402
403
404
				if(options.common.is_last_file)
					*options.common.align_reservoir_samples= 0U;
				else {
					*options.common.align_reservoir_samples= align_remainder;
					data_bytes-= (*options.common.align_reservoir_samples)*bytes_per_frame;
				}
			}

			/* now do from the file */
405
406
			while(data_bytes>0) {
				size_t bytes_read= fread(ucbuffer_, 1U, min(data_bytes, CHUNK_OF_SAMPLES*bytes_per_frame), infile);
407
408
409

				if(bytes_read==0U) {
					if(ferror(infile)) {
410
						flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
411
						return EncoderSession_finish_error(&encoder_session);
412
413
					}
					else if(feof(infile)) {
414
						flac__utils_printf(stderr, 1, "%s: WARNING: unexpected EOF; expected %u samples, got %u samples\n", encoder_session.inbasefilename, (unsigned int)encoder_session.total_samples_to_encode, (unsigned int)encoder_session.samples_written);
415
416
417
418
419
						data_bytes= 0;
					}
				}
				else {
					if(bytes_read % bytes_per_frame != 0U) {
420
						flac__utils_printf(stderr, 1, "%s: ERROR: got partial sample\n", encoder_session.inbasefilename);
421
						return EncoderSession_finish_error(&encoder_session);
422
423
424
					}
					else {
						unsigned int frames= bytes_read/bytes_per_frame;
425
						format_input(input_, frames, is_big_endian_pcm, /*is_unsigned_samples=*/false, channels, bps);
426

427
428
429
						if(!EncoderSession_process(&encoder_session, (const FLAC__int32 *const *)input_, frames)) {
							print_error_with_state(&encoder_session, "ERROR during encoding");
							return EncoderSession_finish_error(&encoder_session);
430
431
432
433
434
435
436
						}
						else
							data_bytes-= bytes_read;
					}
				}
			}

437
438
			if(trim>0) {
				FLAC__ASSERT(!options.common.sector_align);
439
440
441
				if(!fskip_ahead(infile, trim*bytes_per_frame)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
442
443
444
				}
			}

445
			/* now read unaligned samples into reservoir or pad with zeroes if necessary */
446
			if(options.common.sector_align) {
447
448
449
450
451
452
453
454
				if(options.common.is_last_file) {
					unsigned int pad_frames= 588U-align_remainder;

					if(pad_frames<588U) {
						unsigned int i;

						info_align_zero= pad_frames;
						for(i= 0U; i<channels; ++i)
455
							memset(input_[i], 0, pad_frames*(bps>>3));
456

457
458
459
						if(!EncoderSession_process(&encoder_session, (const FLAC__int32 *const *)input_, pad_frames)) {
							print_error_with_state(&encoder_session, "ERROR during encoding");
							return EncoderSession_finish_error(&encoder_session);
460
461
462
463
464
						}
					}
				}
				else {
					if(*options.common.align_reservoir_samples > 0) {
465
						size_t bytes_read= fread(ucbuffer_, 1U, (*options.common.align_reservoir_samples)*bytes_per_frame, infile);
466
467
468

						FLAC__ASSERT(CHUNK_OF_SAMPLES>=588U);
						if(bytes_read==0U && ferror(infile)) {
469
							flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
470
							return EncoderSession_finish_error(&encoder_session);
471
						}
472
						else if(bytes_read != (*options.common.align_reservoir_samples) * bytes_per_frame) {
473
							flac__utils_printf(stderr, 1, "%s: WARNING: unexpected EOF; read %u bytes; expected %u samples, got %u samples\n", encoder_session.inbasefilename, (unsigned int)bytes_read, (unsigned int)encoder_session.total_samples_to_encode, (unsigned int)encoder_session.samples_written);
474
						}
475
476
						else {
							info_align_carry= *options.common.align_reservoir_samples;
477
							format_input(options.common.align_reservoir, *options.common.align_reservoir_samples, is_big_endian_pcm, /*is_unsigned_samples=*/false, channels, bps);
478
479
480
481
482
						}
					}
				}
			}

483
			if(pad==true) {
484
485
486
				unsigned char tmp;

				if(fread(&tmp, 1U, 1U, infile)<1U) {
487
					flac__utils_printf(stderr, 1, "%s: ERROR during read of SSND pad byte\n", encoder_session.inbasefilename);
488
					return EncoderSession_finish_error(&encoder_session);
489
490
491
492
493
				}
			}

			got_ssnd_chunk= true;
		}
494
		else { /* other chunk */
495
			if(!memcmp(chunk_id, "COMM", 4)) {
496
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'COMM' chunk\n", encoder_session.inbasefilename);
497
			}
498
			else if(!memcmp(chunk_id, "SSND", 4)) {
499
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'SSND' chunk\n", encoder_session.inbasefilename);
500
501
			}
			else {
502
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping unknown chunk '%s'\n", encoder_session.inbasefilename, chunk_id);
503
			}
504
505

			/* chunk size */
506
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
507
				return EncoderSession_finish_error(&encoder_session);
508
509
510
511
			else {
				unsigned long skip= xx+(xx & 1U);

				FLAC__ASSERT(skip<=LONG_MAX);
512
513
514
				if(!fskip_ahead(infile, skip)) {
					fprintf(stderr, "%s: ERROR during read while skipping unknown chunk\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
515
516
517
518
519
520
				}
			}
		}
	}

	if(got_ssnd_chunk==false && sample_frames!=0U) {
521
		flac__utils_printf(stderr, 1, "%s: ERROR: missing SSND chunk\n", encoder_session.inbasefilename);
522
		return EncoderSession_finish_error(&encoder_session);
523
524
	}

525
	return EncoderSession_finish_ok(&encoder_session, info_align_carry, info_align_zero);
526
527
}

528
int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options)
Josh Coalson's avatar
Josh Coalson committed
529
{
Josh Coalson's avatar
Josh Coalson committed
530
	EncoderSession encoder_session;
Josh Coalson's avatar
Josh Coalson committed
531
	FLAC__bool is_unsigned_samples = false;
532
	unsigned channels = 0, bps = 0, sample_rate = 0, data_bytes;
Josh Coalson's avatar
Josh Coalson committed
533
	size_t bytes_per_wide_sample, bytes_read;
Josh Coalson's avatar
Josh Coalson committed
534
535
536
	FLAC__uint16 x;
	FLAC__uint32 xx;
	FLAC__bool got_fmt_chunk = false, got_data_chunk = false;
Josh Coalson's avatar
Josh Coalson committed
537
538
539
	unsigned align_remainder = 0;
	int info_align_carry = -1, info_align_zero = -1;

540
541
542
	(void)infilesize;
	(void)lookahead;
	(void)lookahead_length;
Josh Coalson's avatar
Josh Coalson committed
543

544
545
546
547
548
549
550
551
552
553
554
555
556
557
	if(!
		EncoderSession_construct(
			&encoder_session,
#ifdef FLAC__HAS_OGG
			options.common.use_ogg,
#else
			/*use_ogg=*/false,
#endif
			options.common.verify,
			infile,
			infilename,
			outfilename
		)
	)
558
		return 1;
Josh Coalson's avatar
Josh Coalson committed
559
560

	/*
561
	 * lookahead[] already has "RIFFxxxxWAVE", do sub-chunks
Josh Coalson's avatar
Josh Coalson committed
562
	 */
563
	while(!feof(infile)) {
564
		if(!read_little_endian_uint32(infile, &xx, true, encoder_session.inbasefilename))
565
			return EncoderSession_finish_error(&encoder_session);
566
567
		if(feof(infile))
			break;
568
		if(xx == 0x20746d66 && !got_fmt_chunk) { /* "fmt " */
569
570
			unsigned block_align;

571
			/* fmt sub-chunk size */
572
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
573
				return EncoderSession_finish_error(&encoder_session);
574
			if(xx < 16) {
575
				flac__utils_printf(stderr, 1, "%s: ERROR: found non-standard 'fmt ' sub-chunk which has length = %u\n", encoder_session.inbasefilename, (unsigned)xx);
576
				return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
577
			}
578
			else if(xx != 16 && xx != 18) {
579
				flac__utils_printf(stderr, 1, "%s: WARNING: found non-standard 'fmt ' sub-chunk which has length = %u\n", encoder_session.inbasefilename, (unsigned)xx);
580
581
582
			}
			data_bytes = xx;
			/* compression code */
583
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
584
				return EncoderSession_finish_error(&encoder_session);
585
			if(x != 1) {
586
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported compression type %u\n", encoder_session.inbasefilename, (unsigned)x);
587
				return EncoderSession_finish_error(&encoder_session);
588
589
			}
			/* number of channels */
590
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
591
				return EncoderSession_finish_error(&encoder_session);
592
			if(x == 0 || x > FLAC__MAX_CHANNELS) {
593
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u\n", encoder_session.inbasefilename, (unsigned)x);
594
				return EncoderSession_finish_error(&encoder_session);
595
596
			}
			else if(options.common.sector_align && x != 2) {
597
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, (unsigned)x);
598
				return EncoderSession_finish_error(&encoder_session);
599
600
601
			}
			channels = x;
			/* sample rate */
602
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
603
				return EncoderSession_finish_error(&encoder_session);
604
			if(!FLAC__format_sample_rate_is_valid(xx)) {
605
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported sample rate %u\n", encoder_session.inbasefilename, (unsigned)xx);
606
				return EncoderSession_finish_error(&encoder_session);
607
608
			}
			else if(options.common.sector_align && xx != 44100) {
609
				flac__utils_printf(stderr, 1, "%s: ERROR: file's sample rate is %u, must be 44100 for --sector-align\n", encoder_session.inbasefilename, (unsigned)xx);
610
				return EncoderSession_finish_error(&encoder_session);
611
612
613
			}
			sample_rate = xx;
			/* avg bytes per second (ignored) */
614
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
615
				return EncoderSession_finish_error(&encoder_session);
616
			/* block align */
617
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
618
				return EncoderSession_finish_error(&encoder_session);
619
			block_align = x;
620
			/* bits per sample */
621
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
622
				return EncoderSession_finish_error(&encoder_session);
623
			if(x != 8 && x != 16 && x != 24) {
624
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported bits-per-sample %u\n", encoder_session.inbasefilename, (unsigned)x);
625
				return EncoderSession_finish_error(&encoder_session);
626
627
			}
			else if(options.common.sector_align && x != 16) {
628
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u bits per sample, must be 16 for --sector-align\n", encoder_session.inbasefilename, (unsigned)x);
629
				return EncoderSession_finish_error(&encoder_session);
630
631
			}
			bps = x;
632
			if(bps * channels != block_align * 8) {
633
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported block alignment (%u), for bits-per-sample=%u, channels=%u\n", encoder_session.inbasefilename, block_align, bps, channels);
634
635
				return EncoderSession_finish_error(&encoder_session);
			}
636
637
638
639
640
641
642
643
			is_unsigned_samples = (x == 8);

			/* skip any extra data in the fmt sub-chunk */
			data_bytes -= 16;
			if(data_bytes > 0) {
				unsigned left, need;
				for(left = data_bytes; left > 0; ) {
					need = min(left, CHUNK_OF_SAMPLES);
644
					if(fread(ucbuffer_, 1U, need, infile) < need) {
645
						flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
646
						return EncoderSession_finish_error(&encoder_session);
647
648
					}
					left -= need;
649
				}
650
651
			}

652
653
654
655
656
657
658
659
660
			/*
			 * now that we know the sample rate, canonicalize the
			 * --skip string to a number of samples:
			 */
			flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, sample_rate);
			FLAC__ASSERT(options.common.skip_specification.value.samples >= 0);
			encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples;
			FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0);

661
662
663
			got_fmt_chunk = true;
		}
		else if(xx == 0x61746164 && !got_data_chunk && got_fmt_chunk) { /* "data" */
664
			FLAC__uint64 total_samples_in_input, trim = 0;
665
			FLAC__bool pad = false;
666

667
			/* data size */
668
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
669
				return EncoderSession_finish_error(&encoder_session);
670
			data_bytes = xx;
671
			pad = (data_bytes & 1U) ? true : false;
672
673

			bytes_per_wide_sample = channels * (bps >> 3);
Josh Coalson's avatar
Josh Coalson committed
674

675
676
677
678
679
680
681
682
			/* *options.common.align_reservoir_samples will be 0 unless --sector-align is used */
			FLAC__ASSERT(options.common.sector_align || *options.common.align_reservoir_samples == 0);
			total_samples_in_input = data_bytes / bytes_per_wide_sample + *options.common.align_reservoir_samples;

			/*
			 * now that we know the input size, canonicalize the
			 * --until string to an absolute sample number:
			 */
683
			if(!canonicalize_until_specification(&options.common.until_specification, encoder_session.inbasefilename, sample_rate, encoder_session.skip, total_samples_in_input))
684
685
686
687
				return EncoderSession_finish_error(&encoder_session);
			encoder_session.until = (FLAC__uint64)options.common.until_specification.value.samples;
			FLAC__ASSERT(!options.common.sector_align || encoder_session.until == 0);

688
			if(encoder_session.skip > 0) {
689
690
691
				if(!fskip_ahead(infile, encoder_session.skip * bytes_per_wide_sample)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
692
				}
Josh Coalson's avatar
Josh Coalson committed
693
			}
694

695
			data_bytes -= (unsigned)encoder_session.skip * bytes_per_wide_sample; /*@@@ WATCHOUT: 4GB limit */
696
697
			encoder_session.total_samples_to_encode = total_samples_in_input - encoder_session.skip;
			if(encoder_session.until > 0) {
698
				trim = total_samples_in_input - encoder_session.until;
699
700
701
702
703
				FLAC__ASSERT(total_samples_in_input > 0);
				FLAC__ASSERT(!options.common.sector_align);
				data_bytes -= (unsigned int)trim * bytes_per_wide_sample;
				encoder_session.total_samples_to_encode -= trim;
			}
704
			if(options.common.sector_align) {
Josh Coalson's avatar
Josh Coalson committed
705
				align_remainder = (unsigned)(encoder_session.total_samples_to_encode % 588);
706
				if(options.common.is_last_file)
Josh Coalson's avatar
Josh Coalson committed
707
					encoder_session.total_samples_to_encode += (588-align_remainder); /* will pad with zeroes */
708
				else
Josh Coalson's avatar
Josh Coalson committed
709
					encoder_session.total_samples_to_encode -= align_remainder; /* will stop short and carry over to next file */
710
			}
711
712

			/* +44 for the size of the WAV headers; this is just an estimate for the progress indicator and doesn't need to be exact */
Josh Coalson's avatar
Josh Coalson committed
713
			encoder_session.unencoded_size = encoder_session.total_samples_to_encode * bytes_per_wide_sample + 44;
714

715
716
			if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate))
				return EncoderSession_finish_error(&encoder_session);
717

718
719
720
721
			/*
			 * first do any samples in the reservoir
			 */
			if(options.common.sector_align && *options.common.align_reservoir_samples > 0) {
722
723
724
				if(!EncoderSession_process(&encoder_session, (const FLAC__int32 * const *)options.common.align_reservoir, *options.common.align_reservoir_samples)) {
					print_error_with_state(&encoder_session, "ERROR during encoding");
					return EncoderSession_finish_error(&encoder_session);
725
726
				}
			}
727

728
729
730
731
732
733
734
735
736
737
738
739
			/*
			 * decrement the data_bytes counter if we need to align the file
			 */
			if(options.common.sector_align) {
				if(options.common.is_last_file) {
					*options.common.align_reservoir_samples = 0;
				}
				else {
					*options.common.align_reservoir_samples = align_remainder;
					data_bytes -= (*options.common.align_reservoir_samples) * bytes_per_wide_sample;
				}
			}
Josh Coalson's avatar
Josh Coalson committed
740

741
742
743
744
			/*
			 * now do from the file
			 */
			while(data_bytes > 0) {
745
				bytes_read = fread(ucbuffer_, sizeof(unsigned char), min(data_bytes, CHUNK_OF_SAMPLES * bytes_per_wide_sample), infile);
746
747
				if(bytes_read == 0) {
					if(ferror(infile)) {
748
						flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
749
						return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
750
					}
751
					else if(feof(infile)) {
752
						flac__utils_printf(stderr, 1, "%s: WARNING: unexpected EOF; expected %u samples, got %u samples\n", encoder_session.inbasefilename, (unsigned)encoder_session.total_samples_to_encode, (unsigned)encoder_session.samples_written);
753
754
						data_bytes = 0;
					}
Josh Coalson's avatar
Josh Coalson committed
755
				}
756
757
				else {
					if(bytes_read % bytes_per_wide_sample != 0) {
758
						flac__utils_printf(stderr, 1, "%s: ERROR: got partial sample\n", encoder_session.inbasefilename);
759
						return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
760
761
					}
					else {
762
						unsigned wide_samples = bytes_read / bytes_per_wide_sample;
763
						format_input(input_, wide_samples, /*is_big_endian=*/false, is_unsigned_samples, channels, bps);
Josh Coalson's avatar
Josh Coalson committed
764

765
766
767
						if(!EncoderSession_process(&encoder_session, (const FLAC__int32 * const *)input_, wide_samples)) {
							print_error_with_state(&encoder_session, "ERROR during encoding");
							return EncoderSession_finish_error(&encoder_session);
768
						}
769
						data_bytes -= bytes_read;
770
					}
771
772
773
				}
			}

774
			if(trim > 0) {
775
776
777
778
				FLAC__ASSERT(!options.common.sector_align);
				if(!fskip_ahead(infile, trim * bytes_per_wide_sample)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
779
780
781
				}
			}

782
783
784
785
786
787
788
789
790
791
792
793
			/*
			 * now read unaligned samples into reservoir or pad with zeroes if necessary
			 */
			if(options.common.sector_align) {
				if(options.common.is_last_file) {
					unsigned wide_samples = 588 - align_remainder;
					if(wide_samples < 588) {
						unsigned channel;

						info_align_zero = wide_samples;
						data_bytes = wide_samples * (bps >> 3);
						for(channel = 0; channel < channels; channel++)
794
							memset(input_[channel], 0, data_bytes);
795

796
797
798
						if(!EncoderSession_process(&encoder_session, (const FLAC__int32 * const *)input_, wide_samples)) {
							print_error_with_state(&encoder_session, "ERROR during encoding");
							return EncoderSession_finish_error(&encoder_session);
799
800
						}
					}
801
				}
802
803
804
				else {
					if(*options.common.align_reservoir_samples > 0) {
						FLAC__ASSERT(CHUNK_OF_SAMPLES >= 588);
805
						bytes_read = fread(ucbuffer_, sizeof(unsigned char), (*options.common.align_reservoir_samples) * bytes_per_wide_sample, infile);
806
						if(bytes_read == 0 && ferror(infile)) {
807
							flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
808
							return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
809
						}
810
						else if(bytes_read != (*options.common.align_reservoir_samples) * bytes_per_wide_sample) {
811
							flac__utils_printf(stderr, 1, "%s: WARNING: unexpected EOF; read %u bytes; expected %u samples, got %u samples\n", encoder_session.inbasefilename, (unsigned)bytes_read, (unsigned)encoder_session.total_samples_to_encode, (unsigned)encoder_session.samples_written);
812
813
814
815
							data_bytes = 0;
						}
						else {
							info_align_carry = *options.common.align_reservoir_samples;
816
							format_input(options.common.align_reservoir, *options.common.align_reservoir_samples, /*is_big_endian=*/false, is_unsigned_samples, channels, bps);
Josh Coalson's avatar
Josh Coalson committed
817
818
819
						}
					}
				}
820
			}
821

822
823
824
825
826
827
828
829
830
			if(pad == true) {
				unsigned char tmp;

				if(fread(&tmp, 1U, 1U, infile) < 1U) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read of data pad byte\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
				}
			}

831
			got_data_chunk = true;
832
833
		}
		else {
834
			if(xx == 0x20746d66 && got_fmt_chunk) { /* "fmt " */
835
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'fmt ' sub-chunk\n", encoder_session.inbasefilename);
836
837
838
			}
			else if(xx == 0x61746164) { /* "data" */
				if(got_data_chunk) {
839
					flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'data' sub-chunk\n", encoder_session.inbasefilename);
840
841
				}
				else if(!got_fmt_chunk) {
842
					flac__utils_printf(stderr, 1, "%s: ERROR: got 'data' sub-chunk before 'fmt' sub-chunk\n", encoder_session.inbasefilename);
843
					return EncoderSession_finish_error(&encoder_session);
844
845
846
847
848
849
				}
				else {
					FLAC__ASSERT(0);
				}
			}
			else {
850
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping unknown sub-chunk '%c%c%c%c'\n", encoder_session.inbasefilename, (char)(xx&255), (char)((xx>>8)&255), (char)((xx>>16)&255), (char)(xx>>24));
851
			}
852
			/* sub-chunk size */
853
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
854
				return EncoderSession_finish_error(&encoder_session);
855
856
857
858
			else {
				unsigned long skip = xx+(xx & 1U);

				FLAC__ASSERT(skip<=LONG_MAX);
859
860
861
				if(!fskip_ahead(infile, skip)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping unsupported sub-chunk\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
862
				}
863
			}
Josh Coalson's avatar
Josh Coalson committed
864
865
866
		}
	}