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