Skip to content
Newer
Older
100644 252 lines (206 sloc) 6.76 KB
732389a ""
Chris Lightfoot authored May 29, 2002
1 /*
2 * playaudio.c:
3 * Play audio data.
4 *
5 * The game here is that we use threads and fork. Two things you never want to
6 * see together in the same sentence. Presently we only do MPEG data. Because
7 * we can't assume that mpg123 or whatever isn't going to roll over and play
8 * dead if we present it with random stuff we picked up off the network, we
9 * arrange to restart it if it dies.
10 *
11 * Copyright (c) 2002 Chris Lightfoot. All rights reserved.
12 * Email: chris@ex-parrot.com; WWW: http://www.ex-parrot.com/~chris/
13 *
14 */
15
968a9e3 ""
Chris Lightfoot authored Aug 12, 2003
16 static const char rcsid[] = "$Id: playaudio.c,v 1.5 2003/08/12 14:12:29 chris Exp $";
732389a ""
Chris Lightfoot authored May 29, 2002
17
18 #include <sys/types.h>
19
20 #include <errno.h>
21 #include <pthread.h>
a85d292 ""
Chris Lightfoot authored Jun 1, 2002
22 #include <signal.h>
732389a ""
Chris Lightfoot authored May 29, 2002
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28
29 #include <sys/wait.h>
30
31 #include "driftnet.h"
32
33 extern int verbose; /* in driftnet.c */
34
35 /* The program we use to play MPEG data. Can be changed with -M */
36 char *audio_mpeg_player = "mpg123 -";
37
38 static pthread_mutex_t mpeg_mtx = PTHREAD_MUTEX_INITIALIZER;
39
40 #define m_lock pthread_mutex_lock(&mpeg_mtx)
41 #define m_unlock pthread_mutex_unlock(&mpeg_mtx)
42
43 /* audiochunk:
44 * A bit of audio which we are going to throw at the decoder. list represents
45 * the list of all chunks which are available, wr the place that we insert new
46 * data that we've obtained and rd the place that we're reading data to send
47 * into the decoder. */
48 typedef struct _audiochunk {
49 unsigned char *data;
50 size_t len;
51 struct _audiochunk *next;
52 } *audiochunk;
53
54 static audiochunk list, wr, rd;
55
56 /* audiochunk_new:
57 * Allocate a buffer and copy some data into it. */
58 static audiochunk audiochunk_new(const unsigned char *data, const size_t len) {
59 audiochunk A;
968a9e3 ""
Chris Lightfoot authored Aug 12, 2003
60 alloc_struct(_audiochunk, A);
732389a ""
Chris Lightfoot authored May 29, 2002
61 A->len = len;
62 if (data) {
968a9e3 ""
Chris Lightfoot authored Aug 12, 2003
63 A->data = xmalloc(len);
732389a ""
Chris Lightfoot authored May 29, 2002
64 memcpy(A->data, data, len);
65 }
66 return A;
67 }
68
69 /* audiochunk_delete:
70 * Free memory from an audiochunk. */
71 static void audiochunk_delete(audiochunk A) {
968a9e3 ""
Chris Lightfoot authored Aug 12, 2003
72 xfree(A->data);
73 xfree(A);
732389a ""
Chris Lightfoot authored May 29, 2002
74 }
75
76 /* audiochunk_write:
77 * Write the contents of an audiochunk down a file descriptor. Returns 0 on
78 * success or -1 on failure. */
79 #define WRCHUNK 1024
80 static int audiochunk_write(const audiochunk A, int fd) {
81 const unsigned char *p;
82 ssize_t n;
83 if (A->len == 0)
84 return 0;
85 p = A->data;
86 do {
87 size_t d = WRCHUNK;
88 if (p + d > A->data + A->len)
89 d = A->data + A->len - p;
90
91 n = write(fd, p, d);
92 if (n == -1 && errno != EINTR)
93 return -1;
94 else
95 p += d;
96 } while (p < A->data + A->len);
97 return 0;
98 }
99
100 /* How much data we have buffered; if this rises too high, we start silently
101 * dropping data. */
102 static size_t buffered;
103 #define MAX_BUFFERED (8 * 1024 * 1024) /* 8Mb */
104
105 /* mpeg_submit_chunk:
106 * Put some MPEG data into the queue to be played. */
107 void mpeg_submit_chunk(const unsigned char *data, const size_t len) {
108 audiochunk A;
109
110 m_lock;
111
112 if (buffered > MAX_BUFFERED) {
113 if (verbose)
114 fprintf(stderr, PROGNAME": MPEG buffer full with %d bytes\n", buffered);
115 goto finish;
116 }
117 A = audiochunk_new(data, len);
118 wr->next = A;
119 wr = wr->next;
120
121 buffered += len;
122
123 finish:
124 m_unlock;
125 }
126
127 /* mpeg_play:
128 * Play MPEG data. This runs in a separate thread. The parameter is the
129 * audiochunk from which we start reading data. */
130 int mpeg_fd; /* the file descriptor into which we write data. */
131
132 static void *mpeg_play(void *a) {
133 audiochunk A;
134 A = (audiochunk)a;
135
136 while (1) { /*(!foad) {*/
137 audiochunk A;
138
139 m_lock;
140 A = rd->next;
141 m_unlock;
142
143 if (A) {
144 /* Got some data, submit it to the encoder. */
145 if (audiochunk_write(A, mpeg_fd) == -1)
146 fprintf(stderr, PROGNAME": write to MPEG player: %s\n", strerror(errno));
147
148 m_lock;
149 buffered -= A->len;
150 audiochunk_delete(rd);
151 rd = A;
152 m_unlock;
153 } else {
154 /* No data, sleep for a little bit. */
155 struct timespec tm;
156 tm.tv_sec = 0;
157 tm.tv_nsec = 100000000; /* 0.1s */
158 nanosleep(&tm, NULL);
159 }
160 }
161
162 return NULL;
163 }
164
165 /* mpeg_player_manager:
166 * Main loop of child process which keeps an MPEG player running. */
a85d292 ""
Chris Lightfoot authored Jun 1, 2002
167 static void mpeg_player_manager(void) {
168 extern sig_atomic_t foad; /* in driftnet.c */
8cde665 @rbu Minor fixes, do not loop for tmpdir
authored Mar 23, 2009
169 struct sigaction sa = {{0}};
a85d292 ""
Chris Lightfoot authored Jun 1, 2002
170 pid_t mpeg_pid;
62d46a9 ""
Chris Lightfoot authored Jun 4, 2002
171
172 sa.sa_handler = SIG_DFL;
173 sigaction(SIGCHLD, &sa, NULL);
a85d292 ""
Chris Lightfoot authored Jun 1, 2002
174
175 while (!foad) {
732389a ""
Chris Lightfoot authored May 29, 2002
176 time_t whenstarted;
177 int st;
178
179 fprintf(stderr, PROGNAME": starting MPEG player `%s'\n", audio_mpeg_player);
180
181 whenstarted = time(NULL);
182 switch ((mpeg_pid = fork())) {
183 case 0:
184 execl("/bin/sh", "/bin/sh", "-c", audio_mpeg_player, NULL);
185 fprintf(stderr, PROGNAME": exec: %s\n", strerror(errno));
186 exit(-1);
187 break;
188
189 case -1:
190 fprintf(stderr, PROGNAME": fork: %s\n", strerror(errno));
191 exit(-1); /* gah, not much we can do now. */
192 break;
193
194 default:
195 /* parent. */
196 if (verbose)
197 fprintf(stderr, PROGNAME": MPEG player has PID %d\n", (int)mpeg_pid);
198 break;
199 }
200
201 /* wait for it to exit. */
202 waitpid(mpeg_pid, &st, 0);
203 mpeg_pid = 0;
204
205 if (verbose) {
206 if (WIFEXITED(st))
207 fprintf(stderr, PROGNAME": MPEG player exited with status %d\n", WEXITSTATUS(st));
208 else if (WIFSIGNALED(st))
209 fprintf(stderr, PROGNAME": MPEG player killed by signal %d\n", WTERMSIG(st));
210 /* else ?? */
211 }
212
213
a85d292 ""
Chris Lightfoot authored Jun 1, 2002
214 if (!foad && time(NULL) - whenstarted < 5) {
732389a ""
Chris Lightfoot authored May 29, 2002
215 /* The player expired very quickly. Probably something's wrong;
216 * sleep for a bit and hope the problem goes away. */
5654f93 ""
Chris Lightfoot authored Jun 1, 2002
217 fprintf(stderr, PROGNAME": MPEG player expired after %d seconds, sleeping for a bit\n", (int)(time(NULL) - whenstarted));
732389a ""
Chris Lightfoot authored May 29, 2002
218 sleep(5);
219 }
220 }
a85d292 ""
Chris Lightfoot authored Jun 1, 2002
221 if (mpeg_pid)
222 kill(mpeg_pid, SIGTERM);
732389a ""
Chris Lightfoot authored May 29, 2002
223 }
224
225 /* do_mpeg_player:
226 * Fork and start a process which keeps an MPEG player running. Then start a
227 * thread which passes data into the player. */
228 pid_t mpeg_mgr_pid;
a85d292 ""
Chris Lightfoot authored Jun 1, 2002
229
732389a ""
Chris Lightfoot authored May 29, 2002
230 void do_mpeg_player(void) {
231 int pp[2];
232 pthread_t thr;
233
234 rd = wr = list = audiochunk_new(NULL, 0);
235
236 pipe(pp);
237
238 mpeg_mgr_pid = fork();
239 if (mpeg_mgr_pid == 0) {
240 close(pp[1]);
241 dup2(pp[0], 0); /* make pipe our standard input */
242 mpeg_player_manager();
243 exit(-1);
244 } else {
245 close(pp[0]);
246 mpeg_fd = pp[1];
247 pthread_create(&thr, NULL, mpeg_play, rd);
248 }
249
250 /* away we go... */
251 }
Something went wrong with that request. Please try again.