1 /*
2    Copyright (C) 2003 Commonwealth Scientific and Industrial Research
3    Organisation (CSIRO) Australia
4 
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8 
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11 
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 
16    - Neither the name of CSIRO Australia nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
19 
20    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
24    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 #include "config.h"
34 
35 #if OGGZ_CONFIG_WRITE
36 
37 #include <assert.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46 
47 #include <fcntl.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <time.h>
51 
52 #include <ogg/ogg.h>
53 
54 #include "oggz_private.h"
55 #include "oggz_vector.h"
56 
57 /* #define DEBUG */
58 
59 /* Define to 0 or 1 */
60 #define ALWAYS_FLUSH 0
61 
62 #define OGGZ_WRITE_EMPTY (-707)
63 
64 /* #define ZPACKET_CMP */
65 
66 #ifdef ZPACKET_CMP
67 static int
oggz_zpacket_cmp(oggz_writer_packet_t * a,oggz_writer_packet_t * b,void * user_data)68 oggz_zpacket_cmp (oggz_writer_packet_t * a, oggz_writer_packet_t * b,
69 		  void * user_data)
70 {
71   OGGZ * oggz = (OGGZ *)user_data;
72   long serialno_a, serialno_b;
73   ogg_int64_t unit_a, unit_b;
74 
75   serialno_a = a->stream->ogg_stream.serialno;
76   serialno_b = b->stream->ogg_stream.serialno;
77 
78   unit_a = oggz_get_unit (oggz, serialno_a, a->op.granulepos);
79   unit_b = oggz_get_unit (oggz, serialno_b, b->op.granulepos);
80 
81   if (unit_a < unit_b) return -1;
82   else return (unit_a > unit_b);
83 }
84 #endif
85 
86 OGGZ *
oggz_write_init(OGGZ * oggz)87 oggz_write_init (OGGZ * oggz)
88 {
89   OggzWriter * writer = &oggz->x.writer;
90 
91   writer->next_zpacket = NULL;
92 
93   writer->packet_queue = oggz_vector_new ();
94   if (writer->packet_queue == NULL) return NULL;
95 
96 #ifdef ZPACKET_CMP
97   /* XXX: comparison function should only kick in when a metric is set */
98   oggz_vector_set_cmp (writer->packet_queue,
99 		       (OggzCmpFunc)oggz_zpacket_cmp, oggz);
100 #endif
101 
102   writer->hungry = NULL;
103   writer->hungry_user_data = NULL;
104   writer->hungry_only_when_empty = 0;
105 
106   writer->writing = 0;
107   writer->no_more_packets = 0;
108   writer->state = OGGZ_MAKING_PACKETS;
109 
110   writer->flushing = 0;
111 #if 0
112   writer->eog = 1;
113   writer->eop = 1; /* init ready to start next packet */
114 #endif
115   writer->eos = 0;
116 
117   writer->current_zpacket = NULL;
118 
119   writer->packet_offset = 0;
120   writer->page_offset = 0;
121 
122   writer->current_stream = NULL;
123 
124   return oggz;
125 }
126 
127 static int
oggz_writer_packet_free(oggz_writer_packet_t * zpacket)128 oggz_writer_packet_free (oggz_writer_packet_t * zpacket)
129 {
130   if (!zpacket) return 0;
131 
132   if (zpacket->guard) {
133     /* managed by user; flag guard */
134     *zpacket->guard = 1;
135   } else {
136     /* managed by oggz; free copied data */
137     oggz_free (zpacket->op.packet);
138   }
139   oggz_free (zpacket);
140 
141   return 0;
142 }
143 
144 int
oggz_write_flush(OGGZ * oggz)145 oggz_write_flush (OGGZ * oggz)
146 {
147   OggzWriter * writer = &oggz->x.writer;
148   ogg_stream_state * os;
149   ogg_page * og;
150   int ret = 0;
151 
152   os = writer->current_stream;
153   og = &oggz->current_page;
154 
155   if (os != NULL)
156     ret = ogg_stream_flush (os, og);
157 
158   return ret;
159 }
160 
161 OGGZ *
oggz_write_close(OGGZ * oggz)162 oggz_write_close (OGGZ * oggz)
163 {
164   OggzWriter * writer = &oggz->x.writer;
165 
166   oggz_write_flush (oggz);
167 
168   oggz_writer_packet_free (writer->current_zpacket);
169   oggz_writer_packet_free (writer->next_zpacket);
170 
171   oggz_vector_foreach (writer->packet_queue,
172 		       (OggzFunc)oggz_writer_packet_free);
173   oggz_vector_delete (writer->packet_queue);
174 
175   return oggz;
176 }
177 
178 /******** Packet queueing ********/
179 
180 int
oggz_write_set_hungry_callback(OGGZ * oggz,OggzWriteHungry hungry,int only_when_empty,void * user_data)181 oggz_write_set_hungry_callback (OGGZ * oggz, OggzWriteHungry hungry,
182 				int only_when_empty, void * user_data)
183 {
184   OggzWriter * writer;
185 
186   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
187 
188   if (!(oggz->flags & OGGZ_WRITE)) {
189     return OGGZ_ERR_INVALID;
190   }
191 
192   writer = &oggz->x.writer;
193 
194   writer->hungry = hungry;
195   writer->hungry_user_data = user_data;
196   writer->hungry_only_when_empty = only_when_empty;
197 
198   return 0;
199 }
200 
201 int
oggz_write_feed(OGGZ * oggz,ogg_packet * op,long serialno,int flush,int * guard)202 oggz_write_feed (OGGZ * oggz, ogg_packet * op, long serialno, int flush,
203 		 int * guard)
204 {
205   OggzWriter * writer;
206   oggz_stream_t * stream;
207   oggz_writer_packet_t * packet;
208   ogg_packet * new_op;
209   unsigned char * new_buf = NULL;
210   int b_o_s, e_o_s, bos_auto;
211   int strict, prefix, suffix;
212 
213 #ifdef DEBUG
214   printf ("oggz_write_feed: IN\n");
215 #endif
216 
217   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
218 
219   if (!(oggz->flags & OGGZ_WRITE)) {
220     return OGGZ_ERR_INVALID;
221   }
222 
223   writer = &oggz->x.writer;
224 
225   if (guard && *guard != 0) return OGGZ_ERR_BAD_GUARD;
226 
227   /* Check that the serialno is in the valid range for an Ogg page header,
228    * ie. that it fits within 32 bits and does not equal the special value -1 */
229   if ((long)((ogg_int32_t)serialno) != serialno || serialno == -1) {
230 #ifdef DEBUG
231     printf ("oggz_write_feed: serialno %010lu\n", serialno);
232 #endif
233     return OGGZ_ERR_BAD_SERIALNO;
234   }
235 
236 #ifdef DEBUG
237   printf ("oggz_write_feed: (%010lu) FLUSH: %d\n", serialno, flush);
238 #endif
239 
240   /* Cache strict, prefix, suffix */
241   strict = !(oggz->flags & OGGZ_NONSTRICT);
242   prefix = (oggz->flags & OGGZ_PREFIX);
243   suffix = (oggz->flags & OGGZ_SUFFIX);
244 
245   /* Set bos, eos to canonical values */
246   bos_auto = (op->b_o_s == -1) ? 1 : 0;
247   b_o_s = op->b_o_s ? 1 : 0;
248   e_o_s = op->e_o_s ? 1 : 0;
249 
250   stream = oggz_get_stream (oggz, serialno);
251   if (stream == NULL) {
252     if (bos_auto) b_o_s = 1;
253 
254     if (strict && b_o_s && !oggz_get_bos (oggz, -1)) {
255 	return OGGZ_ERR_BOS;
256     }
257 
258     if (b_o_s || !strict || suffix) {
259       stream = oggz_add_stream (oggz, serialno);
260       if (stream == NULL)
261         return OGGZ_ERR_OUT_OF_MEMORY;
262       oggz_auto_identify_packet (oggz, op, serialno);
263     } else {
264       return OGGZ_ERR_BAD_SERIALNO;
265     }
266   } else {
267     if (bos_auto) b_o_s = 0;
268 
269     if (!suffix && strict && stream->e_o_s)
270       return OGGZ_ERR_EOS;
271   }
272 
273   /* Check packet against mapping restrictions */
274   if (strict) {
275     if (op->bytes < 0) return OGGZ_ERR_BAD_BYTES;
276     if (!suffix && b_o_s != stream->b_o_s) return OGGZ_ERR_BAD_B_O_S;
277     if (op->granulepos != -1 && op->granulepos < stream->granulepos &&
278         /* Allow negative granulepos immediately after headers, for Dirac: */
279         !(stream->granulepos == 0 && op->granulepos < 0))
280       return OGGZ_ERR_BAD_GRANULEPOS;
281 
282     /* Allow packetno == -1 to indicate oggz should fill it in; otherwise:
283      * if op is the first packet in the stream, initialize the stream's
284      * packetno to the given one, otherwise check it for strictness.
285      * For stream suffixes, believe the packetno value */
286     if (op->packetno != -1) {
287       if (b_o_s || suffix) {
288 	stream->packetno = op->packetno;
289       } else if (op->packetno <= stream->packetno) {
290 	return OGGZ_ERR_BAD_PACKETNO;
291       }
292     }
293   }
294 
295   /* OK -- Update stream's memory of packet details */
296 
297   if (!stream->metric && (oggz->flags & OGGZ_AUTO)) {
298     oggz_auto_read_bos_packet (oggz, op, serialno, NULL);
299   }
300 
301   stream->b_o_s = 0; /* The stream is henceforth no longer at bos */
302   stream->e_o_s = e_o_s; /* We believe the eos value */
303   stream->granulepos = op->granulepos; /* and the granulepos */
304 
305   /* If the user specified a packetno of -1, fill it in automatically;
306    * otherwise, use the user-specified value */
307   stream->packetno = (op->packetno != -1) ? op->packetno : stream->packetno+1;
308 
309   /* Now set up the packet and add it to the queue */
310   if (guard == NULL) {
311     new_buf = oggz_malloc ((size_t)op->bytes);
312     if (new_buf == NULL) return OGGZ_ERR_OUT_OF_MEMORY;
313 
314     memcpy (new_buf, op->packet, (size_t)op->bytes);
315   } else {
316     new_buf = op->packet;
317   }
318 
319   packet = oggz_malloc (sizeof (oggz_writer_packet_t));
320   if (packet == NULL) {
321     if (guard == NULL && new_buf != NULL) oggz_free (new_buf);
322     return OGGZ_ERR_OUT_OF_MEMORY;
323   }
324 
325   new_op = &packet->op;
326   new_op->packet = new_buf;
327   new_op->bytes = op->bytes;
328   new_op->b_o_s = b_o_s;
329   new_op->e_o_s = e_o_s;
330   new_op->granulepos = op->granulepos;
331   new_op->packetno = stream->packetno;
332 
333   packet->stream = stream;
334   packet->flush = flush;
335   packet->guard = guard;
336 
337 #ifdef DEBUG
338   printf ("oggz_write_feed: made packet bos %ld eos %ld (%ld bytes) FLUSH: %d\n",
339 	  new_op->b_o_s, new_op->e_o_s, new_op->bytes, packet->flush);
340 #endif
341 
342   if (oggz_vector_insert_p (writer->packet_queue, packet) == NULL) {
343     oggz_free (packet);
344     if (!guard) oggz_free (new_buf);
345     return -1;
346   }
347 
348   writer->no_more_packets = 0;
349 
350 #ifdef DEBUG
351   printf ("oggz_write_feed: enqueued packet, queue size %d\n",
352 	  oggz_vector_size (writer->packet_queue));
353 #endif
354 
355   return 0;
356 }
357 
358 /******** Page creation ********/
359 
360 /*
361  *
362 
363             ____           __________________\___           ____
364            /    \         /    Page ready    /   \         /    \
365           |      \ /=========\                /=========\ /      |
366   Page    |       || Make    ||              ||  Write  ||       | Page
367   !ready  V       || packet  ||              ||  page   ||       V ready
368           |      / \=========/                \=========/ \      |
369            \____/         \___/__________________/         \____/
370                               \   Page !ready
371  *
372  */
373 
374 
375 /*
376  * oggz_page_init (oggz)
377  *
378  * Initialises the next page of the current packet.
379  *
380  * If this returns 0, the page is not ready for writing.
381  */
382 static long
oggz_page_init(OGGZ * oggz)383 oggz_page_init (OGGZ * oggz)
384 {
385   OggzWriter * writer;
386   ogg_stream_state * os;
387   ogg_page * og;
388   int ret;
389 
390   if (oggz == NULL) return -1;
391 
392   writer = &oggz->x.writer;
393   os = writer->current_stream;
394   og = &oggz->current_page;
395 
396   if (ALWAYS_FLUSH || writer->flushing) {
397 #ifdef DEBUG
398     printf ("oggz_page_init: ATTEMPT FLUSH: ");
399 #endif
400     ret = oggz_write_flush (oggz);
401   } else {
402 #ifdef DEBUG
403     printf ("oggz_page_init: ATTEMPT pageout: ");
404 #endif
405     ret = ogg_stream_pageout (os, og);
406   }
407 
408   if (ret) {
409     writer->page_offset = 0;
410   }
411 
412 #ifdef DEBUG
413   printf ("%s\n", ret ? "OK" : "NO");
414 #endif
415 
416   return ret;
417 }
418 
419 /*
420  * oggz_packet_init (oggz, buf, n)
421  *
422  * Initialises the next packet with data from buf, length n
423  */
424 static long
oggz_packet_init(OGGZ * oggz,oggz_writer_packet_t * next_zpacket)425 oggz_packet_init (OGGZ * oggz, oggz_writer_packet_t * next_zpacket)
426 {
427   OggzWriter * writer;
428   oggz_stream_t * stream;
429   ogg_stream_state * os;
430   ogg_packet * op;
431 
432   if (oggz == NULL) return -1L;
433 
434   writer = &oggz->x.writer;
435   writer->current_zpacket = next_zpacket;
436   op = &next_zpacket->op;
437 
438 #ifdef DEBUG
439   printf ("oggz_packet_init: %ld bytes\n", (long)op->bytes);
440 #endif
441 
442   stream = next_zpacket->stream;
443 
444   /* Mark this stream as having delivered a non b_o_s packet if so */
445   if (!op->b_o_s) stream->delivered_non_b_o_s = 1;
446 
447   os = &stream->ogg_stream;
448   ogg_stream_packetin (os, op);
449 
450   writer->flushing = (next_zpacket->flush & OGGZ_FLUSH_AFTER);
451 #ifdef DEBUG
452   printf ("oggz_packet_init: set flush to %d\n", writer->flushing);
453 #endif
454   writer->current_stream = os;
455   writer->packet_offset = 0;
456 
457   return 0;
458 }
459 
460 static long
oggz_page_copyout(OGGZ * oggz,unsigned char * buf,long n)461 oggz_page_copyout (OGGZ * oggz, unsigned char * buf, long n)
462 {
463   OggzWriter * writer;
464   long h, b;
465   ogg_page * og;
466 
467   if (oggz == NULL) return -1L;
468 
469   writer = &oggz->x.writer;
470   og = &oggz->current_page;
471 
472   h = MIN (n, og->header_len - writer->page_offset);
473   if (h > 0) {
474     memcpy (buf, og->header + writer->page_offset, h);
475     writer->page_offset += h;
476     n -= h;
477     buf += h;
478   } else {
479     h = 0;
480   }
481 
482   b = MIN (n, og->header_len + og->body_len - writer->page_offset);
483   if (b > 0) {
484 #ifdef DEBUG
485     {
486       unsigned char * c = &og->body[writer->page_offset - og->header_len];
487       printf ("oggz_page_copyout [%d] (@%ld): %c%c%c%c ...\n",
488 	      ogg_page_serialno (og), (long) ogg_page_granulepos (og),
489 	      c[0], c[1], c[2], c[3]);
490     }
491 #endif
492     memcpy (buf, og->body + (writer->page_offset - og->header_len), b);
493     writer->page_offset += b;
494     n -= b;
495     buf += b;
496   } else {
497     b = 0;
498   }
499 
500   return h + b;
501 }
502 
503 static long
oggz_page_writeout(OGGZ * oggz,long n)504 oggz_page_writeout (OGGZ * oggz, long n)
505 {
506   OggzWriter * writer;
507   long h, b, nwritten;
508   ogg_page * og;
509 
510 #ifdef OGGZ_WRITE_DIRECT
511   int fd;
512 #endif
513 
514   if (oggz == NULL) return -1L;
515 
516   writer = &oggz->x.writer;
517   og = &oggz->current_page;
518 
519 #ifdef OGGZ_WRITE_DIRECT
520   fd = fileno (oggz->file);
521 #endif
522 
523   h = MIN (n, og->header_len - writer->page_offset);
524   if (h > 0) {
525 #ifdef OGGZ_WRITE_DIRECT
526     nwritten = write (fd, og->header + writer->page_offset, h);
527 #else
528     nwritten = (long)oggz_io_write (oggz, og->header + writer->page_offset, h);
529 #endif
530 
531 #ifdef DEBUG
532     if (nwritten < h) {
533       printf ("oggz_page_writeout: %ld < %ld\n", nwritten, h);
534     }
535 #endif
536     writer->page_offset += h;
537     n -= h;
538   } else {
539     h = 0;
540   }
541 
542   b = MIN (n, og->header_len + og->body_len - writer->page_offset);
543   if (b > 0) {
544 #ifdef DEBUG
545     {
546       unsigned char * c = &og->body[writer->page_offset - og->header_len];
547       printf ("oggz_page_writeout [%d] (@%ld): %c%c%c%c ...\n",
548 	      ogg_page_serialno (og), (long) ogg_page_granulepos (og),
549 	      c[0], c[1], c[2], c[3]);
550     }
551 #endif
552 #ifdef OGGZ_WRITE_DIRECT
553     nwritten = write (fd,
554 		      og->body + (writer->page_offset - og->header_len), b);
555 #else
556     nwritten = (long)oggz_io_write (oggz, og->body + (writer->page_offset - og->header_len), b);
557 #endif
558 #ifdef DEBUG
559     if (nwritten < b) {
560       printf ("oggz_page_writeout: %ld < %ld\n", nwritten, b);
561     }
562 #endif
563     writer->page_offset += b;
564     n -= b;
565   } else {
566     b = 0;
567   }
568 
569   return h + b;
570 }
571 
572 static int
oggz_dequeue_packet(OGGZ * oggz,oggz_writer_packet_t ** next_zpacket)573 oggz_dequeue_packet (OGGZ * oggz, oggz_writer_packet_t ** next_zpacket)
574 {
575   OggzWriter * writer = &oggz->x.writer;
576   int ret = 0;
577 
578   if (writer->next_zpacket != NULL) {
579 #ifdef DEBUG
580     printf ("oggz_dequeue_packet: queue EMPTY\n");
581 #endif
582     *next_zpacket = writer->next_zpacket;
583     writer->next_zpacket = NULL;
584   } else {
585     *next_zpacket = oggz_vector_pop (writer->packet_queue);
586 
587     if (*next_zpacket == NULL) {
588       if (writer->hungry) {
589         ret = writer->hungry (oggz, 1, writer->hungry_user_data);
590         *next_zpacket = oggz_vector_pop (writer->packet_queue);
591 #ifdef DEBUG
592         printf ("oggz_dequeue_packet: called hungry and popped, new queue size %d\n",
593   	        oggz_vector_size (writer->packet_queue));
594 #endif
595 
596 #ifdef DEBUG
597       } else {
598         printf ("oggz_dequeue_packet: no packet, no hungry, queue size %d\n",
599                 oggz_vector_size (writer->packet_queue));
600 #endif
601       }
602 #ifdef DEBUG
603     } else {
604     printf ("oggz_dequeue_packet: dequeued packet, queue size %d\n",
605             oggz_vector_size (writer->packet_queue));
606 #endif
607     }
608 
609   }
610 
611 #ifdef DEBUG
612   printf("oggz_dequeue_packeT: returning %d\n", ret);
613 #endif
614   return ret;
615 }
616 
617 static long
oggz_writer_make_packet(OGGZ * oggz)618 oggz_writer_make_packet (OGGZ * oggz)
619 {
620   OggzWriter * writer = &oggz->x.writer;
621   oggz_writer_packet_t * zpacket, * next_zpacket = NULL;
622   int cb_ret = 0;
623 
624 #ifdef DEBUG
625   printf ("oggz_writer_make_packet: IN\n");
626 #endif
627 
628   /* finished with current packet; unguard */
629   zpacket = writer->current_zpacket;
630   oggz_writer_packet_free (zpacket);
631   writer->current_zpacket = NULL;
632 
633   /* if the user wants the hungry callback after every packet, give
634    * it to them, marking emptiness appropriately
635    */
636   if (writer->hungry && !writer->hungry_only_when_empty) {
637     int empty = (oggz_vector_size (writer->packet_queue) == 0);
638     cb_ret = writer->hungry (oggz, empty, writer->hungry_user_data);
639   }
640 
641   if (cb_ret == 0) {
642     /* dequeue and init the next packet */
643     cb_ret = oggz_dequeue_packet (oggz, &next_zpacket);
644     if (next_zpacket == NULL) {
645 #ifdef DEBUG
646       printf ("oggz_writer_make_packet: packet queue empty\n");
647 #endif
648       /*writer->eos = 1;*/
649     } else {
650       if ((writer->current_stream != NULL) &&
651 	  (next_zpacket->flush & OGGZ_FLUSH_BEFORE)) {
652 	writer->flushing = 1;
653 #ifdef DEBUG
654 	printf ("oggz_writer_make_packet: set flush to %d\n",
655 		writer->flushing);
656 #endif
657 	next_zpacket->flush &= OGGZ_FLUSH_AFTER;
658 	writer->next_zpacket = next_zpacket;
659       } else {
660 	oggz_packet_init (oggz, next_zpacket);
661       }
662     }
663   }
664 
665 #ifdef DEBUG
666   printf("oggz_writer_make_packet: cb_ret is %d\n", cb_ret);
667 #endif
668 
669   if (cb_ret == 0 && next_zpacket == NULL) return OGGZ_WRITE_EMPTY;
670 
671   return cb_ret;
672 }
673 
674 long
oggz_write_output(OGGZ * oggz,unsigned char * buf,long n)675 oggz_write_output (OGGZ * oggz, unsigned char * buf, long n)
676 {
677   OggzWriter * writer;
678   long bytes, bytes_written = 1, remaining = n, nwritten = 0;
679   int active = 1, cb_ret = 0;
680 
681   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
682 
683   writer = &oggz->x.writer;
684 
685   if (!(oggz->flags & OGGZ_WRITE)) {
686     return OGGZ_ERR_INVALID;
687   }
688 
689   if (writer->writing) return OGGZ_ERR_RECURSIVE_WRITE;
690   writer->writing = 1;
691 
692 #ifdef DEBUG
693   printf ("oggz_write_output: IN\n");
694 #endif
695 
696   if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
697     oggz->cb_next = 0;
698     writer->writing = 0;
699     writer->no_more_packets = 0;
700     if (cb_ret == OGGZ_WRITE_EMPTY) cb_ret = 0;
701     return oggz_map_return_value_to_error (cb_ret);
702   }
703 
704   while (active && remaining > 0) {
705     bytes = MIN (remaining, 1024);
706 
707 #ifdef DEBUG
708     printf ("oggz_write_output: write loop (%ld , %ld remain) ...\n", bytes,
709 	    remaining);
710 #endif
711 
712     while (writer->state == OGGZ_MAKING_PACKETS) {
713 #ifdef DEBUG
714       	printf ("oggz_write_output: MAKING_PACKETS\n");
715 #endif
716       if ((cb_ret = oggz_writer_make_packet (oggz)) != OGGZ_CONTINUE) {
717 #ifdef DEBUG
718         printf ("oggz_write_output: no packets (cb_ret is %d)\n", cb_ret);
719 #endif
720         if (cb_ret == OGGZ_WRITE_EMPTY) {
721           writer->flushing = 1;
722           writer->no_more_packets = 1;
723         }
724         /* At this point, in contrast to oggz_write(), we break out of this
725          * loop unconditionally.
726          */
727         active = 0;
728         break;
729       }
730       if (oggz_page_init (oggz)) {
731         writer->state = OGGZ_WRITING_PAGES;
732       } else {
733 #ifdef DEBUG
734         printf ("oggz_write_output: unable to make page...\n");
735 #endif
736         if (writer->no_more_packets) {
737           active = 0;
738           break;
739         }
740       }
741     }
742 
743     if (writer->state == OGGZ_WRITING_PAGES) {
744       bytes_written = oggz_page_copyout (oggz, buf, bytes);
745 
746       if (bytes_written == -1) {
747         active = 0;
748         cb_ret = OGGZ_ERR_SYSTEM; /* XXX: catch next */
749       } else if (bytes_written == 0) {
750         if (writer->no_more_packets) {
751           active = 0;
752           break;
753         } else if (!oggz_page_init (oggz)) {
754 #ifdef DEBUG
755           printf ("oggz_write_output: bytes_written == 0, DONE\n");
756 #endif
757           writer->state = OGGZ_MAKING_PACKETS;
758         }
759       }
760 
761       buf += bytes_written;
762 
763       remaining -= bytes_written;
764       nwritten += bytes_written;
765     }
766   }
767 
768 #ifdef DEBUG
769   printf ("oggz_write_output: OUT %ld\n", nwritten);
770 #endif
771 
772   writer->writing = 0;
773 
774   if (nwritten == 0) {
775     if (cb_ret == OGGZ_WRITE_EMPTY) cb_ret = 0;
776     return oggz_map_return_value_to_error (cb_ret);
777   } else {
778     oggz->cb_next = cb_ret;
779   }
780 
781   return nwritten;
782 }
783 
784 long
oggz_write(OGGZ * oggz,long n)785 oggz_write (OGGZ * oggz, long n)
786 {
787   OggzWriter * writer;
788   long bytes, bytes_written = 1, remaining = n, nwritten = 0;
789   int active = 1, cb_ret = 0;
790 
791   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
792 
793   writer = &oggz->x.writer;
794 
795   if (!(oggz->flags & OGGZ_WRITE)) {
796     return OGGZ_ERR_INVALID;
797   }
798 
799   if (writer->writing) return OGGZ_ERR_RECURSIVE_WRITE;
800   writer->writing = 1;
801 
802 #ifdef DEBUG
803   printf ("oggz_write: IN\n");
804 #endif
805 
806   if ((cb_ret = oggz->cb_next) != OGGZ_CONTINUE) {
807     oggz->cb_next = 0;
808     writer->writing = 0;
809     writer->no_more_packets = 0;
810     if (cb_ret == OGGZ_WRITE_EMPTY) cb_ret = 0;
811     return oggz_map_return_value_to_error (cb_ret);
812   }
813 
814   while (active && remaining > 0) {
815     bytes = MIN (remaining, 1024);
816 
817 #ifdef DEBUG
818     printf ("oggz_write: write loop (%ld , %ld remain) ...\n", bytes,
819 	    remaining);
820 #endif
821 
822     while (writer->state == OGGZ_MAKING_PACKETS) {
823 #ifdef DEBUG
824       printf ("oggz_write: MAKING PACKETS\n");
825 #endif
826       if ((cb_ret = oggz_writer_make_packet (oggz)) != OGGZ_CONTINUE) {
827 #ifdef DEBUG
828         printf ("oggz_write: no packets (cb_ret is %d)\n", cb_ret);
829 #endif
830         /*
831          * if we're out of packets because we're at the end of the file,
832          * we can't finish just yet.  Instead we need to force a page flush,
833          * and write the page out.  So we set flushing and no_more_packets to
834          * 1.  This causes oggz_page_init to flush the page, then we
835          * will switch the state to OGGZ_WRITING_PAGES, which will trigger
836          * the writing code below.
837          */
838         if (cb_ret == OGGZ_WRITE_EMPTY) {
839 #ifdef DEBUG
840           printf ("oggz_write: Inferred end of data, forcing a page flush.\n");
841 #endif
842           writer->flushing = 1;
843           writer->no_more_packets = 1;
844           cb_ret = OGGZ_CONTINUE;
845         } else {
846 #ifdef DEBUG
847           printf ("oggz_write: Stopped by user callback.\n");
848 #endif
849           active = 0;
850           break;
851         }
852       }
853       if (oggz_page_init (oggz)) {
854         writer->state = OGGZ_WRITING_PAGES;
855       } else {
856 #ifdef DEBUG
857         printf ("oggz_write: unable to make page...\n");
858 #endif
859         if (writer->no_more_packets) {
860           active = 0;
861           break;
862         }
863       }
864     }
865 
866     if (writer->state == OGGZ_WRITING_PAGES) {
867       bytes_written = oggz_page_writeout (oggz, bytes);
868 #ifdef DEBUG
869       printf ("oggz_write: MAKING PAGES; wrote %ld bytes\n", bytes_written);
870 #endif
871 
872       if (bytes_written == -1) {
873         active = 0;
874         return OGGZ_ERR_SYSTEM; /* XXX: catch next */
875       } else if (bytes_written == 0) {
876         /*
877          * OK so we've completely written the current page.  If no_more_packets
878          * is set then that means there's no more pages after this one, so
879          * we set active to 0, break out of the loop, pack up our things and
880          * go home.
881          */
882         if (writer->no_more_packets) {
883           active = 0;
884           break;
885         } else if (!oggz_page_init (oggz)) {
886 #ifdef DEBUG
887           printf ("oggz_write: bytes_written == 0, DONE\n");
888 #endif
889           writer->state = OGGZ_MAKING_PACKETS;
890         }
891       }
892 
893       remaining -= bytes_written;
894       nwritten += bytes_written;
895     }
896   }
897 
898 #ifdef DEBUG
899   printf ("oggz_write: OUT %ld\n", nwritten);
900 #endif
901 
902   writer->writing = 0;
903 
904   if (nwritten == 0) {
905     if (cb_ret == OGGZ_WRITE_EMPTY) cb_ret = 0;
906     return oggz_map_return_value_to_error (cb_ret);
907   } else {
908     oggz->cb_next = cb_ret;
909   }
910 
911   return nwritten;
912 }
913 
914 long
oggz_write_get_next_page_size(OGGZ * oggz)915 oggz_write_get_next_page_size (OGGZ * oggz)
916 {
917   OggzWriter * writer;
918   ogg_page * og;
919 
920   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
921 
922   writer = &oggz->x.writer;
923 
924   if (!(oggz->flags & OGGZ_WRITE)) {
925     return OGGZ_ERR_INVALID;
926   }
927 
928   og = &oggz->current_page;
929 
930   return (og->header_len + og->body_len - (long)writer->page_offset);
931 }
932 
933 #else /* OGGZ_CONFIG_WRITE */
934 
935 #include <ogg/ogg.h>
936 #include "oggz_private.h"
937 
938 OGGZ *
oggz_write_init(OGGZ * oggz)939 oggz_write_init (OGGZ * oggz)
940 {
941   return NULL;
942 }
943 
944 int
oggz_write_flush(OGGZ * oggz)945 oggz_write_flush (OGGZ * oggz)
946 {
947   return OGGZ_ERR_DISABLED;
948 }
949 
950 OGGZ *
oggz_write_close(OGGZ * oggz)951 oggz_write_close (OGGZ * oggz)
952 {
953   return NULL;
954 }
955 
956 int
oggz_write_set_hungry_callback(OGGZ * oggz,OggzWriteHungry hungry,int only_when_empty,void * user_data)957 oggz_write_set_hungry_callback (OGGZ * oggz, OggzWriteHungry hungry,
958 				int only_when_empty, void * user_data)
959 {
960   return OGGZ_ERR_DISABLED;
961 }
962 
963 int
oggz_write_feed(OGGZ * oggz,ogg_packet * op,long serialno,int flush,int * guard)964 oggz_write_feed (OGGZ * oggz, ogg_packet * op, long serialno, int flush,
965 		 int * guard)
966 {
967   return OGGZ_ERR_DISABLED;
968 }
969 
970 long
oggz_write_output(OGGZ * oggz,unsigned char * buf,long n)971 oggz_write_output (OGGZ * oggz, unsigned char * buf, long n)
972 {
973   return OGGZ_ERR_DISABLED;
974 }
975 
976 long
oggz_write(OGGZ * oggz,long n)977 oggz_write (OGGZ * oggz, long n)
978 {
979   return OGGZ_ERR_DISABLED;
980 }
981 
982 long
oggz_write_get_next_page_size(OGGZ * oggz)983 oggz_write_get_next_page_size (OGGZ * oggz)
984 {
985   return OGGZ_ERR_DISABLED;
986 }
987 
988 #endif
989