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