root/liboggz/trunk/src/liboggz/oggz_write.c

Revision 3788, 23.3 kB (checked in by giles, 2 weeks ago)

Clean up some trailing whitespace.

Line 
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
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 *
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
95 #ifdef ZPACKET_CMP
96   /* XXX: comparison function should only kick in when a metric is set */
97   oggz_vector_set_cmp (writer->packet_queue,
98                        (OggzCmpFunc)oggz_zpacket_cmp, oggz);
99 #endif
100
101   writer->hungry = NULL;
102   writer->hungry_user_data = NULL;
103   writer->hungry_only_when_empty = 0;
104
105   writer->writing = 0;
106   writer->no_more_packets = 0;
107   writer->state = OGGZ_MAKING_PACKETS;
108
109   writer->flushing = 0;
110 #if 0
111   writer->eog = 1;
112   writer->eop = 1; /* init ready to start next packet */
113 #endif
114   writer->eos = 0;
115
116   writer->current_zpacket = NULL;
117
118   writer->packet_offset = 0;
119   writer->page_offset = 0;
120
121   writer->current_stream = NULL;
122
123   return oggz;
124 }
125
126 static int
127 oggz_writer_packet_free (oggz_writer_packet_t * zpacket)
128 {
129   if (!zpacket) return 0;
130
131   if (zpacket->guard) {
132     /* managed by user; flag guard */
133     *zpacket->guard = 1;
134   } else {
135     /* managed by oggz; free copied data */
136     oggz_free (zpacket->op.packet);
137   }
138   oggz_free (zpacket);
139
140   return 0;
141 }
142
143 int
144 oggz_write_flush (OGGZ * oggz)
145 {
146   OggzWriter * writer = &oggz->x.writer;
147   ogg_stream_state * os;
148   ogg_page * og;
149   int ret = 0;
150
151   os = writer->current_stream;
152   og = &oggz->current_page;
153
154   if (os != NULL)
155     ret = ogg_stream_flush (os, og);
156
157   return ret;
158 }
159
160 OGGZ *
161 oggz_write_close (OGGZ * oggz)
162 {
163   OggzWriter * writer = &oggz->x.writer;
164
165   oggz_write_flush (oggz);
166
167   oggz_writer_packet_free (writer->current_zpacket);
168   oggz_writer_packet_free (writer->next_zpacket);
169
170   oggz_vector_foreach (writer->packet_queue,
171                        (OggzFunc)oggz_writer_packet_free);
172   oggz_vector_delete (writer->packet_queue);
173
174   return oggz;
175 }
176
177 /******** Packet queueing ********/
178
179 int
180 oggz_write_set_hungry_callback (OGGZ * oggz, OggzWriteHungry hungry,
181                                 int only_when_empty, void * user_data)
182 {
183   OggzWriter * writer;
184
185   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
186
187   if (!(oggz->flags & OGGZ_WRITE)) {
188     return OGGZ_ERR_INVALID;
189   }
190
191   writer = &oggz->x.writer;
192
193   writer->hungry = hungry;
194   writer->hungry_user_data = user_data;
195   writer->hungry_only_when_empty = only_when_empty;
196
197   return 0;
198 }
199
200 int
201 oggz_write_feed (OGGZ * oggz, ogg_packet * op, long serialno, int flush,
202                  int * guard)
203 {
204   OggzWriter * writer;
205   oggz_stream_t * stream;
206   oggz_writer_packet_t * packet;
207   ogg_packet * new_op;
208   unsigned char * new_buf;
209   int b_o_s, e_o_s, bos_auto;
210   int strict, prefix, suffix;
211
212 #ifdef DEBUG
213   printf ("oggz_write_feed: IN\n");
214 #endif
215
216   if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
217
218   if (!(oggz->flags & OGGZ_WRITE)) {
219     return OGGZ_ERR_INVALID;
220   }
221
222   writer = &oggz->x.writer;
223
224   if (guard && *guard != 0) return OGGZ_ERR_BAD_GUARD;
225
226   /* Check that the serialno is in the valid range for an Ogg page header,
227    * ie. that it fits within 32 bits and does not equal the special value -1 */
228   if ((long)((ogg_int32_t)serialno) != serialno || serialno == -1) {
229 #ifdef DEBUG
230     printf ("oggz_write_feed: serialno %010ld\n", serialno);
231 #endif
232     return OGGZ_ERR_BAD_SERIALNO;
233   }
234
235 #ifdef DEBUG
236   printf ("oggz_write_feed: (%010ld) FLUSH: %d\n", serialno, flush);
237 #endif
238
239   /* Cache strict, prefix, suffix */
240   strict = !(oggz->flags & OGGZ_NONSTRICT);
241   prefix = (oggz->flags & OGGZ_PREFIX);
242   suffix = (oggz->flags & OGGZ_SUFFIX);
243
244   /* Set bos, eos to canonical values */
245   bos_auto = (op->b_o_s == -1) ? 1 : 0;
246   b_o_s = op->b_o_s ? 1 : 0;
247   e_o_s = op->e_o_s ? 1 : 0;
248
249   stream = oggz_get_stream (oggz, serialno);
250   if (stream == NULL) {
251     if (bos_auto) b_o_s = 1;
252
253     if (strict && b_o_s && !oggz_get_bos (oggz, -1)) {
254         return OGGZ_ERR_BOS;
255     }
256
257     if (b_o_s || !strict || suffix) {
258       stream = oggz_add_stream (oggz, serialno);
259       oggz_auto_identify_packet (oggz, op, serialno);
260     } else {
261       return OGGZ_ERR_BAD_SERIALNO;
262     }
263   } else {
264     if (bos_auto) b_o_s = 0;
265
266     if (!suffix && strict && stream->e_o_s)
267       return OGGZ_ERR_EOS;
268   }
269
270   /* Check packet against mapping restrictions */
271   if (strict) {
272     if (op->bytes < 0) return OGGZ_ERR_BAD_BYTES;
273     if (!suffix && b_o_s != stream->b_o_s) return OGGZ_ERR_BAD_B_O_S;
274     if (op->granulepos != -1 && op->granulepos < stream->granulepos &&
275         /* Allow negative granulepos immediately after headers, for Dirac: */
276         !(stream->granulepos == 0 && op->granulepos < 0))
277       return OGGZ_ERR_BAD_GRANULEPOS;
278
279     /* Allow packetno == -1 to indicate oggz should fill it in; otherwise:
280      * if op is the first packet in the stream, initialize the stream's
281      * packetno to the given one, otherwise check it for strictness.
282      * For stream suffixes, believe the packetno value */
283     if (op->packetno != -1) {
284       if (b_o_s || suffix) {
285         stream->packetno = op->packetno;
286       } else if (op->packetno <= stream->packetno) {
287         return OGGZ_ERR_BAD_PACKETNO;
288       }
289     }
290   }
291
292   /* OK -- Update stream's memory of packet details */
293
294   if (!stream->metric && (oggz->flags & OGGZ_AUTO)) {
295     oggz_auto_read_bos_packet (oggz, op, serialno, NULL);
296   }
297
298   stream->b_o_s = 0; /* The stream is henceforth no longer at bos */
299   stream->e_o_s = e_o_s; /* We believe the eos value */
300   stream->granulepos = op->granulepos; /* and the granulepos */
301
302   /* If the user specified a packetno of -1, fill it in automatically;
303    * otherwise, use the user-specified value */
304   stream->packetno = (op->packetno != -1) ? op->packetno : stream->packetno+1;
305
306   /* Now set up the packet and add it to the queue */
307   if (guard == NULL) {
308     new_buf = oggz_malloc ((size_t)op->bytes);
309     memcpy (new_buf, op->packet, (size_t)op->bytes);
310   } else {
311     new_buf = op->packet;
312   }
313
314   packet = oggz_malloc (sizeof (oggz_writer_packet_t));
315
316   new_op = &packet->op;
317   new_op->packet = new_buf;
318   new_op->bytes = op->bytes;
319   new_op->b_o_s = b_o_s;
320   new_op->e_o_s = e_o_s;
321   new_op->granulepos = op->granulepos;
322   new_op->packetno = stream->packetno;
323
324   packet->stream = stream;
325   packet->flush = flush;
326   packet->guard = guard;
327
328 #ifdef DEBUG
329   printf ("oggz_write_feed: made packet bos %ld eos %ld (%ld bytes) FLUSH: %d\n",
330           new_op->b_o_s, new_op->e_o_s, new_op->bytes, packet->flush);
331 #endif
332
333   if (oggz_vector_insert_p (writer->packet_queue, packet) == NULL) {
334     oggz_free (packet);
335     if (!guard) oggz_free (new_buf);
336     return -1;
337   }
338
339   writer->no_more_packets = 0;
340
341 #ifdef DEBUG
342   printf ("oggz_write_feed: enqueued packet, queue size %d\n",
343           oggz_vector_size (writer->packet_queue));
344 #endif
345
346   return 0;
347 }
348
349 /******** Page creation ********/
350
351 /*
352  *
353
354             ____           __________________\___           ____
355            /    \         /    Page ready    /   \         /    \
356           |      \ /=========\                /=========\ /      |
357   Page    |       || Make    ||              ||  Write  ||       | Page
358   !ready  V       || packet  ||              ||  page   ||       V ready
359           |      / \=========/                \=========/ \      |
360            \____/         \___/__________________/         \____/
361                               \   Page !ready
362  *
363  */
364
365
366 /*
367  * oggz_page_init (oggz)
368  *
369  * Initialises the next page of the current packet.
370  *
371  * If this returns 0, the page is not ready for writing.
372  */
373 static long
374 oggz_page_init (OGGZ * oggz)
375 {
376   OggzWriter * writer;
377   ogg_stream_state * os;
378   ogg_page * og;
379   int ret;
380
381   if (oggz == NULL) return -1;
382
383   writer = &oggz->x.writer;
384   os = writer->current_stream;
385   og = &oggz->current_page;
386
387   if (ALWAYS_FLUSH || writer->flushing) {
388 #ifdef DEBUG
389     printf ("oggz_page_init: ATTEMPT FLUSH: ");
390 #endif
391     ret = oggz_write_flush (oggz);
392   } else {
393 #ifdef DEBUG
394     printf ("oggz_page_init: ATTEMPT pageout: ");
395 #endif
396     ret = ogg_stream_pageout (os, og);
397   }
398
399   if (ret) {
400     writer->page_offset = 0;
401   }
402
403 #ifdef DEBUG
404   printf ("%s\n", ret ? "OK" : "NO");
405 #endif
406
407   return ret;
408 }
409
410 /*
411  * oggz_packet_init (oggz, buf, n)
412  *
413  * Initialises the next packet with data from buf, length n
414  */
415 static long
416 oggz_packet_init (OGGZ * oggz, oggz_writer_packet_t * next_zpacket)
417 {
418   OggzWriter * writer;
419   oggz_stream_t * stream;
420   ogg_stream_state * os;
421   ogg_packet * op;
422
423   if (oggz == NULL) return -1L;
424
425   writer = &oggz->x.writer;
426   writer->current_zpacket = next_zpacket;
427   op = &next_zpacket->op;
428
429 #ifdef DEBUG
430   printf ("oggz_packet_init: %ld bytes\n", (long)op->bytes);
431 #endif
432
433   stream = next_zpacket->stream;
434
435   /* Mark this stream as having delivered a non b_o_s packet if so */
436   if (!op->b_o_s) stream->delivered_non_b_o_s = 1;
437
438   os = &stream->ogg_stream;
439   ogg_stream_packetin (os, op);
440
441   writer->flushing = (next_zpacket->flush & OGGZ_FLUSH_AFTER);
442 #ifdef DEBUG
443   printf ("oggz_packet_init: set flush to %d\n", writer->flushing);
444 #endif
445   writer->current_stream = os;
446   writer->packet_offset = 0;
447
448   return 0;
449 }
450
451 static long
452 oggz_page_copyout (OGGZ * oggz, unsigned char * buf, long n)
453 {
454   OggzWriter * writer;
455   long h, b;
456   ogg_page * og;
457
458   if (oggz == NULL) return -1L;
459
460   writer = &oggz->x.writer;
461   og = &oggz->current_page;
462
463   h = MIN (n, og->header_len - writer->page_offset);
464   if (h > 0) {
465     memcpy (buf, og->header + writer->page_offset, h);
466     writer->page_offset += h;
467     n -= h;
468     buf += h;
469   } else {
470     h = 0;
471   }
472
473   b = MIN (n, og->header_len + og->body_len - writer->page_offset);
474   if (b > 0) {
475 #ifdef DEBUG
476     {
477       unsigned char * c = &og->body[writer->page_offset - og->header_len];
478       printf ("oggz_page_copyout [%d] (@%ld): %c%c%c%c ...\n",
479               ogg_page_serialno (og), (long) ogg_page_granulepos (og),
480               c[0], c[1], c[2], c[3]);
481     }
482 #endif
483     memcpy (buf, og->body + (writer->page_offset - og->header_len), b);
484     writer->page_offset += b;
485     n -= b;
486     buf += b;
487   } else {
488     b = 0;
489   }
490
491   return h + b;
492 }
493
494 static long
495 oggz_page_writeout (OGGZ * oggz, long n)
496 {
497   OggzWriter * writer;
498   long h, b, nwritten;
499   ogg_page * og;
500
501 #ifdef OGGZ_WRITE_DIRECT
502   int fd;
503 #endif
504
505   if (oggz == NULL) return -1L;
506
507   writer = &oggz->x.writer;
508   og = &oggz->current_page;
509
510 #ifdef OGGZ_WRITE_DIRECT
511   fd = fileno (oggz->file);
512 #endif
513
514   h = MIN (n, og->header_len - writer->page_offset);
515   if (h > 0) {
516 #ifdef OGGZ_WRITE_DIRECT
517     nwritten = write (fd, og->header + writer->page_offset, h);
518 #else
519     nwritten = (long)oggz_io_write (oggz, og->header + writer->page_offset, h);
520 #endif
521
522 #ifdef DEBUG
523     if (nwritten < h) {
524       printf ("oggz_page_writeout: %ld < %ld\n", nwritten, h);
525     }
526 #endif
527     writer->page_offset += h;
528     n -= h;
529   } else {
530     h = 0;
531   }
532
533   b = MIN (n, og->header_len + og->body_len - writer->page_offset);
534   if (b > 0) {
535 #ifdef DEBUG
536     {
537       unsigned char * c = &og->body[writer->page_offset - og->header_len];
538       printf ("oggz_page_writeout [%d] (@%ld): %c%c%c%c ...\n",
539               ogg_page_serialno (og), (long) ogg_page_granulepos (og),
540               c[0], c[1], c[2], c[3]);
541     }
542 #endif
543 #ifdef OGGZ_WRITE_DIRECT
544     nwritten = write (fd,
545                       og->body + (writer->page_offset - og->header_len), b);
546 #else
547     nwritten = (long)oggz_io_write (oggz, og->body + (writer->page_offset - og->header_len), b);
548 #endif
549 #ifdef DEBUG
550     if (nwritten < b) {
551       printf ("oggz_page_writeout: %ld < %ld\n", nwritten, b);
552     }
553 #endif
554     writer->page_offset += b;
555     n -= b;
556   } else {
557     b = 0;
558   }
559
560   return h + b;
561 }
562
563 static int
564 oggz_dequeue_packet (OGGZ * oggz, oggz_writer_packet_t ** next_zpacket)
565 {
566   OggzWriter * writer = &oggz->x.writer;
567   int ret = 0;
568
569   if (writer->next_zpacket != NULL) {
570 #ifdef DEBUG
571     printf ("oggz_dequeue_packet: queue EMPTY\n");
572 #endif
573     *next_zpacket = writer->next_zpacket;
574     writer->next_zpacket = NULL;
575   } else {
576     *next_zpacket = oggz_vector_pop (writer->packet_queue);
577
578     if (*next_zpacket == NULL) {
579       if (writer