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