input: add input_SetProgramId
[vlc.git] / modules / stream_filter / decomp.c
blobebd39295453e3cef8b997d81338d3e50a2c30d1d
1 /*****************************************************************************
2 * decomp.c : Decompression module for vlc
3 *****************************************************************************
4 * Copyright © 2008-2009 Rémi Denis-Courmont
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_plugin.h>
27 #include <vlc_stream.h>
28 #include <vlc_network.h>
29 #include <vlc_fs.h>
30 #include <vlc_spawn.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <sys/ioctl.h>
35 #if defined (__linux__) && defined (HAVE_VMSPLICE)
36 # include <sys/uio.h>
37 # include <sys/mman.h>
38 #else
39 # undef HAVE_VMSPLICE
40 #endif
41 #include <vlc_interrupt.h>
43 #include <signal.h>
45 static int OpenGzip (vlc_object_t *);
46 static int OpenBzip2 (vlc_object_t *);
47 static int OpenXZ (vlc_object_t *);
48 static void Close (vlc_object_t *);
50 vlc_module_begin ()
51 set_category (CAT_INPUT)
52 set_subcategory (SUBCAT_INPUT_STREAM_FILTER)
53 set_capability ("stream_filter", 320)
55 set_description (N_("LZMA decompression"))
56 set_callbacks (OpenXZ, Close)
58 add_submodule ()
59 set_description (N_("Burrows-Wheeler decompression"))
60 set_callbacks (OpenBzip2, Close)
61 /* TODO: access shortnames for vlc_stream_NewURL() */
63 add_submodule ()
64 set_description (N_("gzip decompression"))
65 set_callbacks (OpenGzip, Close)
66 vlc_module_end ()
68 typedef struct
70 /* Thread data */
71 int write_fd;
73 /* Shared data */
74 vlc_cond_t wait;
75 vlc_mutex_t lock;
76 bool paused;
78 /* Caller data */
79 vlc_thread_t thread;
80 pid_t pid;
82 int read_fd;
83 bool can_pace;
84 bool can_pause;
85 vlc_tick_t pts_delay;
86 } stream_sys_t;
88 extern char **environ;
90 static const size_t bufsize = 65536;
91 #ifdef HAVE_VMSPLICE
92 static void cleanup_mmap (void *addr)
94 munmap (addr, bufsize);
96 #endif
98 static void *Thread (void *data)
100 stream_t *stream = data;
101 stream_sys_t *p_sys = stream->p_sys;
102 #ifdef HAVE_VMSPLICE
103 const ssize_t page_mask = sysconf (_SC_PAGE_SIZE) - 1;
104 #endif
105 int fd = p_sys->write_fd;
106 bool error = false;
107 sigset_t set;
109 sigemptyset(&set);
110 sigaddset(&set, SIGPIPE);
111 pthread_sigmask(SIG_BLOCK, &set, NULL);
115 ssize_t len;
116 int canc = vlc_savecancel ();
117 #ifdef HAVE_VMSPLICE
118 unsigned char *buf = mmap (NULL, bufsize, PROT_READ|PROT_WRITE,
119 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
120 if (unlikely(buf == MAP_FAILED))
121 break;
122 vlc_cleanup_push (cleanup_mmap, buf);
123 #else
124 unsigned char *buf = malloc (bufsize);
125 if (unlikely(buf == NULL))
126 break;
127 vlc_cleanup_push (free, buf);
128 #endif
130 vlc_mutex_lock (&p_sys->lock);
131 while (p_sys->paused) /* practically always false, but... */
132 vlc_cond_wait (&p_sys->wait, &p_sys->lock);
133 len = vlc_stream_Read (stream->s, buf, bufsize);
134 vlc_mutex_unlock (&p_sys->lock);
136 vlc_restorecancel (canc);
137 error = len <= 0;
139 for (ssize_t i = 0, j; i < len; i += j)
141 #ifdef HAVE_VMSPLICE
142 if ((len - i) <= page_mask) /* incomplete last page */
143 j = write (fd, buf + i, len - i);
144 else
146 struct iovec iov = {
147 .iov_base = buf + i,
148 .iov_len = (len - i) & ~page_mask };
150 j = vmsplice (fd, &iov, 1, SPLICE_F_GIFT);
152 if (j == -1 && errno == ENOSYS) /* vmsplice() not supported */
153 #endif
154 j = write (fd, buf + i, len - i);
155 if (j <= 0)
157 if (j == 0)
158 errno = EPIPE;
159 msg_Err (stream, "cannot write data: %s",
160 vlc_strerror_c(errno));
161 error = true;
162 break;
165 vlc_cleanup_pop ();
166 #ifdef HAVE_VMSPLICE
167 munmap (buf, bufsize);
168 #else
169 free (buf);
170 #endif
172 while (!error);
174 msg_Dbg (stream, "compressed stream at EOF");
175 /* Let child process know about EOF */
176 p_sys->write_fd = -1;
177 vlc_close (fd);
178 return NULL;
182 #define MIN_BLOCK (1 << 10)
183 #define MAX_BLOCK (1 << 20)
185 * Reads decompressed from the decompression program
186 * @return -1 for EAGAIN, 0 for EOF, byte count otherwise.
188 static ssize_t Read (stream_t *stream, void *buf, size_t buflen)
190 stream_sys_t *sys = stream->p_sys;
191 ssize_t val = vlc_read_i11e (sys->read_fd, buf, buflen);
192 return (val >= 0) ? val : 0;
198 static int Control (stream_t *stream, int query, va_list args)
200 stream_sys_t *p_sys = stream->p_sys;
202 switch (query)
204 case STREAM_CAN_SEEK:
205 case STREAM_CAN_FASTSEEK:
206 *(va_arg (args, bool *)) = false;
207 break;
208 case STREAM_CAN_PAUSE:
209 *(va_arg (args, bool *)) = p_sys->can_pause;
210 break;
211 case STREAM_CAN_CONTROL_PACE:
212 *(va_arg (args, bool *)) = p_sys->can_pace;
213 break;
214 case STREAM_GET_SIZE:
215 *(va_arg (args, uint64_t *)) = 0;
216 break;
217 case STREAM_GET_PTS_DELAY:
218 *va_arg (args, vlc_tick_t *) = p_sys->pts_delay;
219 break;
220 case STREAM_SET_PAUSE_STATE:
222 bool paused = va_arg (args, unsigned);
224 vlc_mutex_lock (&p_sys->lock);
225 vlc_stream_Control(stream->s, STREAM_SET_PAUSE_STATE, paused);
226 p_sys->paused = paused;
227 vlc_cond_signal (&p_sys->wait);
228 vlc_mutex_unlock (&p_sys->lock);
229 break;
231 default:
232 return VLC_EGENERIC;
234 return VLC_SUCCESS;
238 * Pipe data through an external executable.
239 * @param stream the stream filter object.
240 * @param path path to the executable.
242 static int Open (stream_t *stream, const char *path)
244 stream_sys_t *p_sys = stream->p_sys = malloc (sizeof (*p_sys));
245 if (p_sys == NULL)
246 return VLC_ENOMEM;
248 vlc_cond_init (&p_sys->wait);
249 vlc_mutex_init (&p_sys->lock);
250 p_sys->paused = false;
251 p_sys->pid = -1;
252 vlc_stream_Control(stream->s, STREAM_CAN_PAUSE, &p_sys->can_pause);
253 vlc_stream_Control(stream->s, STREAM_CAN_CONTROL_PACE, &p_sys->can_pace);
254 vlc_stream_Control(stream->s, STREAM_GET_PTS_DELAY, &p_sys->pts_delay);
256 /* I am not a big fan of the pyramid style, but I cannot think of anything
257 * better here. There are too many failure cases. */
258 int ret = VLC_EGENERIC;
259 int comp[2];
261 /* We use two pipes rather than one stream socket pair, so that we can
262 * use vmsplice() on Linux. */
263 if (vlc_pipe (comp) == 0)
265 p_sys->write_fd = comp[1];
267 int uncomp[2];
268 if (vlc_pipe (uncomp) == 0)
270 int fdv[] = { comp[0], uncomp[1], 2, -1 };
271 const char *argv[] = { path, NULL };
273 p_sys->read_fd = uncomp[0];
275 if (vlc_spawnp(&p_sys->pid, path, fdv, argv) == 0)
277 if (vlc_clone(&p_sys->thread, Thread, stream,
278 VLC_THREAD_PRIORITY_INPUT) == 0)
279 ret = VLC_SUCCESS;
281 else
283 msg_Err (stream, "cannot execute %s", path);
284 p_sys->pid = -1;
287 vlc_close (uncomp[1]);
288 if (ret != VLC_SUCCESS)
289 vlc_close (uncomp[0]);
291 vlc_close (comp[0]);
292 if (ret != VLC_SUCCESS)
293 vlc_close (comp[1]);
296 if (ret != VLC_SUCCESS)
298 if (p_sys->pid != -1)
299 vlc_waitpid(p_sys->pid);
300 free (p_sys);
301 return ret;
304 stream->pf_read = Read;
305 stream->pf_seek = NULL;
306 stream->pf_control = Control;
307 return VLC_SUCCESS;
312 * Releases allocate resources.
314 static void Close (vlc_object_t *obj)
316 stream_t *stream = (stream_t *)obj;
317 stream_sys_t *p_sys = stream->p_sys;
319 vlc_cancel (p_sys->thread);
320 vlc_close (p_sys->read_fd);
321 vlc_join (p_sys->thread, NULL);
322 if (p_sys->write_fd != -1)
323 /* Killed before EOF? */
324 vlc_close (p_sys->write_fd);
326 msg_Dbg (obj, "waiting for PID %u", (unsigned)p_sys->pid);
327 msg_Dbg (obj, "exit status %d", vlc_waitpid(p_sys->pid));
328 free (p_sys);
333 * Detects gzip file format
335 static int OpenGzip (vlc_object_t *obj)
337 stream_t *stream = (stream_t *)obj;
338 const uint8_t *peek;
340 if (vlc_stream_Peek (stream->s, &peek, 3) < 3)
341 return VLC_EGENERIC;
343 if (memcmp (peek, "\x1f\x8b\x08", 3))
344 return VLC_EGENERIC;
346 msg_Dbg (obj, "detected gzip compressed stream");
347 return Open (stream, "zcat");
352 * Detects bzip2 file format
354 static int OpenBzip2 (vlc_object_t *obj)
356 stream_t *stream = (stream_t *)obj;
357 const uint8_t *peek;
359 /* (Try to) parse the bzip2 header */
360 if (vlc_stream_Peek (stream->s, &peek, 10) < 10)
361 return VLC_EGENERIC;
363 if (memcmp (peek, "BZh", 3) || (peek[3] < '1') || (peek[3] > '9')
364 || memcmp (peek + 4, "\x31\x41\x59\x26\x53\x59", 6))
365 return VLC_EGENERIC;
367 msg_Dbg (obj, "detected bzip2 compressed stream");
368 return Open (stream, "bzcat");
372 * Detects xz file format
374 static int OpenXZ (vlc_object_t *obj)
376 stream_t *stream = (stream_t *)obj;
377 const uint8_t *peek;
379 /* (Try to) parse the xz stream header */
380 if (vlc_stream_Peek (stream->s, &peek, 8) < 8)
381 return VLC_EGENERIC;
383 if (memcmp (peek, "\xfd\x37\x7a\x58\x5a", 6))
384 return VLC_EGENERIC;
386 msg_Dbg (obj, "detected xz compressed stream");
387 return Open (stream, "xzcat");