1 /* Icecast
2 *
3 * This program is distributed under the GNU General Public License, version 2.
4 * A copy of this license is included with this source.
5 *
6 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 * Michael Smith <msmith@xiph.org>,
8 * oddsock <oddsock@xiph.org>,
9 * Karl Heyes <karl@xiph.org>
10 * and others (see AUTHORS for details).
11 */
12
13 /* format_ogg.c
14 *
15 * format plugin for Ogg
16 *
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <ogg/ogg.h>
28
29 #include "refbuf.h"
30 #include "source.h"
31 #include "client.h"
32
33 #include "stats.h"
34 #include "format.h"
35 #include "format_ogg.h"
36 #include "format_vorbis.h"
37 #ifdef HAVE_THEORA
38 #include "format_theora.h"
39 #endif
40 #ifdef HAVE_SPEEX
41 #include "format_speex.h"
42 #endif
43 #include "format_opus.h"
44 #include "format_midi.h"
45 #include "format_flac.h"
46 #include "format_kate.h"
47 #include "format_skeleton.h"
48
49 #ifdef _WIN32
50 #define snprintf _snprintf
51 #endif
52
53 #define CATMODULE "format-ogg"
54 #include "logging.h"
55
56 struct _ogg_state_tag;
57
58 static void format_ogg_free_plugin (format_plugin_t *plugin);
59 static int create_ogg_client_data(source_t *source, client_t *client);
60 static void free_ogg_client_data (client_t *client);
61
62 static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf);
63 static refbuf_t *ogg_get_buffer (source_t *source);
64 static int write_buf_to_client (client_t *client);
65
66
67 struct ogg_client
68 {
69 refbuf_t *headers;
70 refbuf_t *header_page;
71 unsigned pos;
72 int headers_sent;
73 };
74
75
make_refbuf_with_page(ogg_page * page)76 refbuf_t *make_refbuf_with_page (ogg_page *page)
77 {
78 refbuf_t *refbuf = refbuf_new (page->header_len + page->body_len);
79
80 memcpy (refbuf->data, page->header, page->header_len);
81 memcpy (refbuf->data+page->header_len, page->body, page->body_len);
82 return refbuf;
83 }
84
85
86 /* routine for taking the provided page (should be a header page) and
87 * placing it on the collection of header pages
88 */
format_ogg_attach_header(ogg_state_t * ogg_info,ogg_page * page)89 void format_ogg_attach_header (ogg_state_t *ogg_info, ogg_page *page)
90 {
91 refbuf_t *refbuf = make_refbuf_with_page (page);
92
93 if (ogg_page_bos (page))
94 {
95 ICECAST_LOG_DEBUG("attaching BOS page");
96 if (*ogg_info->bos_end == NULL)
97 ogg_info->header_pages_tail = refbuf;
98 refbuf->next = *ogg_info->bos_end;
99 *ogg_info->bos_end = refbuf;
100 ogg_info->bos_end = &refbuf->next;
101 return;
102 }
103 ICECAST_LOG_DEBUG("attaching header page");
104 if (ogg_info->header_pages_tail)
105 ogg_info->header_pages_tail->next = refbuf;
106 ogg_info->header_pages_tail = refbuf;
107
108 if (ogg_info->header_pages == NULL)
109 ogg_info->header_pages = refbuf;
110 }
111
112
format_ogg_free_headers(ogg_state_t * ogg_info)113 void format_ogg_free_headers (ogg_state_t *ogg_info)
114 {
115 refbuf_t *header;
116
117 /* release the header pages first */
118 ICECAST_LOG_DEBUG("releasing header pages");
119 header = ogg_info->header_pages;
120 while (header)
121 {
122 refbuf_t *to_release = header;
123 header = header->next;
124 refbuf_release (to_release);
125 }
126 ogg_info->header_pages = NULL;
127 ogg_info->header_pages_tail = NULL;
128 ogg_info->bos_end = &ogg_info->header_pages;
129 }
130
131
132 /* release the memory used for the codec and header pages from the module */
free_ogg_codecs(ogg_state_t * ogg_info)133 static void free_ogg_codecs (ogg_state_t *ogg_info)
134 {
135 ogg_codec_t *codec;
136
137 if (ogg_info == NULL)
138 return;
139
140 format_ogg_free_headers (ogg_info);
141
142 /* now free the codecs */
143 codec = ogg_info->codecs;
144 ICECAST_LOG_DEBUG("freeing codecs");
145 while (codec)
146 {
147 ogg_codec_t *next = codec->next;
148 if (codec->possible_start)
149 refbuf_release (codec->possible_start);
150 codec->codec_free (ogg_info, codec);
151 codec = next;
152 }
153 ogg_info->codecs = NULL;
154 ogg_info->current = NULL;
155 ogg_info->bos_completed = 0;
156 ogg_info->codec_count = 0;
157 }
158
159
format_ogg_get_plugin(source_t * source)160 int format_ogg_get_plugin (source_t *source)
161 {
162 format_plugin_t *plugin;
163 ogg_state_t *state = calloc (1, sizeof (ogg_state_t));
164
165 plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
166
167 plugin->type = FORMAT_TYPE_OGG;
168 plugin->get_buffer = ogg_get_buffer;
169 plugin->write_buf_to_client = write_buf_to_client;
170 plugin->write_buf_to_file = write_ogg_to_file;
171 plugin->create_client_data = create_ogg_client_data;
172 plugin->free_plugin = format_ogg_free_plugin;
173 plugin->set_tag = NULL;
174 if (strcmp (httpp_getvar (source->parser, "content-type"), "application/x-ogg") == 0)
175 httpp_setvar (source->parser, "content-type", "application/ogg");
176 plugin->contenttype = httpp_getvar (source->parser, "content-type");
177
178 ogg_sync_init (&state->oy);
179
180 plugin->_state = state;
181 source->format = plugin;
182 state->mount = source->mount;
183 state->bos_end = &state->header_pages;
184
185 return 0;
186 }
187
188
format_ogg_free_plugin(format_plugin_t * plugin)189 static void format_ogg_free_plugin (format_plugin_t *plugin)
190 {
191 ogg_state_t *state = plugin->_state;
192
193 /* free memory associated with this plugin instance */
194 free_ogg_codecs (state);
195 free (state->artist);
196 free (state->title);
197
198 ogg_sync_clear (&state->oy);
199 free (state);
200
201 free (plugin);
202 }
203
204
205 /* a new BOS page has been seen so check which codec it is */
process_initial_page(format_plugin_t * plugin,ogg_page * page)206 static int process_initial_page (format_plugin_t *plugin, ogg_page *page)
207 {
208 ogg_state_t *ogg_info = plugin->_state;
209 ogg_codec_t *codec;
210
211 if (ogg_info->bos_completed)
212 {
213 ogg_info->bitrate = 0;
214 ogg_info->codec_sync = NULL;
215 /* need to zap old list of codecs when next group of BOS pages appear */
216 free_ogg_codecs (ogg_info);
217 }
218 do
219 {
220 if (ogg_info->codec_count > 10)
221 {
222 ICECAST_LOG_ERROR("many codecs in stream, playing safe, dropping source");
223 ogg_info->error = 1;
224 return -1;
225 }
226 codec = initial_vorbis_page (plugin, page);
227 if (codec)
228 break;
229 #ifdef HAVE_THEORA
230 codec = initial_theora_page (plugin, page);
231 if (codec)
232 break;
233 #endif
234 codec = initial_midi_page (plugin, page);
235 if (codec)
236 break;
237 codec = initial_flac_page (plugin, page);
238 if (codec)
239 break;
240 #ifdef HAVE_SPEEX
241 codec = initial_speex_page (plugin, page);
242 if (codec)
243 break;
244 #endif
245 codec = initial_kate_page (plugin, page);
246 if (codec)
247 break;
248 codec = initial_skeleton_page (plugin, page);
249 if (codec)
250 break;
251 codec = initial_opus_page (plugin, page);
252 if (codec)
253 break;
254
255 /* any others */
256 ICECAST_LOG_ERROR("Seen BOS page with unknown type");
257 ogg_info->error = 1;
258 return -1;
259 } while (0);
260
261 if (codec)
262 {
263 /* add codec to list */
264 codec->next = ogg_info->codecs;
265 ogg_info->codecs = codec;
266 ogg_info->codec_count++;
267 }
268
269 return 0;
270 }
271
272
273 /* This is called when there has been a change in the metadata. Usually
274 * artist and title are provided separately so here we update the stats
275 * and write log entry if required.
276 */
update_comments(source_t * source)277 static void update_comments (source_t *source)
278 {
279 ogg_state_t *ogg_info = source->format->_state;
280 char *title = ogg_info->title;
281 char *artist = ogg_info->artist;
282 char *metadata = NULL;
283 unsigned int len = 1; /* space for the nul byte at least */
284 ogg_codec_t *codec;
285 char codec_names [100] = "";
286
287 if (ogg_info->artist)
288 {
289 if (title)
290 {
291 len += strlen(artist) + strlen(title) + 3;
292 metadata = calloc (1, len);
293 snprintf (metadata, len, "%s - %s", artist, title);
294 }
295 else
296 {
297 len += strlen(artist);
298 metadata = calloc (1, len);
299 snprintf (metadata, len, "%s", artist);
300 }
301 }
302 else
303 {
304 if (title)
305 {
306 len += strlen (title);
307 metadata = calloc (1, len);
308 snprintf (metadata, len, "%s", title);
309 }
310 }
311 if (metadata)
312 {
313 logging_playlist (source->mount, metadata, source->listeners);
314 free (metadata);
315 }
316 stats_event (source->mount, "artist", artist);
317 stats_event (source->mount, "title", title);
318
319 codec = ogg_info->codecs;
320 while (codec)
321 {
322 if (codec->name)
323 {
324 int len = strlen (codec_names);
325 int remaining = sizeof (codec_names) - len;
326 char *where = codec_names + len;
327 char *separator = "/";
328 if (len == 0)
329 separator = "";
330 snprintf (where, remaining, "%s%s", separator, codec->name);
331 }
332 codec = codec->next;
333 }
334 stats_event (source->mount, "subtype", codec_names);
335 yp_touch (source->mount);
336 }
337
338
339 /* called when preparing a refbuf with audio data to be passed
340 * back for queueing
341 */
complete_buffer(source_t * source,refbuf_t * refbuf)342 static refbuf_t *complete_buffer (source_t *source, refbuf_t *refbuf)
343 {
344 ogg_state_t *ogg_info = source->format->_state;
345 refbuf_t *header = ogg_info->header_pages;
346
347 while (header)
348 {
349 refbuf_addref (header);
350 header = header->next;
351 }
352 refbuf->associated = ogg_info->header_pages;
353
354 if (ogg_info->log_metadata)
355 {
356 update_comments (source);
357 ogg_info->log_metadata = 0;
358 }
359 /* listeners can start anywhere unless the codecs themselves are
360 * marking starting points */
361 if (ogg_info->codec_sync == NULL)
362 refbuf->sync_point = 1;
363 return refbuf;
364 }
365
366
367 /* process the incoming page. this requires searching through the
368 * currently known codecs that have been seen in the stream
369 */
process_ogg_page(ogg_state_t * ogg_info,ogg_page * page)370 static refbuf_t *process_ogg_page (ogg_state_t *ogg_info, ogg_page *page)
371 {
372 ogg_codec_t *codec = ogg_info->codecs;
373 refbuf_t *refbuf = NULL;
374
375 while (codec)
376 {
377 if (ogg_page_serialno (page) == codec->os.serialno)
378 {
379 if (codec->process_page)
380 refbuf = codec->process_page (ogg_info, codec, page);
381 break;
382 }
383
384 codec = codec->next;
385 }
386 ogg_info->current = codec;
387 return refbuf;
388 }
389
390
391 /* main plugin handler for getting a buffer for the queue. In here we
392 * just add an incoming page to the codecs and process it until either
393 * more data is needed or we prodice a buffer for the queue.
394 */
ogg_get_buffer(source_t * source)395 static refbuf_t *ogg_get_buffer (source_t *source)
396 {
397 ogg_state_t *ogg_info = source->format->_state;
398 format_plugin_t *format = source->format;
399 char *data = NULL;
400 int bytes = 0;
401
402 while (1)
403 {
404 while (1)
405 {
406 ogg_page page;
407 refbuf_t *refbuf = NULL;
408 ogg_codec_t *codec = ogg_info->current;
409
410 /* if a codec has just been given a page then process it */
411 if (codec && codec->process)
412 {
413 refbuf = codec->process (ogg_info, codec);
414 if (refbuf)
415 return complete_buffer (source, refbuf);
416
417 ogg_info->current = NULL;
418 }
419
420 if (ogg_sync_pageout (&ogg_info->oy, &page) > 0)
421 {
422 if (ogg_page_bos (&page))
423 {
424 process_initial_page (source->format, &page);
425 }
426 else
427 {
428 ogg_info->bos_completed = 1;
429 refbuf = process_ogg_page (ogg_info, &page);
430 }
431 if (ogg_info->error)
432 {
433 ICECAST_LOG_ERROR("Problem processing stream");
434 source->running = 0;
435 return NULL;
436 }
437 if (refbuf)
438 return complete_buffer (source, refbuf);
439 continue;
440 }
441 /* need more stream data */
442 break;
443 }
444 /* we need more data to continue getting pages */
445 data = ogg_sync_buffer (&ogg_info->oy, 4096);
446
447 bytes = client_read_bytes (source->client, data, 4096);
448 if (bytes <= 0)
449 {
450 ogg_sync_wrote (&ogg_info->oy, 0);
451 return NULL;
452 }
453 format->read_bytes += bytes;
454 ogg_sync_wrote (&ogg_info->oy, bytes);
455 }
456 }
457
458
create_ogg_client_data(source_t * source,client_t * client)459 static int create_ogg_client_data (source_t *source, client_t *client)
460 {
461 struct ogg_client *client_data = calloc (1, sizeof (struct ogg_client));
462 int ret = -1;
463
464 if (client_data)
465 {
466 client_data->headers_sent = 1;
467 client->format_data = client_data;
468 client->free_client_data = free_ogg_client_data;
469 ret = 0;
470 }
471 return ret;
472 }
473
474
free_ogg_client_data(client_t * client)475 static void free_ogg_client_data (client_t *client)
476 {
477 free (client->format_data);
478 client->format_data = NULL;
479 }
480
481
482 /* send out the header pages. These are for all codecs but are
483 * in the order for the stream, ie BOS pages first
484 */
send_ogg_headers(client_t * client,refbuf_t * headers)485 static int send_ogg_headers (client_t *client, refbuf_t *headers)
486 {
487 struct ogg_client *client_data = client->format_data;
488 refbuf_t *refbuf;
489 int written = 0;
490
491 if (client_data->headers_sent)
492 {
493 client_data->header_page = headers;
494 client_data->pos = 0;
495 client_data->headers_sent = 0;
496 }
497 refbuf = client_data->header_page;
498 while (refbuf)
499 {
500 char *data = refbuf->data + client_data->pos;
501 unsigned len = refbuf->len - client_data->pos;
502 int ret;
503
504 ret = client_send_bytes (client, data, len);
505 if (ret > 0)
506 written += ret;
507 if (ret < (int)len)
508 return written ? written : -1;
509 client_data->pos += ret;
510 if (client_data->pos == refbuf->len)
511 {
512 refbuf = refbuf->next;
513 client_data->header_page = refbuf;
514 client_data->pos = 0;
515 }
516 }
517 client_data->headers_sent = 1;
518 client_data->headers = headers;
519 return written;
520 }
521
522
523 /* main client write routine for sending ogg data. Each refbuf has a
524 * single page so we only need to determine if there are new headers
525 */
write_buf_to_client(client_t * client)526 static int write_buf_to_client (client_t *client)
527 {
528 refbuf_t *refbuf = client->refbuf;
529 char *buf = refbuf->data + client->pos;
530 unsigned len = refbuf->len - client->pos;
531 struct ogg_client *client_data = client->format_data;
532 int ret, written = 0;
533
534 do
535 {
536 if (client_data->headers != refbuf->associated)
537 {
538 ret = send_ogg_headers (client, refbuf->associated);
539 if (client_data->headers_sent == 0)
540 break;
541 written += ret;
542 }
543 ret = client_send_bytes (client, buf, len);
544
545 if (ret > 0)
546 client->pos += ret;
547
548 if (ret < (int)len)
549 break;
550 written += ret;
551 /* we have now written the page(s) */
552 ret = 0;
553 } while (0);
554
555 if (ret > 0)
556 written += ret;
557 return written;
558 }
559
560
write_ogg_data(struct source_tag * source,refbuf_t * refbuf)561 static int write_ogg_data (struct source_tag *source, refbuf_t *refbuf)
562 {
563 int ret = 1;
564
565 if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len)
566 {
567 ICECAST_LOG_WARN("Write to dump file failed, disabling");
568 fclose (source->dumpfile);
569 source->dumpfile = NULL;
570 ret = 0;
571 }
572 return ret;
573 }
574
575
write_ogg_to_file(struct source_tag * source,refbuf_t * refbuf)576 static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf)
577 {
578 ogg_state_t *ogg_info = source->format->_state;
579
580 if (ogg_info->file_headers != refbuf->associated)
581 {
582 refbuf_t *header = refbuf->associated;
583 while (header)
584 {
585 if (write_ogg_data (source, header) == 0)
586 return;
587 header = header->next;
588 }
589 ogg_info->file_headers = refbuf->associated;
590 }
591 write_ogg_data (source, refbuf);
592 }
593
594
595