vorbis_comment 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/extconf.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+ find_library('ogg', 'ogg_sync_init')
3
+ find_library('vorbis', 'vorbis_comment_init')
4
+ create_makefile("vorbis_comment_ext")
data/test/blank.ogg ADDED
Binary file
data/test/corrupt.ogg ADDED
Binary file
data/test/lt8k.ogg ADDED
Binary file
Binary file
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'vorbis_comment'
4
+ require 'fileutils'
5
+ require 'test/unit'
6
+
7
+ begin
8
+ VorbisComment.new(File.join(File.dirname(__FILE__), 'lt8k.ogg')).fields
9
+ rescue VorbisComment::InvalidDataError
10
+ puts('The linked version of libvorbis has problems with files < 8K in size')
11
+ end
12
+
13
+ class VorbisCommentTest < Test::Unit::TestCase
14
+ def vc(filename)
15
+ VorbisComment.new(File.join(File.dirname(__FILE__), filename))
16
+ end
17
+
18
+ def work_with_copy(filename, &block)
19
+ new_name = File.join(File.dirname(__FILE__), "copy-#{filename}")
20
+ FileUtils.cp(File.join(File.dirname(__FILE__), filename), new_name)
21
+ begin
22
+ result = yield new_name
23
+ ensure
24
+ File.delete(new_name)
25
+ end
26
+ result
27
+ end
28
+
29
+ def test_initialize
30
+ assert_equal File.join(File.dirname(__FILE__), 'title.ogg'), vc('title.ogg').filename
31
+ # Nonexistent files don't raise an error until they are accessed for the fields
32
+ assert_equal 'nonexistant.ogg', VorbisComment.new('nonexistant.ogg').filename
33
+ # Corrupt files don't raise an error until they are parsed for the fields
34
+ assert_equal File.join(File.dirname(__FILE__), 'corrupt.ogg'), vc('corrupt.ogg').filename
35
+ end
36
+
37
+ def test_exists?
38
+ assert_equal true, vc('title.ogg').exists?
39
+ assert_equal true, vc('manyfields.ogg').exists?
40
+ # Blank tags still exist
41
+ assert_equal true, vc('blank.ogg').exists?
42
+ # Corrupt tags are considered not to exist
43
+ assert_equal false, vc('corrupt.ogg').exists?
44
+ # Files that aren't ogg files will be treated similarly
45
+ assert_equal false, vc('test_vorbis_comment.rb').exists?
46
+ # But nonexistant files raise an OpenError
47
+ assert_raises(VorbisComment::OpenError){vc('nonexistant.ogg').exists?}
48
+ end
49
+
50
+ def test_fields
51
+ assert_equal Hash[], vc('blank.ogg').fields
52
+ assert_equal Hash['title'=>['Silence']], vc('title.ogg').fields
53
+ assert_equal Hash[{"ARTIST"=>["Test Artist 1", "Test Artist 2"], "TrackNumber"=>["1"], "Album"=>["Test Album"], "Title"=>["Test Title"], "Date"=>["1999-12-31"], "comment"=>[""]}], vc('manyfields.ogg').fields
54
+ assert_raises(VorbisComment::InvalidDataError){vc('corrupt.ogg').fields}
55
+ assert_raises(VorbisComment::OpenError){vc('nonexistant.ogg').fields}
56
+ end
57
+
58
+ def test_remove!
59
+ assert_equal Hash[], work_with_copy('blank.ogg'){|filename| VorbisComment.new(filename).remove!}
60
+ assert_equal Hash[], work_with_copy('title.ogg'){|filename| VorbisComment.new(filename).remove!}
61
+ assert_equal Hash[], work_with_copy('manyfields.ogg'){|filename| VorbisComment.new(filename).remove!}
62
+ assert_raises(VorbisComment::InvalidDataError){work_with_copy('corrupt.ogg'){|filename| VorbisComment.new(filename).remove!}}
63
+ assert_raises(VorbisComment::OpenError){VorbisComment.new('nonexistant.ogg').remove!}
64
+ end
65
+
66
+ def test_pretty_print
67
+ assert_equal "", vc('blank.ogg').pretty_print
68
+ assert_equal "title: Silence", vc('title.ogg').pretty_print
69
+ assert_equal "ARTIST: Test Artist 1, Test Artist 2\nAlbum: Test Album\nDate: 1999-12-31\nTitle: Test Title\nTrackNumber: 1\ncomment: ", vc('manyfields.ogg').pretty_print
70
+ assert_equal "CORRUPT TAG!", vc('corrupt.ogg').pretty_print
71
+ assert_equal "CORRUPT TAG!", vc('test_vorbis_comment.rb').pretty_print
72
+ end
73
+
74
+ def test_update
75
+ f = {'Blah'=>['Blah']}
76
+ assert_raises(VorbisComment::InvalidDataError){work_with_copy('corrupt.ogg'){|filename| VorbisComment.new(filename).update{|fields| fields.merge!(f)}}}
77
+ assert_raises(VorbisComment::OpenError){VorbisComment.new('nonexistant.ogg').update{|fields| fields.merge!(f)}}
78
+
79
+ g = {'x'=>'y', 'y'=>:z, :z=>[:A], :zz=>[['A'], [[[:b]], 'c']]}
80
+ h = {'x'=>['y'], 'y'=>['z'], 'z'=>['A'], 'zz'=>['A', 'bc']}
81
+
82
+ %w'blank.ogg title.ogg manyfields.ogg'.each do |fname|
83
+ work_with_copy(fname) do |filename|
84
+ v = VorbisComment.new(filename)
85
+ fx = v.fields.merge(f)
86
+ assert_equal fx, v.update{|fields| fields.merge!(f)}
87
+ assert_equal fx, v.fields
88
+ v = VorbisComment.new(filename)
89
+ assert_equal fx, v.fields
90
+ gx = v.fields.merge(g)
91
+ assert_equal gx, v.update{|fields| fields.merge!(g)}
92
+ v = VorbisComment.new(filename)
93
+ hx = v.fields.merge(h)
94
+ assert_equal hx, VorbisComment.new(filename).fields
95
+ end
96
+ end
97
+ end
98
+
99
+ def test_bad_keys_and_values
100
+ work_with_copy('blank.ogg') do |filename|
101
+ v = VorbisComment.new(filename)
102
+ # Test for bad keys
103
+ (("\x00".."\x1f").to_a + ['='] + ("\x7e".."\xff").to_a).each do |c|
104
+ assert_raises(VorbisComment::InvalidCommentError){v.update{|fields| fields[c] = 'Blah'}}
105
+ end
106
+ # Test for bad vales (Invalid UTF8)
107
+ assert_raises(VorbisComment::InvalidCommentError){v.update{|fields| fields['Blah'] = "\x81\x81"}}
108
+ end
109
+ end
110
+
111
+ def test_add_to_fields_and_normalize_fields
112
+ work_with_copy('blank.ogg') do |filename|
113
+ v = VorbisComment.new(filename)
114
+ assert_equal Hash[], v.fields
115
+ assert_equal [], v.send(:normalize_fields)
116
+ v.send(:add_to_fields, 'A', 'b')
117
+ assert_equal CICPHash['A'=>['b']], v.fields
118
+ assert_equal [['A', 'b']], v.send(:normalize_fields)
119
+ v.send(:add_to_fields, :a, ['C'])
120
+ assert_equal CICPHash['A'=>['b',['C']]], v.fields
121
+ assert_equal [['A', 'C'], ['A', 'b']], v.send(:normalize_fields)
122
+ v.send(:add_to_fields, 12, 42)
123
+ assert_equal CICPHash['A'=>['b',['C']], 12=>[42]], v.fields
124
+ assert_equal [['12', '42'], ['A', 'C'], ['A', 'b']], v.send(:normalize_fields)
125
+ v.update{}
126
+ assert_equal CICPHash['A'=>['C','b'], '12'=>['42']], VorbisComment.new(filename).fields
127
+ assert_equal [['12', '42'], ['A', 'C'], ['A', 'b']], v.send(:normalize_fields)
128
+ end
129
+ end
130
+ end
data/test/title.ogg ADDED
Binary file
data/vcedit.c ADDED
@@ -0,0 +1,583 @@
1
+ /*
2
+ * Copyright (C) 2000-2001 Michael Smith (msmith at xiph org)
3
+ *
4
+ * This library is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU Lesser General Public
6
+ * License as published by the Free Software Foundation, version 2.
7
+ *
8
+ * This library is distributed in the hope that it will be useful,
9
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ * Lesser General Public License for more details.
12
+ *
13
+ * You should have received a copy of the GNU Lesser General Public
14
+ * License along with this library; if not, write to the Free Software
15
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16
+ * MA 02110-1301 USA
17
+ */
18
+
19
+ #include <stdio.h>
20
+ #include <stdbool.h>
21
+ #include <stdlib.h>
22
+ #include <string.h>
23
+ #include <errno.h>
24
+ #include <limits.h>
25
+ #include <unistd.h>
26
+ #include <sys/types.h>
27
+ #include <sys/stat.h>
28
+ #include <ogg/ogg.h>
29
+ #include <vorbis/codec.h>
30
+ #include <assert.h>
31
+
32
+ #include "vcedit.h"
33
+
34
+ #define CHUNKSIZE 4096
35
+
36
+ struct vcedit_state_St {
37
+ int refcount;
38
+
39
+ ogg_sync_state oy;
40
+ ogg_stream_state os;
41
+
42
+ vorbis_comment vc;
43
+ vorbis_info vi;
44
+
45
+ FILE *in;
46
+ mode_t file_mode;
47
+
48
+ bool opened;
49
+ long serial;
50
+
51
+ ogg_packet packet_main;
52
+ ogg_packet packet_code_books;
53
+
54
+ char *vendor;
55
+ int prevW;
56
+
57
+ bool extra_page;
58
+ bool eos;
59
+
60
+ char filename[0];
61
+ };
62
+
63
+ static void
64
+ ogg_packet_init (ogg_packet *p, unsigned char *buf, long len)
65
+ {
66
+ p->packet = buf;
67
+ p->bytes = len;
68
+
69
+ p->b_o_s = p->e_o_s = 0;
70
+ p->granulepos = p->packetno = 0;
71
+ }
72
+
73
+ static bool
74
+ ogg_packet_dup_data (ogg_packet *p)
75
+ {
76
+ unsigned char *tmp;
77
+
78
+ tmp = malloc (p->bytes);
79
+ if (!tmp)
80
+ return false;
81
+
82
+ memcpy (tmp, p->packet, p->bytes);
83
+ p->packet = tmp;
84
+
85
+ return true;
86
+ }
87
+
88
+ static void
89
+ vcedit_state_free (vcedit_state *s)
90
+ {
91
+ free (s->vendor);
92
+
93
+ if (s->in) {
94
+ fclose (s->in);
95
+ s->in = NULL;
96
+ }
97
+
98
+ free (s);
99
+ }
100
+
101
+ static bool
102
+ vcedit_state_init (vcedit_state *s, const char *filename)
103
+ {
104
+ s->refcount = 1;
105
+
106
+ ogg_packet_init (&s->packet_main, NULL, 0);
107
+ ogg_packet_init (&s->packet_code_books, NULL, 0);
108
+
109
+ strcpy (s->filename, filename);
110
+
111
+ return true;
112
+ }
113
+
114
+ vcedit_state *
115
+ vcedit_state_new (const char *filename)
116
+ {
117
+ vcedit_state *s;
118
+ size_t len;
119
+
120
+ len = strlen (filename);
121
+ if (len > PATH_MAX)
122
+ return NULL;
123
+
124
+ s = malloc (sizeof (vcedit_state) + len + 1);
125
+ if (!s)
126
+ return NULL;
127
+
128
+ memset (s, 0, sizeof (vcedit_state));
129
+
130
+ if (!vcedit_state_init (s, filename)) {
131
+ vcedit_state_free (s);
132
+ return NULL;
133
+ }
134
+
135
+ return s;
136
+ }
137
+
138
+ vorbis_comment *
139
+ vcedit_comments (vcedit_state *s)
140
+ {
141
+ return s->opened ? &s->vc : NULL;
142
+ }
143
+
144
+ static void
145
+ vcedit_clear_internals (vcedit_state *s)
146
+ {
147
+ ogg_stream_clear (&s->os);
148
+ ogg_sync_clear (&s->oy);
149
+
150
+ vorbis_info_clear (&s->vi);
151
+ vorbis_comment_clear (&s->vc);
152
+
153
+ free (s->vendor);
154
+ s->vendor = NULL;
155
+
156
+ ogg_packet_clear (&s->packet_main);
157
+ ogg_packet_clear (&s->packet_code_books);
158
+
159
+ s->serial = 0;
160
+ s->opened = false;
161
+ }
162
+
163
+ void
164
+ vcedit_state_ref (vcedit_state *s)
165
+ {
166
+ s->refcount++;
167
+ }
168
+
169
+ void
170
+ vcedit_state_unref (vcedit_state *s)
171
+ {
172
+ if (--s->refcount)
173
+ return;
174
+
175
+ if (s->opened)
176
+ vcedit_clear_internals (s);
177
+
178
+ vcedit_state_free (s);
179
+ }
180
+
181
+ /* Next two functions pulled straight from libvorbis, apart from one change
182
+ * - we don't want to overwrite the vendor string.
183
+ */
184
+ static void
185
+ _v_writestring (oggpack_buffer *o, const char *s, int len)
186
+ {
187
+ while (len--)
188
+ oggpack_write (o, *s++, 8);
189
+ }
190
+
191
+ static bool
192
+ write_comments (vcedit_state *s, ogg_packet *packet)
193
+ {
194
+ oggpack_buffer opb;
195
+ size_t len;
196
+ int i;
197
+
198
+ ogg_packet_init (packet, NULL, 0);
199
+
200
+ oggpack_writeinit (&opb);
201
+
202
+ /* preamble */
203
+ oggpack_write (&opb, 0x03, 8);
204
+ _v_writestring (&opb, "vorbis", 6);
205
+
206
+ /* vendor */
207
+ len = strlen (s->vendor);
208
+ oggpack_write (&opb, len, 32);
209
+ _v_writestring (&opb, s->vendor, len);
210
+
211
+ /* comments */
212
+ oggpack_write (&opb, s->vc.comments, 32);
213
+
214
+ for (i = 0; i < s->vc.comments; i++)
215
+ if (!s->vc.user_comments[i])
216
+ oggpack_write (&opb, 0, 32);
217
+ else {
218
+ oggpack_write (&opb, s->vc.comment_lengths[i], 32);
219
+ _v_writestring (&opb, s->vc.user_comments[i],
220
+ s->vc.comment_lengths[i]);
221
+ }
222
+
223
+ oggpack_write (&opb, 1, 1);
224
+
225
+ packet->bytes = oggpack_bytes (&opb);
226
+
227
+ packet->packet = _ogg_malloc (packet->bytes);
228
+ if (!packet->packet)
229
+ return false;
230
+
231
+ memcpy (packet->packet, opb.buffer, packet->bytes);
232
+
233
+ oggpack_writeclear (&opb);
234
+
235
+ return true;
236
+ }
237
+
238
+ static int
239
+ _blocksize (vcedit_state *s, ogg_packet *p)
240
+ {
241
+ int this, ret = 0;
242
+
243
+ this = vorbis_packet_blocksize (&s->vi, p);
244
+
245
+ if (s->prevW)
246
+ ret = (this + s->prevW) / 4;
247
+
248
+ s->prevW = this;
249
+
250
+ return ret;
251
+ }
252
+
253
+ static int
254
+ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page)
255
+ {
256
+ char *buffer;
257
+ int result, bytes;
258
+
259
+ result = ogg_stream_packetout (&s->os, p);
260
+ if (result == 1)
261
+ return 1;
262
+
263
+ if (s->eos)
264
+ return 0;
265
+
266
+ while (ogg_sync_pageout (&s->oy, page) != 1) {
267
+ buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
268
+ bytes = fread (buffer, 1, CHUNKSIZE, s->in);
269
+ ogg_sync_wrote (&s->oy, bytes);
270
+
271
+ if (!bytes && (feof (s->in) || ferror (s->in)))
272
+ return 0;
273
+ }
274
+
275
+ if (ogg_page_eos (page))
276
+ s->eos = true;
277
+ else if (ogg_page_serialno (page) != s->serial) {
278
+ s->eos = true;
279
+ s->extra_page = true;
280
+
281
+ return 0;
282
+ }
283
+
284
+ ogg_stream_pagein (&s->os, page);
285
+
286
+ return _fetch_next_packet (s, p, page);
287
+ }
288
+
289
+ vcedit_error
290
+ vcedit_open (vcedit_state *s)
291
+ {
292
+ vcedit_error ret;
293
+ ogg_packet packet_comments, *header = &packet_comments;
294
+ ogg_page page;
295
+ struct stat st;
296
+ char *buffer;
297
+ size_t bytes, total = 0;
298
+ int i = 0;
299
+
300
+ s->in = fopen (s->filename, "rb");
301
+ if (!s->in)
302
+ return VCEDIT_ERR_OPEN;
303
+
304
+ s->file_mode = stat (s->filename, &st) ? 0664 : st.st_mode;
305
+
306
+ ogg_sync_init (&s->oy);
307
+
308
+ do {
309
+ /* Bail if we don't find data in the first 40 kB */
310
+ if (feof (s->in) || ferror (s->in) || total >= (CHUNKSIZE * 10)) {
311
+ ogg_sync_clear (&s->oy);
312
+
313
+ return VCEDIT_ERR_INVAL;
314
+ }
315
+
316
+ buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
317
+
318
+ bytes = fread (buffer, 1, CHUNKSIZE, s->in);
319
+ total += bytes;
320
+
321
+ ogg_sync_wrote (&s->oy, bytes);
322
+ } while (ogg_sync_pageout (&s->oy, &page) != 1);
323
+
324
+ s->serial = ogg_page_serialno (&page);
325
+
326
+ ogg_stream_init (&s->os, s->serial);
327
+ vorbis_info_init (&s->vi);
328
+ vorbis_comment_init (&s->vc);
329
+
330
+ if (ogg_stream_pagein (&s->os, &page) < 0) {
331
+ ret = VCEDIT_ERR_INVAL;
332
+ goto err;
333
+ }
334
+
335
+ if (ogg_stream_packetout (&s->os, &s->packet_main) != 1) {
336
+ ret = VCEDIT_ERR_INVAL;
337
+ goto err;
338
+ }
339
+
340
+ if (!ogg_packet_dup_data (&s->packet_main)) {
341
+ s->packet_main.packet = NULL;
342
+ ret = VCEDIT_ERR_INVAL;
343
+ goto err;
344
+ }
345
+
346
+ if (vorbis_synthesis_headerin (&s->vi, &s->vc, &s->packet_main) < 0) {
347
+ ret = VCEDIT_ERR_INVAL;
348
+ goto err;
349
+ }
350
+
351
+ ogg_packet_init (&packet_comments, NULL, 0);
352
+
353
+ while (i < 2) {
354
+ if (feof (s->in) || ferror (s->in)) {
355
+ ret = VCEDIT_ERR_INVAL;
356
+ goto err;
357
+ }
358
+
359
+ while (i < 2) {
360
+ int result;
361
+
362
+ result = ogg_sync_pageout (&s->oy, &page);
363
+ if (!result)
364
+ break; /* Too little data so far */
365
+
366
+ if (result != 1)
367
+ continue;
368
+
369
+ ogg_stream_pagein (&s->os, &page);
370
+
371
+ while (i < 2) {
372
+ result = ogg_stream_packetout (&s->os, header);
373
+ if (!result)
374
+ break;
375
+
376
+ if (result != 1) {
377
+ ret = VCEDIT_ERR_INVAL;
378
+ goto err;
379
+ }
380
+
381
+ if (i++ == 1 && !ogg_packet_dup_data (header)) {
382
+ header->packet = NULL;
383
+ ret = VCEDIT_ERR_INVAL;
384
+ goto err;
385
+ }
386
+
387
+ vorbis_synthesis_headerin (&s->vi, &s->vc, header);
388
+
389
+ header = &s->packet_code_books;
390
+ }
391
+ }
392
+
393
+ buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
394
+
395
+ bytes = fread (buffer, 1, CHUNKSIZE, s->in);
396
+ ogg_sync_wrote (&s->oy, bytes);
397
+ }
398
+
399
+ /* Copy the vendor tag */
400
+ s->vendor = strdup (s->vc.vendor);
401
+
402
+ /* Headers are done! */
403
+ s->opened = true;
404
+
405
+ return VCEDIT_ERR_SUCCESS;
406
+
407
+ err:
408
+ vcedit_clear_internals (s);
409
+
410
+ return ret;
411
+ }
412
+
413
+ static bool
414
+ write_data (const void *buf, size_t size, size_t nmemb, FILE *stream)
415
+ {
416
+ while (nmemb > 0) {
417
+ size_t w;
418
+
419
+ w = fwrite (buf, size, nmemb, stream);
420
+ if (!w && ferror (stream))
421
+ return false;
422
+
423
+ nmemb -= w;
424
+ buf += size * w;
425
+ }
426
+
427
+ return true;
428
+ }
429
+
430
+ static bool
431
+ write_page (FILE *f, ogg_page *p)
432
+ {
433
+ return write_data (p->header, 1, p->header_len, f) &&
434
+ write_data (p->body, 1, p->body_len, f);
435
+ }
436
+
437
+ vcedit_error
438
+ vcedit_write (vcedit_state *s)
439
+ {
440
+ ogg_stream_state stream;
441
+ ogg_packet packet;
442
+ ogg_page page_out, page_in;
443
+ ogg_int64_t granpos = 0;
444
+ FILE *out;
445
+ char *buffer, tmpfile[PATH_MAX];
446
+ bool success = false, need_flush = false, need_out = false;
447
+ int fd, result, bytes;
448
+
449
+ if (!s->opened)
450
+ return VCEDIT_ERR_INVAL;
451
+
452
+ strcpy (tmpfile, s->filename);
453
+ strcat (tmpfile, ".XXXXXX");
454
+
455
+ fd = mkstemp (tmpfile);
456
+ if (fd == -1)
457
+ return VCEDIT_ERR_TMPFILE;
458
+
459
+ out = fdopen (fd, "wb");
460
+ if (!out) {
461
+ unlink (tmpfile);
462
+ close (fd);
463
+
464
+ return VCEDIT_ERR_TMPFILE;
465
+ }
466
+
467
+ s->prevW = 0;
468
+ s->extra_page = s->eos = false;
469
+
470
+ ogg_stream_init (&stream, s->serial);
471
+
472
+ /* write "main" packet */
473
+ s->packet_main.b_o_s = 1;
474
+ ogg_stream_packetin (&stream, &s->packet_main);
475
+ s->packet_main.b_o_s = 0;
476
+
477
+ /* prepare and write comments */
478
+ if (!write_comments (s, &packet)) {
479
+ ogg_stream_clear (&stream);
480
+ unlink (tmpfile);
481
+ fclose (out);
482
+
483
+ return VCEDIT_ERR_INVAL;
484
+ }
485
+
486
+ ogg_stream_packetin (&stream, &packet);
487
+ ogg_packet_clear (&packet);
488
+
489
+ /* write codebooks */
490
+ ogg_stream_packetin (&stream, &s->packet_code_books);
491
+
492
+ while (ogg_stream_flush (&stream, &page_out))
493
+ if (!write_page (out, &page_out))
494
+ goto cleanup;
495
+
496
+ while (_fetch_next_packet (s, &packet, &page_in)) {
497
+ bool write = false;
498
+ int size;
499
+
500
+ size = _blocksize (s, &packet);
501
+ granpos += size;
502
+
503
+ if (need_flush)
504
+ write = ogg_stream_flush (&stream, &page_out);
505
+ else if (need_out)
506
+ write = ogg_stream_pageout (&stream, &page_out);
507
+
508
+ if (write && !write_page (out, &page_out))
509
+ goto cleanup;
510
+
511
+ need_flush = need_out = false;
512
+
513
+ if (packet.granulepos == -1) {
514
+ packet.granulepos = granpos;
515
+ ogg_stream_packetin (&stream, &packet);
516
+ } else {
517
+ /* granulepos is set, validly. Use it, and force a flush to
518
+ * account for shortened blocks (vcut) when appropriate
519
+ */
520
+ if (granpos > packet.granulepos) {
521
+ granpos = packet.granulepos;
522
+ ogg_stream_packetin (&stream, &packet);
523
+ need_flush = true;
524
+ } else {
525
+ ogg_stream_packetin (&stream, &packet);
526
+ need_out = true;
527
+ }
528
+ }
529
+ }
530
+
531
+ stream.e_o_s = 1;
532
+
533
+ while (ogg_stream_flush (&stream, &page_out))
534
+ if (!write_page (out, &page_out))
535
+ goto cleanup;
536
+
537
+ if (s->extra_page && !write_page (out, &page_in))
538
+ goto cleanup;
539
+
540
+ /* clear it, because not all paths to here do */
541
+ s->eos = false;
542
+
543
+ do {
544
+ /* We copy the rest of the stream (other logical streams)
545
+ * through, a page at a time.
546
+ */
547
+ while ((result = ogg_sync_pageout (&s->oy, &page_out)))
548
+ if (result == 1 && !write_page (out, &page_out))
549
+ goto cleanup;
550
+
551
+ buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
552
+ bytes = fread (buffer, 1, CHUNKSIZE, s->in);
553
+ ogg_sync_wrote (&s->oy, bytes);
554
+
555
+ if (ferror (s->in))
556
+ goto cleanup;
557
+ } while (bytes || !feof (s->in));
558
+
559
+ s->eos = success = true;
560
+
561
+ cleanup:
562
+ fclose (s->in);
563
+
564
+ if (!success) {
565
+ unlink (tmpfile);
566
+ fclose (out);
567
+ } else {
568
+ fclose (out);
569
+ unlink (s->filename);
570
+ rename (tmpfile, s->filename);
571
+ chmod (s->filename, s->file_mode);
572
+ }
573
+
574
+ ogg_stream_clear (&stream);
575
+
576
+ if (!s->eos)
577
+ return VCEDIT_ERR_INVAL;
578
+
579
+ vcedit_clear_internals (s);
580
+
581
+ return (vcedit_open (s) == VCEDIT_ERR_SUCCESS) ?
582
+ VCEDIT_ERR_SUCCESS : VCEDIT_ERR_REOPEN;
583
+ }