1 /* EasyTAG - tag editor for audio files
2 * Copyright (C) 2014 David King <amigadave@amigadave.com>
3 * Copyright (C) 2000-2001 Michael Smith <msmith@xiph.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h" /* For definition of ENABLE_OGG. */
21
22 #ifdef ENABLE_OGG
23 #include <gio/gio.h>
24 #include <glib/gi18n.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ogg/ogg.h>
28 #include <vorbis/codec.h>
29
30 #include "vcedit.h"
31 #include "ogg_header.h"
32
33 #define CHUNKSIZE 4096
34
35 struct _EtOggState
36 {
37 /*< private >*/
38 GFileInputStream *in;
39 #ifdef ENABLE_SPEEX
40 SpeexHeader *si;
41 #endif
42 #ifdef ENABLE_OPUS
43 OpusHead *oi;
44 #endif
45 vorbis_info *vi;
46 ogg_stream_state *os;
47 ogg_sync_state *oy;
48 vorbis_comment *vc;
49 EtOggKind oggtype;
50 glong serial;
51 guchar *mainbuf;
52 guchar *bookbuf;
53 gchar *vendor;
54 gint mainlen;
55 gint booklen;
56 gint prevW;
57 gint extrapage;
58 gint eosin;
59 };
60
61 EtOggState *
vcedit_new_state(void)62 vcedit_new_state (void)
63 {
64 EtOggState *state = g_slice_new0 (EtOggState);
65 state->oggtype = ET_OGG_KIND_UNKNOWN;
66
67 return state;
68 }
69
70 vorbis_comment *
vcedit_comments(EtOggState * state)71 vcedit_comments (EtOggState *state)
72 {
73 return state->vc;
74 }
75
76 #ifdef ENABLE_SPEEX
77 const SpeexHeader *
vcedit_speex_header(EtOggState * state)78 vcedit_speex_header (EtOggState *state)
79 {
80 return state->si;
81 }
82 #endif /* ENABLE_SPEEX */
83
84 static void
vcedit_clear_internals(EtOggState * state)85 vcedit_clear_internals (EtOggState *state)
86 {
87 if (state->vc)
88 {
89 vorbis_comment_clear (state->vc);
90 g_slice_free (vorbis_comment, state->vc);
91 }
92
93 if (state->os)
94 {
95 ogg_stream_clear (state->os);
96 g_slice_free (ogg_stream_state, state->os);
97 }
98
99 if (state->oy)
100 {
101 ogg_sync_clear (state->oy);
102 g_slice_free (ogg_sync_state, state->oy);
103 }
104
105 g_free (state->vendor);
106 g_free (state->mainbuf);
107 g_free (state->bookbuf);
108
109 if (state->vi)
110 {
111 vorbis_info_clear (state->vi);
112 g_slice_free (vorbis_info, state->vi);
113 }
114
115 #ifdef ENABLE_SPEEX
116 if (state->si)
117 {
118 speex_header_free (state->si);
119 }
120 #endif
121
122 #ifdef ENABLE_OPUS
123 if (state->oi)
124 {
125 g_slice_free (OpusHead, state->oi);
126 }
127 #endif /* ENABLE_OPUS */
128
129 if (state->in)
130 {
131 g_object_unref (state->in);
132 }
133
134 memset (state, 0, sizeof (*state));
135 }
136
137 void
vcedit_clear(EtOggState * state)138 vcedit_clear (EtOggState *state)
139 {
140 if (state)
141 {
142 vcedit_clear_internals (state);
143 g_slice_free (EtOggState, state);
144 }
145 }
146
147 /* Next two functions pulled straight from libvorbis, apart from one change
148 * - we don't want to overwrite the vendor string.
149 */
150 static void
_v_writestring(oggpack_buffer * o,const char * s,int len)151 _v_writestring (oggpack_buffer *o,
152 const char *s,
153 int len)
154 {
155 while (len--)
156 {
157 oggpack_write (o, *s++, 8);
158 }
159 }
160
161 static int
_commentheader_out(EtOggState * state,ogg_packet * op)162 _commentheader_out (EtOggState *state,
163 ogg_packet *op)
164 {
165 vorbis_comment *vc = state->vc;
166 const gchar *vendor = state->vendor;
167 oggpack_buffer opb;
168
169 oggpack_writeinit (&opb);
170
171 if (state->oggtype == ET_OGG_KIND_VORBIS)
172 {
173 /* preamble */
174 oggpack_write (&opb, 0x03, 8);
175 _v_writestring (&opb, "vorbis", 6);
176 }
177 #ifdef ENABLE_OPUS
178 else if (state->oggtype == ET_OGG_KIND_OPUS)
179 {
180 _v_writestring (&opb, "OpusTags", 8);
181 }
182 #endif
183
184 /* vendor */
185 oggpack_write (&opb, strlen (vendor), 32);
186 _v_writestring (&opb,vendor, strlen (vendor));
187
188 /* comments */
189 oggpack_write (&opb, vc->comments, 32);
190
191 if (vc->comments)
192 {
193 int i;
194
195 for (i=0; i < vc->comments; i++)
196 {
197 if (vc->user_comments[i])
198 {
199 oggpack_write (&opb, vc->comment_lengths[i], 32);
200 _v_writestring (&opb, vc->user_comments[i],
201 vc->comment_lengths[i]);
202 }
203 else
204 {
205 oggpack_write (&opb, 0, 32);
206 }
207 }
208 }
209
210 oggpack_write (&opb, 1, 1);
211
212 op->packet = _ogg_malloc (oggpack_bytes (&opb));
213 memcpy (op->packet, opb.buffer, oggpack_bytes (&opb));
214
215 op->bytes = oggpack_bytes (&opb);
216 op->b_o_s = 0;
217 op->e_o_s = 0;
218 op->granulepos = 0;
219
220 if (state->oggtype == ET_OGG_KIND_VORBIS)
221 {
222 op->packetno = 1;
223 }
224
225 oggpack_writeclear (&opb);
226 return 0;
227 }
228
229 static int
_blocksize(EtOggState * s,ogg_packet * p)230 _blocksize (EtOggState *s,
231 ogg_packet *p)
232 {
233 int this = vorbis_packet_blocksize (s->vi, p);
234 int ret = (this + s->prevW) / 4;
235
236 if(!s->prevW)
237 {
238 s->prevW = this;
239 return 0;
240 }
241
242 s->prevW = this;
243 return ret;
244 }
245
246 static gboolean
_fetch_next_packet(EtOggState * s,ogg_packet * p,ogg_page * page,GError ** error)247 _fetch_next_packet (EtOggState *s,
248 ogg_packet *p,
249 ogg_page *page,
250 GError **error)
251 {
252 int result;
253 char *buffer;
254 gssize bytes;
255
256 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
257
258 result = ogg_stream_packetout (s->os, p);
259
260 if (result > 0)
261 {
262 return TRUE;
263 }
264 else
265 {
266 if (s->eosin)
267 {
268 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_EOS,
269 "Page reached end of logical bitstream");
270 g_assert (error == NULL || *error != NULL);
271 return FALSE;
272 }
273
274 while (ogg_sync_pageout (s->oy, page) <= 0)
275 {
276 buffer = ogg_sync_buffer (s->oy, CHUNKSIZE);
277 bytes = g_input_stream_read (G_INPUT_STREAM (s->in), buffer,
278 CHUNKSIZE, NULL, error);
279 ogg_sync_wrote (s->oy, bytes);
280
281 if(bytes == 0)
282 {
283 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_EOF,
284 "Reached end of file");
285 g_assert (error == NULL || *error != NULL);
286 return FALSE;
287 }
288 else if (bytes == -1)
289 {
290 g_assert (error == NULL || *error != NULL);
291 return FALSE;
292 }
293 }
294
295 if (ogg_page_eos (page))
296 {
297 s->eosin = 1;
298 }
299 else if (ogg_page_serialno (page) != s->serial)
300 {
301 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_SN,
302 "Page serial number and state serial number doesn't match");
303 s->eosin = 1;
304 s->extrapage = 1;
305 g_assert (error == NULL || *error != NULL);
306 return FALSE;
307 }
308
309 g_assert (error == NULL || *error == NULL);
310 ogg_stream_pagein (s->os, page);
311 return _fetch_next_packet (s, p, page, error);
312 }
313 }
314
315 /*
316 * Next functions pulled straight from libvorbis,
317 */
318 static void
_v_readstring(oggpack_buffer * o,char * buf,int bytes)319 _v_readstring (oggpack_buffer *o,
320 char *buf,
321 int bytes)
322 {
323 while (bytes--)
324 {
325 *buf++ = oggpack_read (o, 8);
326 }
327 }
328
329 /*
330 * Next functions pulled straight from libvorbis, apart from one change
331 * - disabled the EOP check
332 */
333 static int
_speex_unpack_comment(vorbis_comment * vc,oggpack_buffer * opb)334 _speex_unpack_comment (vorbis_comment *vc,
335 oggpack_buffer *opb)
336 {
337 int i;
338 int vendorlen = oggpack_read (opb, 32);
339
340 if (vendorlen < 0)
341 {
342 goto err_out;
343 }
344
345 vc->vendor = _ogg_calloc (vendorlen + 1, 1);
346 _v_readstring (opb, vc->vendor, vendorlen);
347
348 vc->comments = oggpack_read (opb, 32);
349
350 if (vc->comments < 0)
351 {
352 goto err_out;
353 }
354
355 vc->user_comments = _ogg_calloc (vc->comments + 1,
356 sizeof (*vc->user_comments));
357 vc->comment_lengths = _ogg_calloc (vc->comments + 1,
358 sizeof (*vc->comment_lengths));
359
360 for (i = 0; i < vc->comments; i++)
361 {
362 int len = oggpack_read (opb, 32);
363
364 if (len < 0)
365 {
366 goto err_out;
367 }
368
369 vc->comment_lengths[i] = len;
370 vc->user_comments[i] = _ogg_calloc (len + 1, 1);
371 _v_readstring (opb, vc->user_comments[i], len);
372 }
373
374 /*if(oggpack_read(opb,1)!=1)goto err_out; EOP check */
375 return(0);
376
377 err_out:
378 vorbis_comment_clear(vc);
379 return(1);
380 }
381
382 gboolean
vcedit_open(EtOggState * state,GFile * file,GError ** error)383 vcedit_open (EtOggState *state,
384 GFile *file,
385 GError **error)
386 {
387 char *buffer;
388 gssize bytes;
389 int i;
390 int chunks = 0;
391 int headerpackets = 0;
392 oggpack_buffer opb;
393 ogg_packet *header;
394 ogg_packet header_main;
395 ogg_packet header_comments;
396 ogg_packet header_codebooks;
397 ogg_page og;
398 GFileInputStream *istream;
399
400 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
401
402 istream = g_file_read (file, NULL, error);
403
404 if (!istream)
405 {
406 g_assert (error == NULL || *error != NULL);
407 return FALSE;
408 }
409
410 state->in = istream;
411 state->oy = g_slice_new (ogg_sync_state);
412 ogg_sync_init (state->oy);
413
414 while(1)
415 {
416 buffer = ogg_sync_buffer (state->oy, CHUNKSIZE);
417 bytes = g_input_stream_read (G_INPUT_STREAM (state->in), buffer,
418 CHUNKSIZE, NULL, error);
419 if (bytes == -1)
420 {
421 goto err;
422 }
423
424 ogg_sync_wrote(state->oy, bytes);
425
426 if(ogg_sync_pageout(state->oy, &og) == 1)
427 break;
428
429 if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */
430 {
431 if(bytes<CHUNKSIZE)
432 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_TRUNC,
433 "Input truncated or empty");
434 else
435 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_NOTOGG,
436 "Input is not an Ogg bitstream");
437 goto err;
438 }
439 }
440
441 state->serial = ogg_page_serialno(&og);
442
443 state->os = g_slice_new (ogg_stream_state);
444 ogg_stream_init (state->os, state->serial);
445
446 state->vi = g_slice_new (vorbis_info);
447 vorbis_info_init (state->vi);
448
449 state->vc = g_slice_new (vorbis_comment);
450 vorbis_comment_init (state->vc);
451
452 if (ogg_stream_pagein (state->os, &og) < 0)
453 {
454 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_PAGE,
455 "Error reading first page of Ogg bitstream");
456 goto err;
457 }
458
459 if (ogg_stream_packetout (state->os, &header_main) != 1)
460 {
461 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_HEADER,
462 "Error reading initial header packet");
463 goto err;
464 }
465
466 /* Save the main header first, it seems speex_packet_to_header() munges
467 * it. */
468 state->mainlen = header_main.bytes;
469 state->mainbuf = g_memdup (header_main.packet, header_main.bytes);
470
471 state->oggtype = ET_OGG_KIND_UNKNOWN;
472
473 if(vorbis_synthesis_headerin (state->vi, state->vc, &header_main) == 0)
474 {
475 state->oggtype = ET_OGG_KIND_VORBIS;
476 }
477 else
478 {
479 #ifdef ENABLE_SPEEX
480 /* Done after "Ogg test" to avoid to display an error message in
481 * function speex_packet_to_header() when the file is not Speex. */
482 if((state->si = speex_packet_to_header ((char*)(&header_main)->packet,
483 (&header_main)->bytes)))
484 {
485 state->oggtype = ET_OGG_KIND_SPEEX;
486 }
487 #endif
488
489 #ifdef ENABLE_OPUS
490 if (state->oggtype == ET_OGG_KIND_UNKNOWN)
491 {
492 state->oi = g_slice_new (OpusHead);
493
494 if (opus_head_parse (state->oi,
495 (unsigned char*)(&header_main)->packet,
496 (&header_main)->bytes) == 0)
497 {
498 state->oggtype = ET_OGG_KIND_OPUS;
499 }
500 }
501 #endif
502 }
503
504 if (state->oggtype == ET_OGG_KIND_UNKNOWN)
505 {
506 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_INVALID,
507 "Ogg bitstream contains unknown data");
508 goto err;
509 }
510
511 switch (state->oggtype)
512 {
513 case ET_OGG_KIND_VORBIS:
514 header = &header_comments;
515 headerpackets = 3;
516 break;
517 #ifdef ENABLE_SPEEX
518 case ET_OGG_KIND_SPEEX:
519 header = &header_comments;
520 headerpackets = 2 + state->si->extra_headers;
521 break;
522 #endif
523 #ifdef ENABLE_OPUS
524 case ET_OGG_KIND_OPUS:
525 header = &header_comments;
526 headerpackets = 2;
527 #endif
528 break;
529 #ifndef ENABLE_SPEEX
530 case ET_OGG_KIND_SPEEX:
531 #endif
532 #ifndef ENABLE_OPUS
533 case ET_OGG_KIND_OPUS:
534 #endif
535 case ET_OGG_KIND_UNKNOWN:
536 default:
537 g_assert_not_reached ();
538 break;
539 }
540
541 i = 1;
542
543 while (i < headerpackets)
544 {
545 while (i < headerpackets)
546 {
547 int result = ogg_sync_pageout (state->oy, &og);
548
549 if (result == 0)
550 {
551 break; /* Too little data so far */
552 }
553 else if (result == 1)
554 {
555 ogg_stream_pagein (state->os, &og);
556
557 while (i < headerpackets)
558 {
559 result = ogg_stream_packetout(state->os, header);
560 if (result == 0)
561 {
562 break;
563 }
564
565 if (result == -1)
566 {
567 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_CORRUPT,
568 "Corrupt secondary header");
569 goto err;
570 }
571 switch (state->oggtype)
572 {
573 case ET_OGG_KIND_VORBIS:
574 vorbis_synthesis_headerin (state->vi, state->vc,
575 header);
576 switch (i)
577 {
578 /* 0 packet was the Vorbis header. */
579 case 1:
580 header = &header_codebooks;
581 break;
582 case 2:
583 state->booklen = header->bytes;
584 state->bookbuf = g_memdup (header->packet,
585 header->bytes);
586 break;
587 default:
588 g_assert_not_reached ();
589 break;
590 }
591 break;
592 case ET_OGG_KIND_SPEEX:
593 switch (i)
594 {
595 /* 0 packet was the Speex header. */
596 case 1:
597 oggpack_readinit(&opb,header->packet,header->bytes);
598 _speex_unpack_comment(state->vc,&opb);
599 break;
600 default: /* FIXME. */
601 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_EXTRA,
602 "Need to save extra headers");
603 goto err;
604 break;
605 }
606 break;
607 #ifdef ENABLE_OPUS
608 case ET_OGG_KIND_OPUS:
609 switch (opus_tags_parse ((OpusTags *)state->vc,
610 header->packet,
611 header->bytes))
612 {
613 case 0:
614 break;
615
616 case OP_ENOTFORMAT:
617 g_set_error (error, ET_OGG_ERROR,
618 ET_OGG_ERROR_HEADER,
619 "Ogg Opus tags do not start with \"OpusTags\"");
620 goto err;
621 break;
622
623 case OP_EFAULT:
624 g_set_error (error, ET_OGG_ERROR,
625 ET_OGG_ERROR_HEADER,
626 "Not enough memory to store Ogg Opus tags");
627 goto err;
628 break;
629
630 case OP_EBADHEADER:
631 g_set_error (error, ET_OGG_ERROR,
632 ET_OGG_ERROR_HEADER,
633 "Ogg Opus tags do not follow the specification");
634 goto err;
635 break;
636 default:
637 g_assert_not_reached ();
638 break;
639 }
640 break;
641 #endif
642 #ifndef ENABLE_OPUS
643 case ET_OGG_KIND_OPUS:
644 #endif
645 case ET_OGG_KIND_UNKNOWN:
646 default:
647 g_assert_not_reached ();
648 break;
649 }
650
651 i++;
652 }
653 }
654 }
655
656 buffer = ogg_sync_buffer (state->oy, CHUNKSIZE);
657 bytes = g_input_stream_read (G_INPUT_STREAM (state->in), buffer,
658 CHUNKSIZE, NULL, error);
659
660 if (bytes == -1)
661 {
662 goto err;
663 }
664
665 if (bytes == 0 && i < 2)
666 {
667 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_VORBIS,
668 "EOF before end of Vorbis headers");
669 goto err;
670 }
671 ogg_sync_wrote (state->oy, bytes);
672 }
673
674 /* Copy the vendor tag */
675 state->vendor = g_strdup (state->vc->vendor);
676
677 /* Headers are done! */
678 g_assert (error == NULL || *error == NULL);
679
680 return TRUE;
681
682 err:
683 g_assert (error == NULL || *error != NULL);
684 vcedit_clear_internals (state);
685 return FALSE;
686 }
687
688 gboolean
vcedit_write(EtOggState * state,GFile * file,GError ** error)689 vcedit_write (EtOggState *state,
690 GFile *file,
691 GError **error)
692 {
693 ogg_stream_state streamout;
694 ogg_packet header_main;
695 ogg_packet header_comments;
696 ogg_packet header_codebooks;
697
698 ogg_page ogout, ogin;
699 ogg_packet op;
700 ogg_int64_t granpos = 0;
701 int result;
702 char *buffer;
703 int bytes;
704 int needflush = 0, needout = 0;
705 GOutputStream *ostream;
706 gchar *buf;
707 gsize size;
708 GFileInfo *fileinfo;
709
710 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
711
712 fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
713 G_FILE_QUERY_INFO_NONE, NULL, error);
714 if (!fileinfo)
715 {
716 g_assert (error == NULL || *error != NULL);
717 return FALSE;
718 }
719
720 buf = g_malloc (g_file_info_get_size (fileinfo));
721 ostream = g_memory_output_stream_new (buf,
722 g_file_info_get_size (fileinfo),
723 g_realloc, g_free);
724 g_object_unref (fileinfo);
725
726 state->eosin = 0;
727 state->extrapage = 0;
728
729 header_main.bytes = state->mainlen;
730 header_main.packet = state->mainbuf;
731 header_main.b_o_s = 1;
732 header_main.e_o_s = 0;
733 header_main.granulepos = 0;
734
735 header_codebooks.bytes = state->booklen;
736 header_codebooks.packet = state->bookbuf;
737 header_codebooks.b_o_s = 0;
738 header_codebooks.e_o_s = 0;
739 header_codebooks.granulepos = 0;
740
741 ogg_stream_init (&streamout, state->serial);
742
743 _commentheader_out (state, &header_comments);
744
745 ogg_stream_packetin (&streamout, &header_main);
746 ogg_stream_packetin (&streamout, &header_comments);
747
748 if (state->oggtype == ET_OGG_KIND_VORBIS)
749 {
750 ogg_stream_packetin (&streamout, &header_codebooks);
751 }
752
753 while ((result = ogg_stream_flush (&streamout, &ogout)))
754 {
755 gsize bytes_written;
756
757 if (!g_output_stream_write_all (ostream, ogout.header,
758 ogout.header_len, &bytes_written, NULL,
759 error))
760 {
761 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes of data "
762 "were written", bytes_written, ogout.header_len);
763 g_assert (error == NULL || *error != NULL);
764 goto cleanup;
765 }
766
767 if (!g_output_stream_write_all (ostream, ogout.body, ogout.body_len,
768 &bytes_written, NULL, error))
769 {
770 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes of data "
771 "were written", bytes_written, ogout.body_len);
772 g_assert (error == NULL || *error != NULL);
773 goto cleanup;
774 }
775 }
776
777 while (_fetch_next_packet (state, &op, &ogin, error))
778 {
779 if (needflush)
780 {
781 if (ogg_stream_flush (&streamout, &ogout))
782 {
783 gsize bytes_written;
784
785 if (!g_output_stream_write_all (ostream, ogout.header,
786 ogout.header_len, &bytes_written,
787 NULL, error))
788 {
789 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes of "
790 "data were written", bytes_written, ogout.header_len);
791 g_assert (error == NULL || *error != NULL);
792 goto cleanup;
793 }
794
795 if (!g_output_stream_write_all (ostream, ogout.body,
796 ogout.body_len, &bytes_written,
797 NULL, error))
798 {
799 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes of "
800 "data were written", bytes_written, ogout.body_len);
801 g_assert (error == NULL || *error != NULL);
802 goto cleanup;
803 }
804 }
805 }
806 else if (needout)
807 {
808 if(ogg_stream_pageout (&streamout, &ogout))
809 {
810 gsize bytes_written;
811
812 if (!g_output_stream_write_all (ostream, ogout.header,
813 ogout.header_len,
814 &bytes_written, NULL, error))
815 {
816 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes "
817 "of data were written", bytes_written,
818 ogout.header_len);
819 g_assert (error == NULL || *error != NULL);
820 goto cleanup;
821 }
822
823 if (!g_output_stream_write_all (ostream, ogout.body,
824 ogout.body_len, &bytes_written,
825 NULL, error))
826 {
827 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes "
828 "of data were written", bytes_written,
829 ogout.body_len);
830 g_assert (error == NULL || *error != NULL);
831 goto cleanup;
832 }
833 }
834 }
835
836 needflush = needout = 0;
837
838 if (state->oggtype == ET_OGG_KIND_VORBIS ||
839 state->oggtype == ET_OGG_KIND_OPUS)
840 {
841 if (state->oggtype == ET_OGG_KIND_VORBIS)
842 {
843 granpos += _blocksize (state, &op);
844 }
845 #ifdef ENABLE_OPUS
846 else
847 {
848 granpos += opus_packet_get_samples_per_frame (op.packet,
849 48000);
850 }
851 #endif
852 if(op.granulepos == -1)
853 {
854 op.granulepos = granpos;
855 ogg_stream_packetin (&streamout, &op);
856 }
857 else /* granulepos is set, validly. Use it, and force a flush to
858 account for shortened blocks (vcut) when appropriate */
859 {
860 if (granpos > op.granulepos)
861 {
862 granpos = op.granulepos;
863 ogg_stream_packetin (&streamout, &op);
864 needflush = 1;
865 }
866 else
867 {
868 ogg_stream_packetin (&streamout, &op);
869 needout = 1;
870 }
871 }
872 }
873 /* Don't know about granulepos for speex, will just assume the original
874 was appropriate. Not sure about the flushing?? */
875 else if (state->oggtype == ET_OGG_KIND_SPEEX)
876 {
877 ogg_stream_packetin (&streamout, &op);
878 needout = 1;
879 }
880 }
881
882 if (error != NULL)
883 {
884 if (g_error_matches (*error, ET_OGG_ERROR, ET_OGG_ERROR_EOF)
885 || g_error_matches (*error, ET_OGG_ERROR, ET_OGG_ERROR_EOS))
886 {
887 /* While nominally errors, these are expected and can be safely
888 * ignored. */
889 g_clear_error (error);
890 }
891 else
892 {
893 goto cleanup;
894 }
895 }
896
897 streamout.e_o_s = 1;
898
899 while (ogg_stream_flush (&streamout, &ogout))
900 {
901 gsize bytes_written;
902
903 if (!g_output_stream_write_all (ostream, ogout.header,
904 ogout.header_len, &bytes_written, NULL,
905 error))
906 {
907 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes of data "
908 "were written", bytes_written, ogout.header_len);
909 g_assert (error == NULL || *error != NULL);
910 goto cleanup;
911 }
912
913 if (!g_output_stream_write_all (ostream, ogout.body, ogout.body_len,
914 &bytes_written, NULL, error))
915 {
916 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes of data "
917 "were written", bytes_written, ogout.body_len);
918 g_assert (error == NULL || *error != NULL);
919 goto cleanup;
920 }
921 }
922
923 if (state->extrapage)
924 {
925 gsize bytes_written;
926
927 if (!g_output_stream_write_all (ostream, ogout.header,
928 ogout.header_len, &bytes_written, NULL,
929 error))
930 {
931 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes of data "
932 "were written", bytes_written, ogout.header_len);
933 g_assert (error == NULL || *error != NULL);
934 goto cleanup;
935 }
936
937 if (!g_output_stream_write_all (ostream, ogout.body, ogout.body_len,
938 &bytes_written, NULL, error))
939 {
940 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %ld bytes of data "
941 "were written", bytes_written, ogout.body_len);
942 g_assert (error == NULL || *error != NULL);
943 goto cleanup;
944 }
945 }
946
947 state->eosin = 0; /* clear it, because not all paths to here do */
948
949 while (!state->eosin) /* We reached eos, not eof */
950 {
951 /* We copy the rest of the stream (other logical streams)
952 * through, a page at a time. */
953 while(1)
954 {
955 result = ogg_sync_pageout (state->oy, &ogout);
956
957 if (result == 0)
958 {
959 break;
960 }
961
962 if (result < 0)
963 {
964 g_debug ("%s", "Corrupt or missing data, continuing");
965 }
966 else
967 {
968 gsize bytes_written;
969
970 /* Don't bother going through the rest, we can just
971 * write the page out now */
972 if (!g_output_stream_write_all (ostream, ogout.header,
973 ogout.header_len,
974 &bytes_written, NULL, error))
975 {
976 g_assert (error == NULL || *error != NULL);
977 goto cleanup;
978 }
979
980 if (!g_output_stream_write_all (ostream, ogout.body,
981 ogout.body_len, &bytes_written,
982 NULL, error))
983 {
984 g_assert (error == NULL || *error != NULL);
985 goto cleanup;
986 }
987 }
988 }
989
990 buffer = ogg_sync_buffer (state->oy, CHUNKSIZE);
991
992 bytes = g_input_stream_read (G_INPUT_STREAM (state->in), buffer,
993 CHUNKSIZE, NULL, error);
994
995 if (bytes == -1)
996 {
997 g_assert (error == NULL || *error != NULL);
998 goto cleanup;
999 }
1000
1001 ogg_sync_wrote (state->oy, bytes);
1002
1003 if (bytes == 0)
1004 {
1005 state->eosin = 1;
1006 break;
1007 }
1008 }
1009
1010
1011 cleanup:
1012 ogg_stream_clear (&streamout);
1013 ogg_packet_clear (&header_comments);
1014
1015 g_free (state->mainbuf);
1016 g_free (state->bookbuf);
1017 state->mainbuf = state->bookbuf = NULL;
1018
1019 if (!state->eosin)
1020 {
1021 if (!error)
1022 {
1023 g_set_error (error, ET_OGG_ERROR, ET_OGG_ERROR_OUTPUT,
1024 "Error writing stream to output. Output stream may be corrupted or truncated");
1025 }
1026 }
1027
1028 if (error == NULL || *error != NULL)
1029 {
1030 g_object_unref (ostream);
1031 return FALSE;
1032 }
1033
1034 g_assert (error == NULL || *error == NULL);
1035
1036 if (!g_output_stream_close (ostream, NULL, error))
1037 {
1038 g_object_unref (ostream);
1039 g_assert (error == NULL || *error != NULL);
1040 return FALSE;
1041 }
1042
1043 g_assert (error == NULL || *error == NULL);
1044
1045 buf = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (ostream));
1046 size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (ostream));
1047
1048 /* At least on Windows, writing to a file with an open-for-reading stream
1049 * fails, so close the input stream before writing to the file. */
1050 if (!g_input_stream_close (G_INPUT_STREAM (state->in), NULL, error))
1051 {
1052 /* Ignore the _close() failure, and try the write anyway. */
1053 g_warning ("Error closing Ogg file for reading: %s",
1054 (*error)->message);
1055 g_clear_error (error);
1056 }
1057
1058 g_object_unref (state->in);
1059 state->in = NULL;
1060
1061 /* Write the in-memory data back out to the original file. */
1062 if (!g_file_replace_contents (file, buf, size, NULL, FALSE,
1063 G_FILE_CREATE_NONE, NULL, NULL, error))
1064 {
1065 GError *tmp_error = NULL;
1066
1067 g_object_unref (ostream);
1068 g_free (buf);
1069
1070 /* Re-open the file for reading, to keep the internal state
1071 * consistent. */
1072 state->in = g_file_read (file, NULL, &tmp_error);
1073
1074 if (!state->in)
1075 {
1076 g_warning ("Error opening Ogg file for reading after write failure: %s",
1077 tmp_error->message);
1078 g_clear_error (&tmp_error);
1079 g_assert (error == NULL || *error != NULL);
1080 return FALSE;
1081 }
1082
1083 g_assert (error == NULL || *error != NULL);
1084 return FALSE;
1085 }
1086
1087 g_free (buf);
1088 g_object_unref (ostream);
1089
1090 /* Re-open the file, now that the write has completed. */
1091 state->in = g_file_read (file, NULL, error);
1092
1093 if (!state->in)
1094 {
1095 g_assert (error == NULL || *error != NULL);
1096 return FALSE;
1097 }
1098
1099
1100 return TRUE;
1101 }
1102
1103 #endif /* ENABLE_OGG */
1104