diff options
Diffstat (limited to 'src/conversation/gnunet-helper-audio-playback.c')
-rw-r--r-- | src/conversation/gnunet-helper-audio-playback.c | 1023 |
1 files changed, 515 insertions, 508 deletions
diff --git a/src/conversation/gnunet-helper-audio-playback.c b/src/conversation/gnunet-helper-audio-playback.c index e86bb6508..34e61c100 100644 --- a/src/conversation/gnunet-helper-audio-playback.c +++ b/src/conversation/gnunet-helper-audio-playback.c | |||
@@ -11,12 +11,12 @@ | |||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Affero General Public License for more details. | 13 | Affero General Public License for more details. |
14 | 14 | ||
15 | You should have received a copy of the GNU Affero General Public License | 15 | You should have received a copy of the GNU Affero General Public License |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | 17 | ||
18 | SPDX-License-Identifier: AGPL3.0-or-later | 18 | SPDX-License-Identifier: AGPL3.0-or-later |
19 | */ | 19 | */ |
20 | /** | 20 | /** |
21 | * @file conversation/gnunet-helper-audio-playback.c | 21 | * @file conversation/gnunet-helper-audio-playback.c |
22 | * @brief program to playback audio data to the speaker | 22 | * @brief program to playback audio data to the speaker |
@@ -124,8 +124,7 @@ static float gain; | |||
124 | GNUNET_NETWORK_STRUCT_BEGIN | 124 | GNUNET_NETWORK_STRUCT_BEGIN |
125 | 125 | ||
126 | /* OggOpus spec says the numbers must be in little-endian order */ | 126 | /* OggOpus spec says the numbers must be in little-endian order */ |
127 | struct OpusHeadPacket | 127 | struct OpusHeadPacket { |
128 | { | ||
129 | uint8_t magic[8]; | 128 | uint8_t magic[8]; |
130 | uint8_t version; | 129 | uint8_t version; |
131 | uint8_t channels; | 130 | uint8_t channels; |
@@ -143,71 +142,71 @@ GNUNET_NETWORK_STRUCT_END | |||
143 | * elsewhere in the code. | 142 | * elsewhere in the code. |
144 | */ | 143 | */ |
145 | static OpusDecoder * | 144 | static OpusDecoder * |
146 | process_header (ogg_packet *op) | 145 | process_header(ogg_packet *op) |
147 | { | 146 | { |
148 | int err; | 147 | int err; |
149 | OpusDecoder *dec; | 148 | OpusDecoder *dec; |
150 | struct OpusHeadPacket header; | 149 | struct OpusHeadPacket header; |
151 | 150 | ||
152 | if ( ((unsigned int) op->bytes) < sizeof (header)) | 151 | if (((unsigned int)op->bytes) < sizeof(header)) |
153 | return NULL; | 152 | return NULL; |
154 | GNUNET_memcpy (&header, | 153 | GNUNET_memcpy(&header, |
155 | op->packet, | 154 | op->packet, |
156 | sizeof (header)); | 155 | sizeof(header)); |
157 | header.preskip = GNUNET_le16toh (header.preskip); | 156 | header.preskip = GNUNET_le16toh(header.preskip); |
158 | header.sampling_rate = GNUNET_le32toh (header.sampling_rate); | 157 | header.sampling_rate = GNUNET_le32toh(header.sampling_rate); |
159 | header.gain = GNUNET_le16toh (header.gain); | 158 | header.gain = GNUNET_le16toh(header.gain); |
160 | 159 | ||
161 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 160 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
162 | "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n", | 161 | "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n", |
163 | header.version, | 162 | header.version, |
164 | header.channels, | 163 | header.channels, |
165 | header.preskip, | 164 | header.preskip, |
166 | header.sampling_rate, | 165 | header.sampling_rate, |
167 | header.gain); | 166 | header.gain); |
168 | channels = header.channels; | 167 | channels = header.channels; |
169 | preskip = header.preskip; | 168 | preskip = header.preskip; |
170 | 169 | ||
171 | if (header.channel_mapping != 0) | 170 | if (header.channel_mapping != 0) |
172 | { | 171 | { |
173 | fprintf (stderr, | 172 | fprintf(stderr, |
174 | "This implementation does not support non-mono streams\n"); | 173 | "This implementation does not support non-mono streams\n"); |
175 | return NULL; | 174 | return NULL; |
176 | } | 175 | } |
177 | 176 | ||
178 | dec = opus_decoder_create (SAMPLING_RATE, channels, &err); | 177 | dec = opus_decoder_create(SAMPLING_RATE, channels, &err); |
179 | if (OPUS_OK != err) | 178 | if (OPUS_OK != err) |
180 | { | ||
181 | fprintf (stderr, | ||
182 | "Cannot create encoder: %s\n", | ||
183 | opus_strerror (err)); | ||
184 | return NULL; | ||
185 | } | ||
186 | if (! dec) | ||
187 | { | ||
188 | fprintf (stderr, | ||
189 | "Decoder initialization failed: %s\n", | ||
190 | opus_strerror (err)); | ||
191 | return NULL; | ||
192 | } | ||
193 | |||
194 | if (0 != header.gain) | ||
195 | { | ||
196 | /*Gain API added in a newer libopus version, if we don't have it | ||
197 | we apply the gain ourselves. We also add in a user provided | ||
198 | manual gain at the same time.*/ | ||
199 | int gainadj = (int) header.gain; | ||
200 | err = opus_decoder_ctl (dec, OPUS_SET_GAIN (gainadj)); | ||
201 | if(OPUS_UNIMPLEMENTED == err) | ||
202 | { | 179 | { |
203 | gain = pow (10.0, gainadj / 5120.0); | 180 | fprintf(stderr, |
181 | "Cannot create encoder: %s\n", | ||
182 | opus_strerror(err)); | ||
183 | return NULL; | ||
204 | } | 184 | } |
205 | else if (OPUS_OK != err) | 185 | if (!dec) |
206 | { | 186 | { |
207 | fprintf (stderr, "Error setting gain: %s\n", opus_strerror (err)); | 187 | fprintf(stderr, |
188 | "Decoder initialization failed: %s\n", | ||
189 | opus_strerror(err)); | ||
208 | return NULL; | 190 | return NULL; |
209 | } | 191 | } |
210 | } | 192 | |
193 | if (0 != header.gain) | ||
194 | { | ||
195 | /*Gain API added in a newer libopus version, if we don't have it | ||
196 | we apply the gain ourselves. We also add in a user provided | ||
197 | manual gain at the same time.*/ | ||
198 | int gainadj = (int)header.gain; | ||
199 | err = opus_decoder_ctl(dec, OPUS_SET_GAIN(gainadj)); | ||
200 | if (OPUS_UNIMPLEMENTED == err) | ||
201 | { | ||
202 | gain = pow(10.0, gainadj / 5120.0); | ||
203 | } | ||
204 | else if (OPUS_OK != err) | ||
205 | { | ||
206 | fprintf(stderr, "Error setting gain: %s\n", opus_strerror(err)); | ||
207 | return NULL; | ||
208 | } | ||
209 | } | ||
211 | 210 | ||
212 | return dec; | 211 | return dec; |
213 | } | 212 | } |
@@ -217,122 +216,126 @@ process_header (ogg_packet *op) | |||
217 | static size_t | 216 | static size_t |
218 | fwrite_le32(opus_int32 i32, FILE *file) | 217 | fwrite_le32(opus_int32 i32, FILE *file) |
219 | { | 218 | { |
220 | unsigned char buf[4]; | 219 | unsigned char buf[4]; |
221 | buf[0]=(unsigned char)(i32&0xFF); | 220 | |
222 | buf[1]=(unsigned char)(i32>>8&0xFF); | 221 | buf[0] = (unsigned char)(i32 & 0xFF); |
223 | buf[2]=(unsigned char)(i32>>16&0xFF); | 222 | buf[1] = (unsigned char)(i32 >> 8 & 0xFF); |
224 | buf[3]=(unsigned char)(i32>>24&0xFF); | 223 | buf[2] = (unsigned char)(i32 >> 16 & 0xFF); |
225 | return fwrite(buf,4,1,file); | 224 | buf[3] = (unsigned char)(i32 >> 24 & 0xFF); |
225 | return fwrite(buf, 4, 1, file); | ||
226 | } | 226 | } |
227 | 227 | ||
228 | 228 | ||
229 | static size_t | 229 | static size_t |
230 | fwrite_le16(int i16, FILE *file) | 230 | fwrite_le16(int i16, FILE *file) |
231 | { | 231 | { |
232 | unsigned char buf[2]; | 232 | unsigned char buf[2]; |
233 | buf[0]=(unsigned char)(i16&0xFF); | 233 | |
234 | buf[1]=(unsigned char)(i16>>8&0xFF); | 234 | buf[0] = (unsigned char)(i16 & 0xFF); |
235 | return fwrite(buf,2,1,file); | 235 | buf[1] = (unsigned char)(i16 >> 8 & 0xFF); |
236 | return fwrite(buf, 2, 1, file); | ||
236 | } | 237 | } |
237 | 238 | ||
238 | 239 | ||
239 | static int | 240 | static int |
240 | write_wav_header() | 241 | write_wav_header() |
241 | { | 242 | { |
242 | int ret; | 243 | int ret; |
243 | FILE *file = stdout; | 244 | FILE *file = stdout; |
244 | 245 | ||
245 | ret = fprintf (file, "RIFF") >= 0; | 246 | ret = fprintf(file, "RIFF") >= 0; |
246 | ret &= fwrite_le32 (0x7fffffff, file); | 247 | ret &= fwrite_le32(0x7fffffff, file); |
247 | 248 | ||
248 | ret &= fprintf (file, "WAVEfmt ") >= 0; | 249 | ret &= fprintf(file, "WAVEfmt ") >= 0; |
249 | ret &= fwrite_le32 (16, file); | 250 | ret &= fwrite_le32(16, file); |
250 | ret &= fwrite_le16 (1, file); | 251 | ret &= fwrite_le16(1, file); |
251 | ret &= fwrite_le16 (channels, file); | 252 | ret &= fwrite_le16(channels, file); |
252 | ret &= fwrite_le32 (SAMPLING_RATE, file); | 253 | ret &= fwrite_le32(SAMPLING_RATE, file); |
253 | ret &= fwrite_le32 (2*channels*SAMPLING_RATE, file); | 254 | ret &= fwrite_le32(2 * channels * SAMPLING_RATE, file); |
254 | ret &= fwrite_le16 (2*channels, file); | 255 | ret &= fwrite_le16(2 * channels, file); |
255 | ret &= fwrite_le16 (16, file); | 256 | ret &= fwrite_le16(16, file); |
256 | 257 | ||
257 | ret &= fprintf (file, "data") >= 0; | 258 | ret &= fprintf(file, "data") >= 0; |
258 | ret &= fwrite_le32 (0x7fffffff, file); | 259 | ret &= fwrite_le32(0x7fffffff, file); |
259 | 260 | ||
260 | return !ret ? -1 : 16; | 261 | return !ret ? -1 : 16; |
261 | } | 262 | } |
262 | 263 | ||
263 | #endif | 264 | #endif |
264 | 265 | ||
265 | 266 | ||
266 | static int64_t | 267 | static int64_t |
267 | audio_write (int64_t maxout) | 268 | audio_write(int64_t maxout) |
268 | { | 269 | { |
269 | int64_t sampout = 0; | 270 | int64_t sampout = 0; |
270 | int tmp_skip; | 271 | int tmp_skip; |
271 | unsigned out_len; | 272 | unsigned out_len; |
272 | unsigned to_write; | 273 | unsigned to_write; |
273 | float *output; | 274 | float *output; |
275 | |||
274 | #ifdef DEBUG_DUMP_DECODED_OGG | 276 | #ifdef DEBUG_DUMP_DECODED_OGG |
275 | static int wrote_wav_header; | 277 | static int wrote_wav_header; |
276 | 278 | ||
277 | if (dump_to_stdout && !wrote_wav_header) | 279 | if (dump_to_stdout && !wrote_wav_header) |
278 | { | 280 | { |
279 | write_wav_header (); | 281 | write_wav_header(); |
280 | wrote_wav_header = 1; | 282 | wrote_wav_header = 1; |
281 | } | 283 | } |
282 | #endif | 284 | #endif |
283 | maxout = 0 > maxout ? 0 : maxout; | 285 | maxout = 0 > maxout ? 0 : maxout; |
284 | do | 286 | do |
285 | { | ||
286 | tmp_skip = (preskip > frame_size) ? (int) frame_size : preskip; | ||
287 | preskip -= tmp_skip; | ||
288 | output = pcm_buffer + channels * tmp_skip; | ||
289 | out_len = frame_size - tmp_skip; | ||
290 | if (out_len > MAX_FRAME_SIZE) | ||
291 | exit (6); | ||
292 | frame_size = 0; | ||
293 | |||
294 | to_write = out_len < maxout ? out_len : (unsigned) maxout; | ||
295 | if (0 < maxout) | ||
296 | { | 287 | { |
297 | int64_t wrote = 0; | 288 | tmp_skip = (preskip > frame_size) ? (int)frame_size : preskip; |
298 | wrote = to_write; | 289 | preskip -= tmp_skip; |
299 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 290 | output = pcm_buffer + channels * tmp_skip; |
300 | "Writing %u * %u * %u = %llu bytes into PA\n", | 291 | out_len = frame_size - tmp_skip; |
301 | to_write, | 292 | if (out_len > MAX_FRAME_SIZE) |
302 | channels, | 293 | exit(6); |
303 | (unsigned int) sizeof (float), | 294 | frame_size = 0; |
304 | (unsigned long long) (to_write * channels * sizeof (float))); | 295 | |
296 | to_write = out_len < maxout ? out_len : (unsigned)maxout; | ||
297 | if (0 < maxout) | ||
298 | { | ||
299 | int64_t wrote = 0; | ||
300 | wrote = to_write; | ||
301 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
302 | "Writing %u * %u * %u = %llu bytes into PA\n", | ||
303 | to_write, | ||
304 | channels, | ||
305 | (unsigned int)sizeof(float), | ||
306 | (unsigned long long)(to_write * channels * sizeof(float))); | ||
305 | #ifdef DEBUG_DUMP_DECODED_OGG | 307 | #ifdef DEBUG_DUMP_DECODED_OGG |
306 | if (dump_to_stdout) | 308 | if (dump_to_stdout) |
307 | { | 309 | { |
308 | # define fminf(_x,_y) ((_x)<(_y)?(_x):(_y)) | 310 | # define fminf(_x, _y) ((_x) < (_y) ? (_x) : (_y)) |
309 | # define fmaxf(_x,_y) ((_x)>(_y)?(_x):(_y)) | 311 | # define fmaxf(_x, _y) ((_x) > (_y) ? (_x) : (_y)) |
310 | # define float2int(flt) ((int)(floor(.5+flt))) | 312 | # define float2int(flt) ((int)(floor(.5 + flt))) |
311 | int i; | 313 | int i; |
312 | int16_t *out = alloca(sizeof(short)*MAX_FRAME_SIZE*channels); | 314 | int16_t *out = alloca(sizeof(short) * MAX_FRAME_SIZE * channels); |
313 | for (i=0;i<(int)out_len*channels;i++) | 315 | for (i = 0; i < (int)out_len * channels; i++) |
314 | out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767))); | 316 | out[i] = (short)float2int(fmaxf(-32768, fminf(output[i] * 32768.f, 32767))); |
315 | 317 | ||
316 | fwrite (out, 2 * channels, out_len<maxout?out_len:maxout, stdout); | 318 | fwrite(out, 2 * channels, out_len < maxout ? out_len : maxout, stdout); |
317 | } | 319 | } |
318 | else | 320 | else |
319 | #endif | 321 | #endif |
320 | if (pa_stream_write | 322 | if (pa_stream_write |
321 | (stream_out, output, to_write * channels * sizeof (float), NULL, 0, | 323 | (stream_out, output, to_write * channels * sizeof(float), NULL, 0, |
322 | PA_SEEK_RELATIVE) < 0) | 324 | PA_SEEK_RELATIVE) < 0) |
323 | { | 325 | { |
324 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 326 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, |
325 | _("pa_stream_write() failed: %s\n"), | 327 | _("pa_stream_write() failed: %s\n"), |
326 | pa_strerror (pa_context_errno (context))); | 328 | pa_strerror(pa_context_errno(context))); |
327 | } | 329 | } |
328 | sampout += wrote; | 330 | sampout += wrote; |
329 | maxout -= wrote; | 331 | maxout -= wrote; |
332 | } | ||
330 | } | 333 | } |
331 | } while (0 < frame_size && 0 < maxout); | 334 | while (0 < frame_size && 0 < maxout); |
332 | 335 | ||
333 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 336 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
334 | "Wrote %" PRId64 " samples\n", | 337 | "Wrote %" PRId64 " samples\n", |
335 | sampout); | 338 | sampout); |
336 | return sampout; | 339 | return sampout; |
337 | } | 340 | } |
338 | 341 | ||
@@ -341,16 +344,16 @@ audio_write (int64_t maxout) | |||
341 | * Pulseaudio shutdown task | 344 | * Pulseaudio shutdown task |
342 | */ | 345 | */ |
343 | static void | 346 | static void |
344 | quit (int ret) | 347 | quit(int ret) |
345 | { | 348 | { |
346 | mainloop_api->quit (mainloop_api, | 349 | mainloop_api->quit(mainloop_api, |
347 | ret); | 350 | ret); |
348 | exit (ret); | 351 | exit(ret); |
349 | } | 352 | } |
350 | 353 | ||
351 | 354 | ||
352 | static void | 355 | static void |
353 | ogg_demux_and_decode () | 356 | ogg_demux_and_decode() |
354 | { | 357 | { |
355 | ogg_page og; | 358 | ogg_page og; |
356 | static int stream_init; | 359 | static int stream_init; |
@@ -365,189 +368,189 @@ ogg_demux_and_decode () | |||
365 | static int total_links; | 368 | static int total_links; |
366 | static int gran_offset; | 369 | static int gran_offset; |
367 | 370 | ||
368 | while (1 == ogg_sync_pageout (&oy, &og)) | 371 | while (1 == ogg_sync_pageout(&oy, &og)) |
369 | { | ||
370 | if (0 == stream_init) | ||
371 | { | 372 | { |
372 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 373 | if (0 == stream_init) |
373 | "Initialized the stream\n"); | ||
374 | ogg_stream_init (&os, ogg_page_serialno (&og)); | ||
375 | stream_init = 1; | ||
376 | } | ||
377 | if (ogg_page_serialno (&og) != os.serialno) | ||
378 | { | ||
379 | /* so all streams are read. */ | ||
380 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
381 | "Re-set serial number\n"); | ||
382 | ogg_stream_reset_serialno (&os, ogg_page_serialno (&og)); | ||
383 | } | ||
384 | /*Add page to the bitstream*/ | ||
385 | ogg_stream_pagein (&os, &og); | ||
386 | page_granule = ogg_page_granulepos (&og); | ||
387 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
388 | "Reading page that ends at %" PRId64 "\n", | ||
389 | page_granule); | ||
390 | /*Extract all available packets*/ | ||
391 | while (1 == ogg_stream_packetout (&os, &op)) | ||
392 | { | ||
393 | /*OggOpus streams are identified by a magic string in the initial | ||
394 | stream header.*/ | ||
395 | if (op.b_o_s && op.bytes >= 8 && !memcmp (op.packet, "OpusHead", 8)) | ||
396 | { | ||
397 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
398 | "Got Opus Header\n"); | ||
399 | if (has_opus_stream && has_tags_packet) | ||
400 | { | ||
401 | /*If we're seeing another BOS OpusHead now it means | ||
402 | the stream is chained without an EOS. | ||
403 | This can easily happen if record helper is terminated unexpectedly. | ||
404 | */ | ||
405 | has_opus_stream = 0; | ||
406 | if (dec) | ||
407 | opus_decoder_destroy (dec); | ||
408 | dec = NULL; | ||
409 | fprintf (stderr, "\nWarning: stream %" PRId64 " ended without EOS and a new stream began.\n", (int64_t) os.serialno); | ||
410 | } | ||
411 | if (!has_opus_stream) | ||
412 | { | 374 | { |
413 | if (packet_count > 0 && opus_serialno == os.serialno) | 375 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
414 | { | 376 | "Initialized the stream\n"); |
415 | fprintf (stderr, "\nError: Apparent chaining without changing serial number (%" PRId64 "==%" PRId64 ").\n", | 377 | ogg_stream_init(&os, ogg_page_serialno(&og)); |
416 | (int64_t) opus_serialno, (int64_t) os.serialno); | 378 | stream_init = 1; |
417 | quit(1); | ||
418 | } | ||
419 | opus_serialno = os.serialno; | ||
420 | has_opus_stream = 1; | ||
421 | has_tags_packet = 0; | ||
422 | link_out = 0; | ||
423 | packet_count = 0; | ||
424 | eos = 0; | ||
425 | total_links++; | ||
426 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
427 | "Got header for stream %" PRId64 ", this is %dth link\n", | ||
428 | (int64_t) opus_serialno, total_links); | ||
429 | } | 379 | } |
430 | else | 380 | if (ogg_page_serialno(&og) != os.serialno) |
431 | { | 381 | { |
432 | fprintf (stderr, "\nWarning: ignoring opus stream %" PRId64 "\n", (int64_t) os.serialno); | 382 | /* so all streams are read. */ |
383 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
384 | "Re-set serial number\n"); | ||
385 | ogg_stream_reset_serialno(&os, ogg_page_serialno(&og)); | ||
433 | } | 386 | } |
434 | } | 387 | /*Add page to the bitstream*/ |
435 | if (!has_opus_stream || os.serialno != opus_serialno) | 388 | ogg_stream_pagein(&os, &og); |
436 | { | 389 | page_granule = ogg_page_granulepos(&og); |
437 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 390 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
438 | "breaking out\n"); | 391 | "Reading page that ends at %" PRId64 "\n", |
439 | break; | 392 | page_granule); |
440 | } | 393 | /*Extract all available packets*/ |
441 | /*If first packet in a logical stream, process the Opus header*/ | 394 | while (1 == ogg_stream_packetout(&os, &op)) |
442 | if (0 == packet_count) | ||
443 | { | ||
444 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
445 | "Decoding header\n"); | ||
446 | dec = process_header (&op); | ||
447 | if (!dec) | ||
448 | quit (1); | ||
449 | |||
450 | if (0 != ogg_stream_packetout (&os, &op) || 255 == og.header[og.header_len - 1]) | ||
451 | { | ||
452 | /*The format specifies that the initial header and tags packets are on their | ||
453 | own pages. To aid implementors in discovering that their files are wrong | ||
454 | we reject them explicitly here. In some player designs files like this would | ||
455 | fail even without an explicit test.*/ | ||
456 | fprintf (stderr, "Extra packets on initial header page. Invalid stream.\n"); | ||
457 | quit (1); | ||
458 | } | ||
459 | |||
460 | /*Remember how many samples at the front we were told to skip | ||
461 | so that we can adjust the timestamp counting.*/ | ||
462 | gran_offset = preskip; | ||
463 | |||
464 | if (! pcm_buffer) | ||
465 | { | 395 | { |
466 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 396 | /*OggOpus streams are identified by a magic string in the initial |
467 | "Allocating %u * %u * %u = %llu bytes of buffer space\n", | 397 | stream header.*/ |
468 | MAX_FRAME_SIZE, | 398 | if (op.b_o_s && op.bytes >= 8 && !memcmp(op.packet, "OpusHead", 8)) |
469 | channels, | 399 | { |
470 | (unsigned int) sizeof (float), | 400 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
471 | (unsigned long long) (MAX_FRAME_SIZE * channels * sizeof (float))); | 401 | "Got Opus Header\n"); |
472 | pcm_buffer = pa_xmalloc (sizeof (float) * MAX_FRAME_SIZE * channels); | 402 | if (has_opus_stream && has_tags_packet) |
403 | { | ||
404 | /*If we're seeing another BOS OpusHead now it means | ||
405 | the stream is chained without an EOS. | ||
406 | This can easily happen if record helper is terminated unexpectedly. | ||
407 | */ | ||
408 | has_opus_stream = 0; | ||
409 | if (dec) | ||
410 | opus_decoder_destroy(dec); | ||
411 | dec = NULL; | ||
412 | fprintf(stderr, "\nWarning: stream %" PRId64 " ended without EOS and a new stream began.\n", (int64_t)os.serialno); | ||
413 | } | ||
414 | if (!has_opus_stream) | ||
415 | { | ||
416 | if (packet_count > 0 && opus_serialno == os.serialno) | ||
417 | { | ||
418 | fprintf(stderr, "\nError: Apparent chaining without changing serial number (%" PRId64 "==%" PRId64 ").\n", | ||
419 | (int64_t)opus_serialno, (int64_t)os.serialno); | ||
420 | quit(1); | ||
421 | } | ||
422 | opus_serialno = os.serialno; | ||
423 | has_opus_stream = 1; | ||
424 | has_tags_packet = 0; | ||
425 | link_out = 0; | ||
426 | packet_count = 0; | ||
427 | eos = 0; | ||
428 | total_links++; | ||
429 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
430 | "Got header for stream %" PRId64 ", this is %dth link\n", | ||
431 | (int64_t)opus_serialno, total_links); | ||
432 | } | ||
433 | else | ||
434 | { | ||
435 | fprintf(stderr, "\nWarning: ignoring opus stream %" PRId64 "\n", (int64_t)os.serialno); | ||
436 | } | ||
437 | } | ||
438 | if (!has_opus_stream || os.serialno != opus_serialno) | ||
439 | { | ||
440 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
441 | "breaking out\n"); | ||
442 | break; | ||
443 | } | ||
444 | /*If first packet in a logical stream, process the Opus header*/ | ||
445 | if (0 == packet_count) | ||
446 | { | ||
447 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
448 | "Decoding header\n"); | ||
449 | dec = process_header(&op); | ||
450 | if (!dec) | ||
451 | quit(1); | ||
452 | |||
453 | if (0 != ogg_stream_packetout(&os, &op) || 255 == og.header[og.header_len - 1]) | ||
454 | { | ||
455 | /*The format specifies that the initial header and tags packets are on their | ||
456 | own pages. To aid implementors in discovering that their files are wrong | ||
457 | we reject them explicitly here. In some player designs files like this would | ||
458 | fail even without an explicit test.*/ | ||
459 | fprintf(stderr, "Extra packets on initial header page. Invalid stream.\n"); | ||
460 | quit(1); | ||
461 | } | ||
462 | |||
463 | /*Remember how many samples at the front we were told to skip | ||
464 | so that we can adjust the timestamp counting.*/ | ||
465 | gran_offset = preskip; | ||
466 | |||
467 | if (!pcm_buffer) | ||
468 | { | ||
469 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
470 | "Allocating %u * %u * %u = %llu bytes of buffer space\n", | ||
471 | MAX_FRAME_SIZE, | ||
472 | channels, | ||
473 | (unsigned int)sizeof(float), | ||
474 | (unsigned long long)(MAX_FRAME_SIZE * channels * sizeof(float))); | ||
475 | pcm_buffer = pa_xmalloc(sizeof(float) * MAX_FRAME_SIZE * channels); | ||
476 | } | ||
477 | } | ||
478 | else if (1 == packet_count) | ||
479 | { | ||
480 | has_tags_packet = 1; | ||
481 | if (0 != ogg_stream_packetout(&os, &op) || 255 == og.header[og.header_len - 1]) | ||
482 | { | ||
483 | fprintf(stderr, "Extra packets on initial tags page. Invalid stream.\n"); | ||
484 | quit(1); | ||
485 | } | ||
486 | } | ||
487 | else | ||
488 | { | ||
489 | int ret; | ||
490 | int64_t maxout; | ||
491 | int64_t outsamp; | ||
492 | |||
493 | /*End of stream condition*/ | ||
494 | if (op.e_o_s && os.serialno == opus_serialno) | ||
495 | { | ||
496 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
497 | "Got EOS\n"); | ||
498 | eos = 1; /* don't care for anything except opus eos */ | ||
499 | } | ||
500 | |||
501 | /*Decode Opus packet*/ | ||
502 | ret = opus_decode_float(dec, | ||
503 | (const unsigned char *)op.packet, | ||
504 | op.bytes, | ||
505 | pcm_buffer, | ||
506 | MAX_FRAME_SIZE, 0); | ||
507 | |||
508 | /*If the decoder returned less than zero, we have an error.*/ | ||
509 | if (0 > ret) | ||
510 | { | ||
511 | fprintf(stderr, "Decoding error: %s\n", opus_strerror(ret)); | ||
512 | break; | ||
513 | } | ||
514 | frame_size = ret; | ||
515 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
516 | "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n", | ||
517 | ret, | ||
518 | ret * channels, | ||
519 | (unsigned int)op.bytes); | ||
520 | |||
521 | /*Apply header gain, if we're not using an opus library new | ||
522 | enough to do this internally.*/ | ||
523 | if (0 != gain) | ||
524 | { | ||
525 | int i; | ||
526 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
527 | "Applying gain %f\n", | ||
528 | gain); | ||
529 | for (i = 0; i < frame_size * channels; i++) | ||
530 | pcm_buffer[i] *= gain; | ||
531 | } | ||
532 | |||
533 | /*This handles making sure that our output duration respects | ||
534 | the final end-trim by not letting the output sample count | ||
535 | get ahead of the granpos indicated value.*/ | ||
536 | maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000) - link_out; | ||
537 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
538 | "Writing audio packet %" PRId64 ", at most %" PRId64 " samples\n", | ||
539 | packet_count, maxout); | ||
540 | |||
541 | outsamp = audio_write(0 > maxout ? 0 : maxout); | ||
542 | link_out += outsamp; | ||
543 | } | ||
544 | packet_count++; | ||
473 | } | 545 | } |
474 | } | 546 | if (eos) |
475 | else if (1 == packet_count) | ||
476 | { | ||
477 | has_tags_packet = 1; | ||
478 | if (0 != ogg_stream_packetout (&os, &op) || 255 == og.header[og.header_len - 1]) | ||
479 | { | 547 | { |
480 | fprintf (stderr, "Extra packets on initial tags page. Invalid stream.\n"); | 548 | has_opus_stream = 0; |
481 | quit (1); | 549 | if (dec) |
482 | } | 550 | opus_decoder_destroy(dec); |
483 | } | 551 | dec = NULL; |
484 | else | ||
485 | { | ||
486 | int ret; | ||
487 | int64_t maxout; | ||
488 | int64_t outsamp; | ||
489 | |||
490 | /*End of stream condition*/ | ||
491 | if (op.e_o_s && os.serialno == opus_serialno) | ||
492 | { | ||
493 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
494 | "Got EOS\n"); | ||
495 | eos = 1; /* don't care for anything except opus eos */ | ||
496 | } | ||
497 | |||
498 | /*Decode Opus packet*/ | ||
499 | ret = opus_decode_float (dec, | ||
500 | (const unsigned char *) op.packet, | ||
501 | op.bytes, | ||
502 | pcm_buffer, | ||
503 | MAX_FRAME_SIZE, 0); | ||
504 | |||
505 | /*If the decoder returned less than zero, we have an error.*/ | ||
506 | if (0 > ret) | ||
507 | { | ||
508 | fprintf (stderr, "Decoding error: %s\n", opus_strerror (ret)); | ||
509 | break; | ||
510 | } | ||
511 | frame_size = ret; | ||
512 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
513 | "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n", | ||
514 | ret, | ||
515 | ret * channels, | ||
516 | (unsigned int) op.bytes); | ||
517 | |||
518 | /*Apply header gain, if we're not using an opus library new | ||
519 | enough to do this internally.*/ | ||
520 | if (0 != gain) | ||
521 | { | ||
522 | int i; | ||
523 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
524 | "Applying gain %f\n", | ||
525 | gain); | ||
526 | for (i = 0; i < frame_size * channels; i++) | ||
527 | pcm_buffer[i] *= gain; | ||
528 | } | 552 | } |
529 | |||
530 | /*This handles making sure that our output duration respects | ||
531 | the final end-trim by not letting the output sample count | ||
532 | get ahead of the granpos indicated value.*/ | ||
533 | maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000) - link_out; | ||
534 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
535 | "Writing audio packet %" PRId64 ", at most %" PRId64 " samples\n", | ||
536 | packet_count, maxout); | ||
537 | |||
538 | outsamp = audio_write (0 > maxout ? 0 : maxout); | ||
539 | link_out += outsamp; | ||
540 | } | ||
541 | packet_count++; | ||
542 | } | ||
543 | if (eos) | ||
544 | { | ||
545 | has_opus_stream = 0; | ||
546 | if (dec) | ||
547 | opus_decoder_destroy (dec); | ||
548 | dec = NULL; | ||
549 | } | 553 | } |
550 | } | ||
551 | } | 554 | } |
552 | 555 | ||
553 | 556 | ||
@@ -560,31 +563,32 @@ ogg_demux_and_decode () | |||
560 | * #GNUNET_SYSERR to stop further processing due to error | 563 | * #GNUNET_SYSERR to stop further processing due to error |
561 | */ | 564 | */ |
562 | static int | 565 | static int |
563 | stdin_receiver (void *cls, | 566 | stdin_receiver(void *cls, |
564 | const struct GNUNET_MessageHeader *msg) | 567 | const struct GNUNET_MessageHeader *msg) |
565 | { | 568 | { |
566 | struct AudioMessage *audio; | 569 | struct AudioMessage *audio; |
567 | char *data; | 570 | char *data; |
568 | size_t payload_len; | 571 | size_t payload_len; |
569 | 572 | ||
570 | (void) cls; | 573 | (void)cls; |
571 | switch (ntohs (msg->type)) | 574 | switch (ntohs(msg->type)) |
572 | { | 575 | { |
573 | case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO: | 576 | case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO: |
574 | audio = (struct AudioMessage *) msg; | 577 | audio = (struct AudioMessage *)msg; |
575 | payload_len = ntohs (audio->header.size) - sizeof (struct AudioMessage); | 578 | payload_len = ntohs(audio->header.size) - sizeof(struct AudioMessage); |
576 | 579 | ||
577 | /*Get the ogg buffer for writing*/ | 580 | /*Get the ogg buffer for writing*/ |
578 | data = ogg_sync_buffer (&oy, payload_len); | 581 | data = ogg_sync_buffer(&oy, payload_len); |
579 | /*Read bitstream from input file*/ | 582 | /*Read bitstream from input file*/ |
580 | GNUNET_memcpy (data, (const unsigned char *) &audio[1], payload_len); | 583 | GNUNET_memcpy(data, (const unsigned char *)&audio[1], payload_len); |
581 | ogg_sync_wrote (&oy, payload_len); | 584 | ogg_sync_wrote(&oy, payload_len); |
582 | 585 | ||
583 | ogg_demux_and_decode (); | 586 | ogg_demux_and_decode(); |
584 | break; | 587 | break; |
585 | default: | 588 | |
586 | break; | 589 | default: |
587 | } | 590 | break; |
591 | } | ||
588 | return GNUNET_OK; | 592 | return GNUNET_OK; |
589 | } | 593 | } |
590 | 594 | ||
@@ -593,20 +597,20 @@ stdin_receiver (void *cls, | |||
593 | * Callback when data is there for playback | 597 | * Callback when data is there for playback |
594 | */ | 598 | */ |
595 | static void | 599 | static void |
596 | stream_write_callback (pa_stream *s, | 600 | stream_write_callback(pa_stream *s, |
597 | size_t length, | 601 | size_t length, |
598 | void *userdata) | 602 | void *userdata) |
599 | { | 603 | { |
600 | /* unblock 'main' */ | 604 | /* unblock 'main' */ |
601 | (void) userdata; | 605 | (void)userdata; |
602 | (void) length; | 606 | (void)length; |
603 | (void) s; | 607 | (void)s; |
604 | if (-1 != ready_pipe[1]) | 608 | if (-1 != ready_pipe[1]) |
605 | { | 609 | { |
606 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 610 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
607 | "Unblocking main loop!\n"); | 611 | "Unblocking main loop!\n"); |
608 | (void) write (ready_pipe[1], "r", 1); | 612 | (void)write(ready_pipe[1], "r", 1); |
609 | } | 613 | } |
610 | } | 614 | } |
611 | 615 | ||
612 | 616 | ||
@@ -614,18 +618,18 @@ stream_write_callback (pa_stream *s, | |||
614 | * Exit callback for SIGTERM and SIGINT | 618 | * Exit callback for SIGTERM and SIGINT |
615 | */ | 619 | */ |
616 | static void | 620 | static void |
617 | exit_signal_callback (pa_mainloop_api *m, | 621 | exit_signal_callback(pa_mainloop_api *m, |
618 | pa_signal_event *e, | 622 | pa_signal_event *e, |
619 | int sig, | 623 | int sig, |
620 | void *userdata) | 624 | void *userdata) |
621 | { | 625 | { |
622 | (void) m; | 626 | (void)m; |
623 | (void) e; | 627 | (void)e; |
624 | (void) sig; | 628 | (void)sig; |
625 | (void) userdata; | 629 | (void)userdata; |
626 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 630 | GNUNET_log(GNUNET_ERROR_TYPE_INFO, |
627 | _("gnunet-helper-audio-playback - Got signal, exiting\n")); | 631 | _("gnunet-helper-audio-playback - Got signal, exiting\n")); |
628 | quit (1); | 632 | quit(1); |
629 | } | 633 | } |
630 | 634 | ||
631 | 635 | ||
@@ -633,62 +637,64 @@ exit_signal_callback (pa_mainloop_api *m, | |||
633 | * Pulseaudio stream state callback | 637 | * Pulseaudio stream state callback |
634 | */ | 638 | */ |
635 | static void | 639 | static void |
636 | context_state_callback (pa_context *c, | 640 | context_state_callback(pa_context *c, |
637 | void *userdata) | 641 | void *userdata) |
638 | { | 642 | { |
639 | int p; | 643 | int p; |
640 | 644 | ||
641 | (void) userdata; | 645 | (void)userdata; |
642 | GNUNET_assert (NULL != c); | 646 | GNUNET_assert(NULL != c); |
643 | switch (pa_context_get_state (c)) | 647 | switch (pa_context_get_state(c)) |
644 | { | ||
645 | case PA_CONTEXT_CONNECTING: | ||
646 | case PA_CONTEXT_AUTHORIZING: | ||
647 | case PA_CONTEXT_SETTING_NAME: | ||
648 | break; | ||
649 | case PA_CONTEXT_READY: | ||
650 | { | ||
651 | GNUNET_assert (! stream_out); | ||
652 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
653 | _("Connection established.\n")); | ||
654 | if (! (stream_out = | ||
655 | pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL))) | ||
656 | { | 648 | { |
657 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 649 | case PA_CONTEXT_CONNECTING: |
658 | _("pa_stream_new() failed: %s\n"), | 650 | case PA_CONTEXT_AUTHORIZING: |
659 | pa_strerror (pa_context_errno (c))); | 651 | case PA_CONTEXT_SETTING_NAME: |
660 | goto fail; | 652 | break; |
661 | } | 653 | |
662 | pa_stream_set_write_callback (stream_out, | 654 | case PA_CONTEXT_READY: |
663 | &stream_write_callback, | ||
664 | NULL); | ||
665 | if ((p = | ||
666 | pa_stream_connect_playback (stream_out, NULL, | ||
667 | NULL, | ||
668 | PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE, | ||
669 | NULL, NULL)) < 0) | ||
670 | { | 655 | { |
671 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 656 | GNUNET_assert(!stream_out); |
672 | _("pa_stream_connect_playback() failed: %s\n"), | 657 | GNUNET_log(GNUNET_ERROR_TYPE_INFO, |
673 | pa_strerror (pa_context_errno (c))); | 658 | _("Connection established.\n")); |
659 | if (!(stream_out = | ||
660 | pa_stream_new(c, "GNUNET VoIP playback", &sample_spec, NULL))) | ||
661 | { | ||
662 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, | ||
663 | _("pa_stream_new() failed: %s\n"), | ||
664 | pa_strerror(pa_context_errno(c))); | ||
665 | goto fail; | ||
666 | } | ||
667 | pa_stream_set_write_callback(stream_out, | ||
668 | &stream_write_callback, | ||
669 | NULL); | ||
670 | if ((p = | ||
671 | pa_stream_connect_playback(stream_out, NULL, | ||
672 | NULL, | ||
673 | PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE, | ||
674 | NULL, NULL)) < 0) | ||
675 | { | ||
676 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, | ||
677 | _("pa_stream_connect_playback() failed: %s\n"), | ||
678 | pa_strerror(pa_context_errno(c))); | ||
679 | goto fail; | ||
680 | } | ||
681 | break; | ||
682 | } | ||
683 | |||
684 | case PA_CONTEXT_TERMINATED: | ||
685 | quit(0); | ||
686 | break; | ||
687 | |||
688 | case PA_CONTEXT_FAILED: | ||
689 | default: | ||
690 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, | ||
691 | _("Connection failure: %s\n"), | ||
692 | pa_strerror(pa_context_errno(c))); | ||
674 | goto fail; | 693 | goto fail; |
675 | } | 694 | } |
676 | break; | ||
677 | } | ||
678 | case PA_CONTEXT_TERMINATED: | ||
679 | quit (0); | ||
680 | break; | ||
681 | |||
682 | case PA_CONTEXT_FAILED: | ||
683 | default: | ||
684 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
685 | _("Connection failure: %s\n"), | ||
686 | pa_strerror (pa_context_errno (c))); | ||
687 | goto fail; | ||
688 | } | ||
689 | return; | 695 | return; |
690 | fail: | 696 | fail: |
691 | quit (1); | 697 | quit(1); |
692 | } | 698 | } |
693 | 699 | ||
694 | 700 | ||
@@ -696,66 +702,66 @@ context_state_callback (pa_context *c, | |||
696 | * Pulseaudio initialization | 702 | * Pulseaudio initialization |
697 | */ | 703 | */ |
698 | static void | 704 | static void |
699 | pa_init () | 705 | pa_init() |
700 | { | 706 | { |
701 | int r; | 707 | int r; |
702 | 708 | ||
703 | if (!pa_sample_spec_valid (&sample_spec)) | 709 | if (!pa_sample_spec_valid(&sample_spec)) |
704 | { | 710 | { |
705 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 711 | GNUNET_log(GNUNET_ERROR_TYPE_INFO, |
706 | _("Wrong Spec\n")); | 712 | _("Wrong Spec\n")); |
707 | } | 713 | } |
708 | /* set up threaded playback mainloop */ | 714 | /* set up threaded playback mainloop */ |
709 | if (!(m = pa_threaded_mainloop_new ())) | 715 | if (!(m = pa_threaded_mainloop_new())) |
710 | { | 716 | { |
711 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 717 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, |
712 | _("pa_mainloop_new() failed.\n")); | 718 | _("pa_mainloop_new() failed.\n")); |
713 | } | 719 | } |
714 | mainloop_api = pa_threaded_mainloop_get_api (m); | 720 | mainloop_api = pa_threaded_mainloop_get_api(m); |
715 | /* listen to signals */ | 721 | /* listen to signals */ |
716 | r = pa_signal_init (mainloop_api); | 722 | r = pa_signal_init(mainloop_api); |
717 | GNUNET_assert (r == 0); | 723 | GNUNET_assert(r == 0); |
718 | pa_signal_new (SIGINT, exit_signal_callback, NULL); | 724 | pa_signal_new(SIGINT, exit_signal_callback, NULL); |
719 | pa_signal_new (SIGTERM, exit_signal_callback, NULL); | 725 | pa_signal_new(SIGTERM, exit_signal_callback, NULL); |
720 | 726 | ||
721 | 727 | ||
722 | /* connect to the main pulseaudio context */ | 728 | /* connect to the main pulseaudio context */ |
723 | if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP"))) | 729 | if (!(context = pa_context_new(mainloop_api, "GNUnet VoIP"))) |
724 | { | 730 | { |
725 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 731 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, |
726 | _("pa_context_new() failed.\n")); | 732 | _("pa_context_new() failed.\n")); |
727 | } | 733 | } |
728 | pa_context_set_state_callback (context, context_state_callback, NULL); | 734 | pa_context_set_state_callback(context, context_state_callback, NULL); |
729 | 735 | ||
730 | if (pa_context_connect (context, NULL, 0, NULL) < 0) | 736 | if (pa_context_connect(context, NULL, 0, NULL) < 0) |
731 | { | 737 | { |
732 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 738 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, |
733 | _("pa_context_connect() failed: %s\n"), | 739 | _("pa_context_connect() failed: %s\n"), |
734 | pa_strerror (pa_context_errno (context))); | 740 | pa_strerror(pa_context_errno(context))); |
735 | } | 741 | } |
736 | if (pa_threaded_mainloop_start (m) < 0) | 742 | if (pa_threaded_mainloop_start(m) < 0) |
737 | { | 743 | { |
738 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 744 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, |
739 | _("pa_mainloop_run() failed.\n")); | 745 | _("pa_mainloop_run() failed.\n")); |
740 | } | 746 | } |
741 | } | 747 | } |
742 | 748 | ||
743 | 749 | ||
744 | static void | 750 | static void |
745 | ogg_init () | 751 | ogg_init() |
746 | { | 752 | { |
747 | ogg_sync_init (&oy); | 753 | ogg_sync_init(&oy); |
748 | } | 754 | } |
749 | 755 | ||
750 | 756 | ||
751 | static void | 757 | static void |
752 | drain_callback (pa_stream*s, int success, void *userdata) | 758 | drain_callback(pa_stream*s, int success, void *userdata) |
753 | { | 759 | { |
754 | (void) s; | 760 | (void)s; |
755 | (void) success; | 761 | (void)success; |
756 | (void) userdata; | 762 | (void)userdata; |
757 | pa_threaded_mainloop_signal (m, | 763 | pa_threaded_mainloop_signal(m, |
758 | 0); | 764 | 0); |
759 | } | 765 | } |
760 | 766 | ||
761 | 767 | ||
@@ -767,95 +773,96 @@ drain_callback (pa_stream*s, int success, void *userdata) | |||
767 | * @return 0 ok, 1 on error | 773 | * @return 0 ok, 1 on error |
768 | */ | 774 | */ |
769 | int | 775 | int |
770 | main (int argc, char *argv[]) | 776 | main(int argc, char *argv[]) |
771 | { | 777 | { |
772 | static unsigned long long toff; | 778 | static unsigned long long toff; |
773 | char readbuf[MAXLINE]; | 779 | char readbuf[MAXLINE]; |
774 | struct GNUNET_MessageStreamTokenizer *stdin_mst; | 780 | struct GNUNET_MessageStreamTokenizer *stdin_mst; |
775 | char c; | 781 | char c; |
776 | ssize_t ret; | 782 | ssize_t ret; |
783 | |||
777 | #ifdef DEBUG_READ_PURE_OGG | 784 | #ifdef DEBUG_READ_PURE_OGG |
778 | int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0; | 785 | int read_pure_ogg = getenv("GNUNET_READ_PURE_OGG") ? 1 : 0; |
779 | #endif | 786 | #endif |
780 | 787 | ||
781 | (void) argc; | 788 | (void)argc; |
782 | (void) argv; | 789 | (void)argv; |
783 | GNUNET_assert (GNUNET_OK == | 790 | GNUNET_assert(GNUNET_OK == |
784 | GNUNET_log_setup ("gnunet-helper-audio-playback", | 791 | GNUNET_log_setup("gnunet-helper-audio-playback", |
785 | "WARNING", | 792 | "WARNING", |
786 | NULL)); | 793 | NULL)); |
787 | if (0 != pipe (ready_pipe)) | 794 | if (0 != pipe(ready_pipe)) |
788 | { | 795 | { |
789 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe"); | 796 | GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "pipe"); |
790 | return 1; | 797 | return 1; |
791 | } | 798 | } |
792 | stdin_mst = GNUNET_MST_create (&stdin_receiver, NULL); | 799 | stdin_mst = GNUNET_MST_create(&stdin_receiver, NULL); |
793 | ogg_init (); | 800 | ogg_init(); |
794 | pa_init (); | 801 | pa_init(); |
795 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 802 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
796 | "Waiting for PulseAudio to be ready.\n"); | 803 | "Waiting for PulseAudio to be ready.\n"); |
797 | GNUNET_assert (1 == read (ready_pipe[0], &c, 1)); | 804 | GNUNET_assert(1 == read(ready_pipe[0], &c, 1)); |
798 | close (ready_pipe[0]); | 805 | close(ready_pipe[0]); |
799 | close (ready_pipe[1]); | 806 | close(ready_pipe[1]); |
800 | ready_pipe[0] = -1; | 807 | ready_pipe[0] = -1; |
801 | ready_pipe[1] = -1; | 808 | ready_pipe[1] = -1; |
802 | #ifdef DEBUG_DUMP_DECODED_OGG | 809 | #ifdef DEBUG_DUMP_DECODED_OGG |
803 | dump_to_stdout = getenv ("GNUNET_DUMP_DECODED_OGG") ? 1 : 0; | 810 | dump_to_stdout = getenv("GNUNET_DUMP_DECODED_OGG") ? 1 : 0; |
804 | #endif | 811 | #endif |
805 | while (1) | 812 | while (1) |
806 | { | ||
807 | ret = read (STDIN_FILENO, | ||
808 | readbuf, | ||
809 | sizeof (readbuf)); | ||
810 | toff += ret; | ||
811 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
812 | "Received %d bytes of audio data (total: %llu)\n", | ||
813 | (int) ret, | ||
814 | toff); | ||
815 | if (0 > ret) | ||
816 | { | 813 | { |
817 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 814 | ret = read(STDIN_FILENO, |
818 | _("Read error from STDIN: %s\n"), | 815 | readbuf, |
819 | strerror (errno)); | 816 | sizeof(readbuf)); |
820 | break; | 817 | toff += ret; |
821 | } | 818 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
822 | if (0 == ret) | 819 | "Received %d bytes of audio data (total: %llu)\n", |
823 | break; | 820 | (int)ret, |
821 | toff); | ||
822 | if (0 > ret) | ||
823 | { | ||
824 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, | ||
825 | _("Read error from STDIN: %s\n"), | ||
826 | strerror(errno)); | ||
827 | break; | ||
828 | } | ||
829 | if (0 == ret) | ||
830 | break; | ||
824 | #ifdef DEBUG_READ_PURE_OGG | 831 | #ifdef DEBUG_READ_PURE_OGG |
825 | if (read_pure_ogg) | 832 | if (read_pure_ogg) |
826 | { | 833 | { |
827 | char *data = ogg_sync_buffer (&oy, ret); | 834 | char *data = ogg_sync_buffer(&oy, ret); |
828 | GNUNET_memcpy (data, readbuf, ret); | 835 | GNUNET_memcpy(data, readbuf, ret); |
829 | ogg_sync_wrote (&oy, ret); | 836 | ogg_sync_wrote(&oy, ret); |
830 | ogg_demux_and_decode (); | 837 | ogg_demux_and_decode(); |
831 | } | 838 | } |
832 | else | 839 | else |
833 | #endif | 840 | #endif |
834 | GNUNET_MST_from_buffer (stdin_mst, | 841 | GNUNET_MST_from_buffer(stdin_mst, |
835 | readbuf, ret, | 842 | readbuf, ret, |
836 | GNUNET_NO, GNUNET_NO); | 843 | GNUNET_NO, GNUNET_NO); |
837 | } | 844 | } |
838 | GNUNET_MST_destroy (stdin_mst); | 845 | GNUNET_MST_destroy(stdin_mst); |
839 | if (stream_out) | 846 | if (stream_out) |
840 | { | ||
841 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
842 | "Locking\n"); | ||
843 | pa_threaded_mainloop_lock (m); | ||
844 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
845 | "Draining\n"); | ||
846 | pa_operation *o = pa_stream_drain (stream_out, drain_callback, NULL); | ||
847 | while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) | ||
848 | { | 847 | { |
849 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 848 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, |
850 | "Waiting\n"); | 849 | "Locking\n"); |
851 | pa_threaded_mainloop_wait (m); | 850 | pa_threaded_mainloop_lock(m); |
851 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
852 | "Draining\n"); | ||
853 | pa_operation *o = pa_stream_drain(stream_out, drain_callback, NULL); | ||
854 | while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) | ||
855 | { | ||
856 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
857 | "Waiting\n"); | ||
858 | pa_threaded_mainloop_wait(m); | ||
859 | } | ||
860 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
861 | "Unreffing\n"); | ||
862 | pa_operation_unref(o); | ||
863 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, | ||
864 | "Unlocking\n"); | ||
865 | pa_threaded_mainloop_unlock(m); | ||
852 | } | 866 | } |
853 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
854 | "Unreffing\n"); | ||
855 | pa_operation_unref (o); | ||
856 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
857 | "Unlocking\n"); | ||
858 | pa_threaded_mainloop_unlock (m); | ||
859 | } | ||
860 | return 0; | 867 | return 0; |
861 | } | 868 | } |