1 /*
2 * "muxer" to write mpeg audio elementary streams
3 * Copyright (C) 2013 Dave Chapman
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <htmlui://www.gnu.org/licenses/>.
17 */
18
19 #include <assert.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <sys/stat.h>
24
25 #include "tvheadend.h"
26 #include "streaming.h"
27 #include "epg.h"
28 #include "channels.h"
29 #include "muxer_audioes.h"
30
31 typedef struct audioes_muxer {
32 muxer_t;
33
34 int error;
35
36 /* Info about the service */
37 int am_index;
38
39 /* File descriptor stuff */
40 int am_fd;
41 int am_seekable;
42 int am_error;
43 off_t am_off;
44
45 /* Filename is also used for logging */
46 char *am_filename;
47 } audioes_muxer_t;
48
49
50 /**
51 *
52 */
53 static int
audioes_muxer_type(streaming_component_type_t type)54 audioes_muxer_type(streaming_component_type_t type)
55 {
56 muxer_container_type_t mc = MC_UNKNOWN;
57 switch (type) {
58 case SCT_MPEG2AUDIO: mc = MC_MPEG2AUDIO; break;
59 case SCT_AC3: mc = MC_AC3; break;
60 case SCT_EAC3: mc = MC_AC3; break;
61 case SCT_AAC: mc = MC_AAC; break;
62 case SCT_MP4A: mc = MC_MP4A; break;
63 case SCT_VORBIS: mc = MC_VORBIS; break;
64 default: break;
65 }
66 return mc;
67 }
68
69
70 /**
71 *
72 */
73 static const streaming_start_component_t *
audioes_get_component(muxer_t * m,const struct streaming_start * ss)74 audioes_get_component(muxer_t *m, const struct streaming_start *ss)
75 {
76 const streaming_start_component_t *ssc;
77 muxer_container_type_t mc = MC_UNKNOWN;
78 int i, count;
79
80 for (i = count = 0; i < ss->ss_num_components;i++) {
81 ssc = &ss->ss_components[i];
82
83 if ((!ssc->ssc_disabled) && (SCT_ISAUDIO(ssc->ssc_type))) {
84 if (m->m_config.m_force_type != MC_UNKNOWN) {
85 mc = audioes_muxer_type(ssc->ssc_type);
86 if (m->m_config.m_force_type != mc)
87 continue;
88 }
89 if (m->m_config.m_index == count)
90 return ssc;
91 count++;
92 }
93 }
94 return NULL;
95 }
96
97
98 /**
99 * Figure out the mimetype
100 */
101 static const char *
audioes_muxer_mime(muxer_t * m,const struct streaming_start * ss)102 audioes_muxer_mime(muxer_t *m, const struct streaming_start *ss)
103 {
104 muxer_container_type_t mc = MC_UNKNOWN;
105 const streaming_start_component_t *ssc;
106
107 if (m->m_config.m_force_type != MC_UNKNOWN)
108 return muxer_container_type2mime(m->m_config.m_force_type, 0);
109
110 ssc = audioes_get_component(m, ss);
111 if (ssc)
112 mc = audioes_muxer_type(ssc->ssc_type);
113
114 return muxer_container_type2mime(mc, 0);
115 }
116
117
118 /**
119 * Reconfigure the muxer
120 */
121 static int
audioes_muxer_reconfigure(muxer_t * m,const struct streaming_start * ss)122 audioes_muxer_reconfigure(muxer_t *m, const struct streaming_start *ss)
123 {
124 audioes_muxer_t *am = (audioes_muxer_t*)m;
125 const streaming_start_component_t *ssc;
126
127 am->am_index = -1;
128
129 ssc = audioes_get_component(m, ss);
130 if (ssc)
131 am->am_index = ssc->ssc_index;
132
133 return 0;
134 }
135
136
137 /**
138 * Init the builtin mkv muxer with streams
139 */
140 static int
audioes_muxer_init(muxer_t * m,struct streaming_start * ss,const char * name)141 audioes_muxer_init(muxer_t* m, struct streaming_start *ss, const char *name)
142 {
143 return audioes_muxer_reconfigure(m, ss);
144 }
145
146
147 /*
148 * Open the muxer as a stream muxer (using a non-seekable socket)
149 */
150 static int
audioes_muxer_open_stream(muxer_t * m,int fd)151 audioes_muxer_open_stream(muxer_t *m, int fd)
152 {
153 audioes_muxer_t *am = (audioes_muxer_t*)m;
154
155 am->am_fd = fd;
156 am->am_seekable = 0;
157 am->am_off = 0;
158 am->am_filename = strdup("Live stream");
159
160 return 0;
161 }
162
163
164 /**
165 * Open the file and set the file descriptor
166 */
167 static int
audioes_muxer_open_file(muxer_t * m,const char * filename)168 audioes_muxer_open_file(muxer_t *m, const char *filename)
169 {
170 int fd;
171 audioes_muxer_t *am = (audioes_muxer_t*)m;
172
173 tvhtrace(LS_AUDIOES, "Creating file \"%s\" with file permissions \"%o\"", filename, am->m_config.m_file_permissions);
174
175 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, am->m_config.m_file_permissions);
176
177 if(fd < 0) {
178 am->am_error = errno;
179 tvherror(LS_AUDIOES, "%s: Unable to create file, open failed -- %s",
180 filename, strerror(errno));
181 am->m_errors++;
182 return -1;
183 }
184
185 /* bypass umask settings */
186 if (fchmod(fd, am->m_config.m_file_permissions))
187 tvherror(LS_AUDIOES, "%s: Unable to change permissions -- %s",
188 filename, strerror(errno));
189
190 am->am_seekable = 1;
191 am->am_off = 0;
192 am->am_fd = fd;
193 am->am_filename = strdup(filename);
194 return 0;
195 }
196
197 /**
198 * Write a packet to the muxer
199 */
200 static int
audioes_muxer_write_pkt(muxer_t * m,streaming_message_type_t smt,void * data)201 audioes_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data)
202 {
203 th_pkt_t *pkt = (th_pkt_t*)data;
204 audioes_muxer_t *am = (audioes_muxer_t*)m;
205 size_t size;
206
207 assert(smt == SMT_PACKET);
208
209 if (pkt->pkt_componentindex != am->am_index) {
210 pkt_ref_dec(pkt);
211 return am->error;
212 }
213
214 size = pktbuf_len(pkt->pkt_payload);
215 if (size == 0) {
216 pkt_ref_dec(pkt);
217 return am->error;
218 }
219
220 if (am->am_error) {
221 am->m_errors++;
222 } else if (tvh_write(am->am_fd, pktbuf_ptr(pkt->pkt_payload), size)) {
223 am->am_error = errno;
224 if (!MC_IS_EOS_ERROR(errno)) {
225 tvherror(LS_AUDIOES, "%s: Write failed -- %s", am->am_filename,
226 strerror(errno));
227 } else {
228 am->m_eos = 1;
229 }
230 am->m_errors++;
231 if (am->am_seekable) {
232 muxer_cache_update(m, am->am_fd, am->am_off, 0);
233 am->am_off = lseek(am->am_fd, 0, SEEK_CUR);
234 }
235 } else {
236 if (am->am_seekable)
237 muxer_cache_update(m, am->am_fd, am->am_off, 0);
238 am->am_off += size;
239 }
240
241 pkt_ref_dec(pkt);
242 return am->error;
243 }
244
245
246 /**
247 * NOP
248 */
249 static int
audioes_muxer_write_meta(muxer_t * m,struct epg_broadcast * eb,const char * comment)250 audioes_muxer_write_meta(muxer_t *m, struct epg_broadcast *eb, const char *comment)
251 {
252 return 0;
253 }
254
255
256
257 /**
258 * Close the muxer
259 */
260 static int
audioes_muxer_close(muxer_t * m)261 audioes_muxer_close(muxer_t *m)
262 {
263 audioes_muxer_t *am = (audioes_muxer_t*)m;
264
265 if ((am->am_seekable) && (close(am->am_fd))) {
266 am->am_error = errno;
267 tvherror(LS_AUDIOES, "%s: Unable to close file -- %s",
268 am->am_filename, strerror(errno));
269 am->m_errors++;
270 return -1;
271 }
272
273 return 0;
274 }
275
276
277 /**
278 * Free all memory associated with the muxer
279 */
280 static void
audioes_muxer_destroy(muxer_t * m)281 audioes_muxer_destroy(muxer_t *m)
282 {
283 audioes_muxer_t *am = (audioes_muxer_t*)m;
284
285 if (am->am_filename)
286 free(am->am_filename);
287 free(am);
288 }
289
290
291 /**
292 * Create a new builtin muxer
293 */
294 muxer_t*
audioes_muxer_create(const muxer_config_t * m_cfg)295 audioes_muxer_create(const muxer_config_t *m_cfg)
296 {
297 audioes_muxer_t *am;
298
299 if(m_cfg->m_type != MC_MPEG2AUDIO &&
300 m_cfg->m_type != MC_AC3 &&
301 m_cfg->m_type != MC_AAC &&
302 m_cfg->m_type != MC_MP4A &&
303 m_cfg->m_type != MC_VORBIS)
304 return NULL;
305
306 am = calloc(1, sizeof(audioes_muxer_t));
307 am->m_open_stream = audioes_muxer_open_stream;
308 am->m_open_file = audioes_muxer_open_file;
309 am->m_mime = audioes_muxer_mime;
310 am->m_init = audioes_muxer_init;
311 am->m_reconfigure = audioes_muxer_reconfigure;
312 am->m_write_meta = audioes_muxer_write_meta;
313 am->m_write_pkt = audioes_muxer_write_pkt;
314 am->m_close = audioes_muxer_close;
315 am->m_destroy = audioes_muxer_destroy;
316
317 return (muxer_t*)am;
318 }
319