1 /* -*- c-basic-offset: 8; -*- */
2 /* ogg.c: Generic ogg data handler
3  * $Id$
4  *
5  *  Copyright (C) 2002-2004 the Icecast team <team@icecast.org>
6  *  Copyright (C) 2015-2019 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Library General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Library General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Library General Public
19  *  License along with this library; if not, write to the Free
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #   include <config.h>
25 #endif
26 
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #ifdef HAVE_INTTYPES_H
31 #   include <inttypes.h>
32 #endif
33 
34 #include <ogg/ogg.h>
35 
36 #include <shout/shout.h>
37 #include "shout_private.h"
38 #include "format_ogg.h"
39 
40 /* -- local datatypes -- */
41 typedef struct {
42     ogg_sync_state  oy;
43     ogg_codec_t    *codecs;
44     char            bos;
45 } ogg_data_t;
46 
47 /* -- static prototypes -- */
48 static int  send_ogg(shout_t *self, const unsigned char *data, size_t len);
49 static void close_ogg(shout_t *self);
50 static int  open_codec(ogg_codec_t *codec, ogg_page *page);
51 static void free_codec(ogg_codec_t *codec);
52 static void free_codecs(ogg_data_t *ogg_data);
53 static int  send_page(shout_t *self, ogg_page *page);
54 
55 typedef int (*codec_open_t)(ogg_codec_t *codec, ogg_page *page);
56 
57 static codec_open_t codecs[] = {
58     _shout_open_vorbis,
59 #ifdef HAVE_THEORA
60     _shout_open_theora,
61 #endif
62     _shout_open_opus,
63 #ifdef HAVE_SPEEX
64     _shout_open_speex,
65 #endif
66     NULL
67 };
68 
shout_open_ogg(shout_t * self)69 int shout_open_ogg(shout_t *self)
70 {
71     ogg_data_t *ogg_data;
72 
73     if (!(ogg_data = (ogg_data_t *)calloc(1, sizeof(ogg_data_t)))) {
74         return self->error = SHOUTERR_MALLOC;
75     }
76     self->format_data = ogg_data;
77 
78     ogg_sync_init(&ogg_data->oy);
79     ogg_data->bos = 1;
80 
81     self->send  = send_ogg;
82     self->close = close_ogg;
83 
84     return SHOUTERR_SUCCESS;
85 }
86 
send_ogg(shout_t * self,const unsigned char * data,size_t len)87 static int send_ogg(shout_t *self, const unsigned char *data, size_t len)
88 {
89     ogg_data_t  *ogg_data = (ogg_data_t*)self->format_data;
90     ogg_codec_t *codec;
91     char        *buffer;
92     ogg_page     page;
93 
94     buffer = ogg_sync_buffer(&ogg_data->oy, len);
95     if (!buffer)
96         return self->error = SHOUTERR_INSANE;
97     memcpy(buffer, data, len);
98     ogg_sync_wrote(&ogg_data->oy, len);
99 
100     while (ogg_sync_pageout(&ogg_data->oy, &page) == 1) {
101         if (ogg_page_bos(&page)) {
102             if (!ogg_data->bos) {
103                 free_codecs(ogg_data);
104                 ogg_data->bos = 1;
105             }
106 
107             codec = calloc(1, sizeof(ogg_codec_t));
108             if (! codec) {
109                 return self->error = SHOUTERR_MALLOC;
110             }
111 
112             if ((self->error = open_codec(codec, &page)) != SHOUTERR_SUCCESS) {
113                 return self->error;
114             }
115 
116             codec->headers = 1;
117             codec->senttime = self->senttime;
118             codec->next = ogg_data->codecs;
119             ogg_data->codecs = codec;
120         } else {
121             ogg_data->bos = 0;
122 
123             codec = ogg_data->codecs;
124             while (codec) {
125                 if (ogg_page_serialno(&page) == codec->os.serialno) {
126                     if (codec->read_page) {
127                         ogg_stream_pagein(&codec->os, &page);
128                         codec->read_page(codec, &page);
129 
130                         if (self->senttime < codec->senttime) {
131                             self->senttime = codec->senttime;
132                         }
133                     }
134 
135                     break;
136                 }
137                 codec = codec->next;
138             }
139         }
140 
141         if ((self->error = send_page(self, &page)) != SHOUTERR_SUCCESS) {
142             return self->error;
143         }
144     }
145 
146     return self->error = SHOUTERR_SUCCESS;
147 }
148 
close_ogg(shout_t * self)149 static void close_ogg(shout_t *self)
150 {
151     ogg_data_t *ogg_data = (ogg_data_t*)self->format_data;
152     free_codecs(ogg_data);
153     ogg_sync_clear(&ogg_data->oy);
154     free(ogg_data);
155 }
156 
open_codec(ogg_codec_t * codec,ogg_page * page)157 static int open_codec(ogg_codec_t *codec, ogg_page *page)
158 {
159     codec_open_t    this_codec;
160     int             i = 0;
161 
162     while ((this_codec = codecs[i])) {
163         ogg_stream_init(&codec->os, ogg_page_serialno(page));
164         ogg_stream_pagein(&codec->os, page);
165 
166         if (this_codec(codec, page) == SHOUTERR_SUCCESS) {
167             return SHOUTERR_SUCCESS;
168         }
169 
170         ogg_stream_clear(&codec->os);
171         i++;
172     }
173 
174     /* if no handler is found, we currently just fall back to untimed send_raw */
175     return SHOUTERR_SUCCESS;
176 }
177 
free_codecs(ogg_data_t * ogg_data)178 static void free_codecs(ogg_data_t *ogg_data)
179 {
180     ogg_codec_t *codec, *next;
181 
182     if (ogg_data == NULL) {
183         return;
184     }
185 
186     codec = ogg_data->codecs;
187     while (codec) {
188         next = codec->next;
189         free_codec(codec);
190         codec = next;
191     }
192     ogg_data->codecs = NULL;
193 }
194 
free_codec(ogg_codec_t * codec)195 static void free_codec(ogg_codec_t *codec)
196 {
197     if (codec->free_data) {
198         codec->free_data(codec->codec_data);
199     }
200     ogg_stream_clear(&codec->os);
201     free(codec);
202 }
203 
send_page(shout_t * self,ogg_page * page)204 static int send_page(shout_t *self, ogg_page *page)
205 {
206     int ret;
207 
208     ret = shout_send_raw(self, page->header, page->header_len);
209     if (ret != page->header_len) {
210         return self->error = SHOUTERR_SOCKET;
211     }
212 
213     ret = shout_send_raw(self, page->body, page->body_len);
214     if (ret != page->body_len) {
215         return self->error = SHOUTERR_SOCKET;
216     }
217 
218     return SHOUTERR_SUCCESS;
219 }
220