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