1 /*
2 # (C) 2009 Hans de Goede <hdegoede@redhat.com>
3
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU Lesser General Public License as published by
6 # the Free Software Foundation; either version 2.1 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <signal.h>
25 #include <sys/wait.h>
26 #include "libv4lconvert-priv.h"
27
28 #define READ_END 0
29 #define WRITE_END 1
30
31 /* <sigh> Unfortunately I've failed in contact some Authors of decompression
32 code of out of tree drivers. So I've no permission to relicense their code
33 their code from GPL to LGPL. To work around this, these decompression
34 algorithms are put in separate executables and we pipe data through these
35 to decompress.
36
37 The "protocol" is very simple:
38
39 From libv4l to the helper the following is send:
40 int width
41 int height
42 int flags
43 int data length
44 unsigned char[] data (data length long)
45
46 From the helper to libv4l the following is send:
47 int data length (-1 in case of a decompression error)
48 unsigned char[] data (not present when a decompression error happened)
49 */
50
v4lconvert_helper_start(struct v4lconvert_data * data,const char * helper)51 static int v4lconvert_helper_start(struct v4lconvert_data *data,
52 const char *helper)
53 {
54 if (pipe(data->decompress_in_pipe)) {
55 V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno));
56 goto error;
57 }
58
59 if (pipe(data->decompress_out_pipe)) {
60 V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno));
61 goto error_close_in_pipe;
62 }
63
64 data->decompress_pid = fork();
65 if (data->decompress_pid == -1) {
66 V4LCONVERT_ERR("with helper fork: %s\n", strerror(errno));
67 goto error_close_out_pipe;
68 }
69
70 if (data->decompress_pid == 0) {
71 /* We are the child */
72
73 /* Closed unused read / write end of the pipes */
74 close(data->decompress_out_pipe[WRITE_END]);
75 close(data->decompress_in_pipe[READ_END]);
76
77 /* Connect stdin / out to the pipes */
78 if (dup2(data->decompress_out_pipe[READ_END], STDIN_FILENO) == -1) {
79 perror("libv4lconvert: error with helper dup2");
80 exit(1);
81 }
82 if (dup2(data->decompress_in_pipe[WRITE_END], STDOUT_FILENO) == -1) {
83 perror("libv4lconvert: error with helper dup2");
84 exit(1);
85 }
86
87 /* And execute the helper */
88 execl(helper, helper, NULL);
89
90 /* We should never get here */
91 perror("libv4lconvert: error starting helper");
92 exit(1);
93 } else {
94 /* Closed unused read / write end of the pipes */
95 close(data->decompress_out_pipe[READ_END]);
96 close(data->decompress_in_pipe[WRITE_END]);
97 }
98
99 return 0;
100
101 error_close_out_pipe:
102 close(data->decompress_out_pipe[READ_END]);
103 close(data->decompress_out_pipe[WRITE_END]);
104 error_close_in_pipe:
105 close(data->decompress_in_pipe[READ_END]);
106 close(data->decompress_in_pipe[WRITE_END]);
107 error:
108 return -1;
109 }
110
111 /* IMPROVE ME: we could block SIGPIPE here using pthread_sigmask()
112 and then in case of EPIPE consume the signal using
113 sigtimedwait (we need to check if a blocked signal wasn't present
114 before the write, otherwise we will consume that) and
115 after consuming the signal try to restart the helper.
116
117 Note we currently do not do this, as SIGPIPE only happens if the
118 decompressor crashes, which in case of an embedded decompressor
119 would mean end of program, so by not handling SIGPIPE we treat
120 external decompressors identical. */
v4lconvert_helper_write(struct v4lconvert_data * data,const void * b,size_t count)121 static int v4lconvert_helper_write(struct v4lconvert_data *data,
122 const void *b, size_t count)
123 {
124 const unsigned char *buf = b;
125 size_t ret, written = 0;
126
127 while (written < count) {
128 ret = write(data->decompress_out_pipe[WRITE_END], buf + written,
129 count - written);
130 if (ret == -1) {
131 if (errno == EINTR)
132 continue;
133
134 V4LCONVERT_ERR("writing to helper: %s\n", strerror(errno));
135 return -1;
136 }
137 written += ret;
138 }
139
140 return 0;
141 }
142
v4lconvert_helper_read(struct v4lconvert_data * data,void * b,size_t count)143 static int v4lconvert_helper_read(struct v4lconvert_data *data, void *b,
144 size_t count)
145 {
146 unsigned char *buf = b;
147 size_t ret, r = 0;
148
149 while (r < count) {
150 ret = read(data->decompress_in_pipe[READ_END], buf + r, count - r);
151 if (ret == -1) {
152 if (errno == EINTR)
153 continue;
154
155 V4LCONVERT_ERR("reading from helper: %s\n", strerror(errno));
156 return -1;
157 }
158 if (ret == 0) {
159 V4LCONVERT_ERR("reading from helper: unexpected EOF\n");
160 return -1;
161 }
162 r += ret;
163 }
164
165 return 0;
166 }
167
v4lconvert_helper_decompress(struct v4lconvert_data * data,const char * helper,const unsigned char * src,int src_size,unsigned char * dest,int dest_size,int width,int height,int flags)168 int v4lconvert_helper_decompress(struct v4lconvert_data *data,
169 const char *helper, const unsigned char *src, int src_size,
170 unsigned char *dest, int dest_size, int width, int height, int flags)
171 {
172 int r;
173
174 if (data->decompress_pid == -1) {
175 if (v4lconvert_helper_start(data, helper))
176 return -1;
177 }
178
179 if (v4lconvert_helper_write(data, &width, sizeof(int)))
180 return -1;
181
182 if (v4lconvert_helper_write(data, &height, sizeof(int)))
183 return -1;
184
185 if (v4lconvert_helper_write(data, &flags, sizeof(int)))
186 return -1;
187
188 if (v4lconvert_helper_write(data, &src_size, sizeof(int)))
189 return -1;
190
191 if (v4lconvert_helper_write(data, src, src_size))
192 return -1;
193
194 if (v4lconvert_helper_read(data, &r, sizeof(int)))
195 return -1;
196
197 if (r < 0) {
198 V4LCONVERT_ERR("decompressing frame data\n");
199 return -1;
200 }
201
202 if (dest_size < r) {
203 V4LCONVERT_ERR("destination buffer to small\n");
204 return -1;
205 }
206
207 return v4lconvert_helper_read(data, dest, r);
208 }
209
v4lconvert_helper_cleanup(struct v4lconvert_data * data)210 void v4lconvert_helper_cleanup(struct v4lconvert_data *data)
211 {
212 int status;
213
214 if (data->decompress_pid != -1) {
215 close(data->decompress_out_pipe[WRITE_END]);
216 close(data->decompress_in_pipe[READ_END]);
217 waitpid(data->decompress_pid, &status, 0);
218 data->decompress_pid = -1;
219 }
220 }
221