source: gutenbach/debian/lib/sipbmp3-filter @ 53d7625

debianmacno-cupsnodebathenaweb
Last change on this file since 53d7625 was 53d7625, checked in by jtwang <jtwang>, 21 years ago

Removed output buffer when playing MPEG streams because files seem to
be cut by mpg123 if transfer cannot keep up with the file bitrate when
output buffer is enabled.

Added code to handle shoutcast playlists (Content-Type: audio/x-scpls)

  • Property mode set to 100755
File size: 18.1 KB
Line 
1#!/usr/athena/bin/perl
2# Play the data on STDIN as an audio file
3#
4# $Id: sipbmp3-filter,v 1.11 2003-10-24 20:31:42 jtwang Exp $
5# $Source: /tmp/tmp.UFBNno9997/RCS/sipbmp3-filter,v $
6#
7# TODO
8# ----
9# Make this structured code. It's a mess.
10# Repeat what we just played for EXT files too
11# Support HTTP Auth on ogg streams
12# License, cleanup and package
13#
14# Jered Floyd <jered@mit.edu> takes very little credit for this code
15
16use Getopt::Std;
17
18# Get the MP3Info module from this directory, because I suck.
19unshift(@INC, "/usr/local/bin");
20require MP3::Info;
21import MP3::Info;
22&use_winamp_genres();
23
24print STDERR "STDERR FROM SPOOL FILTER\n";
25
26# set real uid to be effective uid
27$< = $>;
28
29# Attach necessary lockers
30system("/bin/athena/attach -h -n -q infoagents sipb outland ' .
31    'consult 2>&1 > /dev/null");
32
33# Select the correct output device and set the volume
34system("/usr/local/bin/audio_setdevice -out headphones 100 ' .
35    '2>&1 </dev/null > /dev/null");
36
37# The command line we get from lpd is (no spaces between options and args):
38#  -C lpr -C class
39#  -A LPRng internal identifier
40#  -H originating host
41#  -J lpr -J jobname (default: list of files)
42#  -L lpr -U username
43#  -P logname
44#  -Q queuename (lpr -Q)
45#  -Z random user-specified options
46#  -a printcap af (accounting file name)
47#  -d printcap sd entry (spool dir)
48#  -e print job data file name (currently being processed)
49#  -h print job originiating host (same as -H)
50#  -j job number in spool queue
51#  -k print job control file name
52#  -l printcap pl (page length)
53#  -n user name (same as -L)
54#  -s printcap sf (status file)
55#  -w printcap pw (page width)
56#  -x printcap px (page x dimension)
57#  -y printcap py (page y dimension)
58# accounting file name
59
60# All the filter_options from lpd
61getopt('ACFHJLPQRZacdefhijklnprswxy', \%opts);
62
63# Status messages at start of playback
64open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i ' .
65  'sipbmp3@xcb -s "SIPB LPR music spooler"');
66print(ZEPHYR "User $opts{'n'} on host $opts{'H'} is playing:\n");
67
68# So, the file we're currently processing is "-d/-e".
69# Do some magic to make sure it's an MP3, and get the important bits
70# to zephyr out.
71open(DATAFILE, "$opts{'d'}/$opts{'e'}");
72sysread(DATAFILE, $magic, 3);
73close(DATAFILE);
74
75# MPEG header is beshort &0xffe0
76# meditate upon the header:
77($magic0, $magic1, @magic2) = unpack("C3", $magic);
78if ((($magic0 & 0xff) == 0xff) &&
79    (($magic1 & 0xe0) == 0xe0)) {
80  # MPEG audio file, we like it
81  # Fall through
82} elsif ($magic eq "ID3") {
83  # ID3 v2 does sketchy things with "garbage" beginning with ID3 at file start.
84  # MPEG audio file with ID3 tag, we like it
85  # Fall through
86} elsif ($magic eq "EXT") {
87  # This is an external stream reference (a jered-special)
88  &play_external_reference(\%opts, $magic);
89  exit 0;
90} elsif ($magic eq "htt") {
91  # This is an external stream reference (a jered-special)
92  &play_external_reference(\%opts, $magic);
93  exit 0;
94} elsif ($magic eq "Ogg") {
95  # Ogg-Vorbis audio file
96  &play_ogg_audio(\%opts);
97  exit 0;
98} else {
99    # add more cases later, whine for now
100    printf(ZEPHYR "I don't think this is an MPEG audio file... %02x %02x\n",
101           $magic0, $magic1);
102    print ZEPHYR "I'm going to try playing it as one anyway.\n";
103}
104# Default
105&play_mpeg_audio(\%opts);
106
107
108# Play an external stream reference
109sub play_external_reference {
110    # Retrieve those command line opts.
111    my %opts = %{shift(@_)};
112    my $magic = shift(@_);
113
114    # External references are *not* playlists; they only support
115    # a single URL in order to be fair.  A reference is structured as
116    # follows:
117    #
118    # EXT <format>\n
119    # <URI>[ <user:pass>]\n
120    # Descriptive text of the file that will be\n
121    # played; this may span multiple lines until the\n
122    # EOF
123    #
124    # Where <URI> is a valid URI for the stream, and <format> is a
125    # recognized format name (currently 'MP3' or 'OGG' or 'SHOUTCAST').
126    # <user:pass> is an optional user and password pair for HTTP Basic Auth.
127    my $format, $uri, $userpass;
128
129    if ("$magic" eq "EXT") {
130      if (<STDIN> =~ /^EXT (.*)$/) {
131        # Found the header
132        $format = $1;
133      } else {
134        print ZEPHYR "Couldn't read EXT header\n";
135        close(ZEPHYR);
136      }
137 
138      if (<STDIN> =~ /^(\S*)\s*(.*)$/) {
139        # Found the URI (and optionally user:pass)
140        $uri = $1;
141        if ($2) {
142        $userpass = $2;
143        }
144      } else {
145        print ZEPHYR "Couldn't read URI for external reference\n";
146        close(ZEPHYR);
147      }
148  } elsif ("$magic" eq "htt") {
149      if (<STDIN> =~ /^(\S*)\s*(.*)$/) {
150          $uri=$1;
151          my $pid = open(W3M, "-|");
152          unless (defined $pid) {
153              print ZEPHYR "Couldn't fork: $!\n";
154              close(ZEPHYR);
155              die;
156          }
157
158          if ($pid) { #parent
159              $contenttype="unknown";
160              while (<W3M>) {
161                  if ($_ =~ /^Content-Type:\s(\S*)/) {
162                      $contenttype=$1;
163                  }
164              }
165              if ($contenttype eq "audio/mpeg") { $format="MP3" }
166              elsif ($contenttype eq "application/x-ogg") { $format="OGG" }
167              elsif ($contenttype eq "application/ogg") { $format="OGG" }
168              elsif ($contenttype eq "audio/x-scpls") { $format="SHOUTCAST" }
169              else {
170                  print ZEPHYR
171                      "Unknown Content-Type $contenttype for URI $uri\n";
172                  close(ZEPHYR);
173              }
174              close(W3M) || print ZEPHYR "w3m exited $?\n";
175          }
176          else { # child
177              exec("/mit/sipb/bin/w3m", "-dump_head", $uri)
178                  || die "Couldn't exec";
179          }
180      } else {
181        print ZEPHYR "Couldn't read URI for external reference\n";
182        close(ZEPHYR);
183    }
184  } else {
185      print ZEPHYR "Unknown syntax in play_external_reference(): $magic\n";
186      close(ZEPHYR);
187  }
188
189    # Echo the rest to the zephyr
190    while (<STDIN>) {
191      print ZEPHYR $_;
192    }
193    print ZEPHYR "\n";
194
195    # Play the thing the right way
196    if (($format eq "MP3") ||
197        ($format eq "mp3")) {
198      print ZEPHYR "MP3 audio stream...\n";
199      close(ZEPHYR);
200      &play_mpeg_stream($uri, $userpass);
201    }
202    elsif ($format eq "SHOUTCAST") {
203        print ZEPHYR "Shoutcast playlist...\n";
204        #Don't close ZEPHYR yet, will print the name of the stream if available
205        $uri = &get_shoutcast($uri);
206        close(ZEPHYR);
207        &play_mpeg_stream($uri, undef);
208    }   
209    elsif (($format eq "OGG") ||
210             ($format eq "ogg")) {
211      print ZEPHYR "OggVorbis stream...\n";
212      close(ZEPHYR);
213      &play_ogg_stream($uri, $userpass);
214    } else {
215      print ZEPHYR "Unrecognized stream format: $format\n";
216      close(ZEPHYR);
217    }
218}
219
220# Process a Shoutcast playlist
221# get_shoutcast(URI)
222sub get_shoutcast {
223  my $uri = shift(@_);
224 
225  # grab the shoutcast .pls file
226  my $pid = open(W3M, "-|");
227  unless (defined $pid) {
228      print ZEPHYR "Couldn't fork: $!\n";
229      close(ZEPHYR);
230      die;
231  }
232  if (!$pid) { # child
233      exec("/mit/sipb/bin/w3m","-dump_source",$uri) || die "Couldn't fork";
234  }
235  while (<W3M>) {
236      if (/^File\d+=(\S+)/) {
237          push(@uris, $1);
238      }
239      if (/^Title\d+=(.+)$/) {
240          push(@titles, $1);
241      }
242  }
243  close(W3M);
244 
245  # choose a random server
246  $server = int(rand scalar(@uris));
247  # print the name of the stream if available
248  print ZEPHYR "$titles[$server]\n";
249  return $uris[$server];
250}
251
252# Play an MPEG audio stream
253# play_mpeg_stream(URI, userpass)
254sub play_mpeg_stream {
255  my $uri = shift(@_);
256  my $userpass = shift(@_);
257
258  # Add the user:pass argument if is was given to us
259  my $up = '';
260  if ($userpass) {
261    $up = '-u ' . $userpass;
262  }
263
264  system("ps ax | grep ogg123 | awk '{print $2}' | xargs kill -9");
265  system("ps ax | grep mpg123 | awk '{print $2}' | xargs kill -9");
266  system("chmod a+rw /dev/audio");
267
268  # Prepare to write status:
269  open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i ' .
270       'sipbmp3@xcb -s "SIPB LPR music spooler"');
271     
272  # fork for mpg123
273  my $pid = open(MP3STATUS, "-|");
274  unless (defined $pid) {
275      print ZEPHYR "Couldn't fork: $!\n";
276      close(ZEPHYR);
277      return;
278  }
279 
280  if ($pid) { #parent
281      # Check if there were any errors
282      if ($_ = <MP3STATUS>) {
283          print ZEPHYR "Playback completed with the following errors:\n";
284          print ZEPHYR $_;
285          while (<MP3STATUS>) {
286              print ZEPHYR $_;
287          }
288      } else {
289          print ZEPHYR "Playback completed successfully.\n";
290      }
291      close(MP3STATUS) || print ZEPHYR "mpg123 exited $?\n";
292     
293      close(ZEPHYR);
294  }
295  else { # child
296      # redirect STDERR to STDOUT
297      open STDERR, '>&STDOUT';
298
299      # disabling the buffer seems to improve streaming performances
300      # (files don't get cut in the middle) - perhaps mpg123 fails to
301      # wait for OS TCP buffer to be filled. -- jtwang 22 Oct 2003
302      #exec("/mit/infoagents/bin/mpg123", "-b16384", "-q", $up, $uri ||
303      #   die "Couldn't exec";
304      exec("/mit/infoagents/bin/mpg123", "-q", $up, $uri) ||
305          die "Couldn't exec";
306  }
307}
308
309# ID3 comments often have useless crap because tools like iTunes were
310# written by drooling idiots
311sub filter_comment {
312  my $comment = shift(@_);
313
314  if ($comment =~ /^engiTunes_CDDB/) {
315    return undef;
316  }
317  return $comment;
318}
319
320
321# Play an MPEG audio file
322sub play_mpeg_audio {
323    # Retrieve those command line opts.
324    my %opts = %{shift(@_)};
325
326    my %MPEGModes = ( 0 => "stereo",
327                      1 => "joint-stereo",
328                      2 => "dual-channel",
329                      3 => "single-channel");
330
331    # If it's an MP3 file, try to extract useful data
332    my $tag = get_mp3tag("$opts{'d'}/$opts{'e'}");
333    if (!$tag) {
334        print ZEPHYR "No ID3 tag found\n";
335        my @fnamearray = split /,/, $opts{'J'};
336        foreach $fname (@fnamearray) {
337            print ZEPHYR "Filename: $fname\n";
338        }
339#           print ZEPHYR "Filename: $opts{'J'}\n\n";
340    } else {
341      print ZEPHYR "Title          : $tag->{TITLE}\n";
342      print ZEPHYR "Artist         : $tag->{ARTIST}\n";
343      print ZEPHYR "Album          : $tag->{ALBUM}\n";
344      if ($tag->{TRACKNUM} =~ /(.*)\/.*/) {
345          $tag->{TRACKNUM} = $1;
346      }
347      if ($tag->{TRACKNUM}) {
348          print ZEPHYR "Track #        : $tag->{TRACKNUM}\n";
349      }
350      if ($tag->{YEAR}) {
351        print ZEPHYR "Year           : $tag->{YEAR}\n";
352      }
353      if ($tag->{GENRE}) {
354        # iTunes (?) does something weird with genres, leaving them
355        # as the string "(##)" where ## is the genre type. Let's deal
356        # with this.
357        if ($tag->{GENRE} =~ /^\((\d*)\)$/) {
358          $tag->{GENRE} = $MP3::Info::mp3_genres[$1];
359        }
360        if ($tag->{GENRE} =~ /^(\d*)$/) {
361          $tag->{GENRE} = $MP3::Info::mp3_genres[$1];
362        }
363        print ZEPHYR "Genre          : $tag->{GENRE}\n";
364      }
365      if (ref $tag->{COMMENT} eq 'ARRAY') {
366        foreach $index (0 .. $#{$tag->{COMMENT}}) {
367          if ($comment = filter_comment(@{$tag->{COMMENT}}[$index])) {
368            print ZEPHYR "Comment        : $comment\n";
369          }
370        }
371      } elsif ($tag->{COMMENT}) {
372        if ($comment = filter_comment($tag->{COMMENT})) {
373          print ZEPHYR "Comment        : $comment\n";
374        }
375      }
376
377      # Maybe get some extended ID3v2 info?
378      my $v2tag = get_mp3tag("$opts{'d'}/$opts{'e'}", 2, 1);
379      %lessinfo = %$v2tag;
380      foreach $dtag (keys %MP3::Info::v2_to_v1_names) {
381        delete $lessinfo{$dtag};
382      }
383      delete $lessinfo{'GENRE'};
384      # Delete annoying useless crap
385      my @bad_tags = ('GEO', 'GEOB', # General encapsulated object
386                      'PIC', 'APIC', # Attached picture.
387                      );
388      foreach $dtag (@bad_tags) {
389          delete $lessinfo{$dtag};
390      }
391      while (($key,$val) = each %lessinfo) {
392        printf ZEPHYR "%-15s: %s\n", $MP3::Info::v2_tag_names{$key}, $val;
393      }
394      print ZEPHYR "\n";
395    }
396
397    my $info = get_mp3info("$opts{'d'}/$opts{'e'}");
398    if (!$info) {
399        print ZEPHYR "No MPEG header found\n";
400    } else {
401        print ZEPHYR "MPEG $info->{VERSION} layer $info->{LAYER}, ";
402        if ($info->{VBR}) {
403            print ZEPHYR "VBR ";
404        }
405        print ZEPHYR "$info->{BITRATE} kbit/s, $info->{FREQUENCY} kHz ";
406        print ZEPHYR $MPEGModes{$info->{STEREO}};
407        print ZEPHYR "\n\n";
408        printf ZEPHYR "Track length: %d:%02ds\n", $info->{MM}, $info->{SS};
409    }
410    close(ZEPHYR);
411
412    # Play the file
413    # mpg123 is a crock.  If you don't give it -q, it needs to be on a pty
414    # or it SEGVs. Really.
415
416    system("chmod a+rw /dev/audio");
417    system("ps ax | grep ogg123 | awk '{print $2}' | xargs kill -9");
418    system("ps ax | grep mpg123 | awk '{print $2}' | xargs kill -9");
419    system("/mit/infoagents/bin/mpg123 -b 16384 -q - >/tmp/mpg123.out 2>&1");
420
421    # Done. Status:
422    open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i ' .
423         'sipbmp3@xcb -s "SIPB LPR music spooler"');
424
425    # Check if there were any errors
426    open(MP3STATUS, "/tmp/mpg123.out");
427    if ($_ = <MP3STATUS>) {
428        print ZEPHYR "Playback completed with the following errors:\n";
429        print ZEPHYR $_;
430        while (<MP3STATUS>) {
431            print ZEPHYR $_;
432        }
433    } else {
434        print ZEPHYR "Playback completed successfully.\n";
435        # Repeat tag data for those playing at home
436        if (!$tag) {
437          print ZEPHYR "No ID3 tag found\n";
438          print ZEPHYR "Filename: $opts{'J'}\n\n";
439        } else {
440          print ZEPHYR "Title          : $tag->{TITLE}\n";
441          print ZEPHYR "Artist         : $tag->{ARTIST}\n";
442          print ZEPHYR "Album          : $tag->{ALBUM}\n";
443          if ($tag->{TRACKNUM} =~ /(.*)\/.*/) {
444              $tag->{TRACKNUM} = $1;
445          }
446          if ($tag->{TRACKNUM}) {
447              print ZEPHYR "Track #        : $tag->{TRACKNUM}\n";
448          }
449          if ($tag->{YEAR}) {
450            print ZEPHYR "Year           : $tag->{YEAR}\n";
451          }
452          if ($tag->{GENRE}) {
453              print ZEPHYR "Genre          : $tag->{GENRE}\n";
454          }
455          if (ref $tag->{COMMENT} eq 'ARRAY') {
456            foreach $index (0 .. $#{$tag->{COMMENT}}) {
457              if ($comment = filter_comment(@{$tag->{COMMENT}}[$index])) {
458                print ZEPHYR "Comment        : $comment\n";
459              }
460            }
461          } elsif ($tag->{COMMENT}) {
462            if ($comment = filter_comment($tag->{COMMENT})) {
463              print ZEPHYR "Comment        : $comment\n";
464            }
465          }
466
467          # others
468          while (($key,$val) = each %lessinfo) {
469            printf ZEPHYR "%-15s: %s\n", $MP3::Info::v2_tag_names{$key}, $val;
470          }
471        }
472    }
473    close(MP3STATUS);
474    unlink(MP3STATUS);
475
476    close(ZEPHYR);
477}
478
479# Play an OggVorbis audio stream (doesn't support auth!)
480# play_ogg_stream(URI, userpass)
481sub play_ogg_stream {
482  my $uri = shift(@_);
483  my $userpass = shift(@_);
484
485  system("chmod a+rw /dev/audio");
486  system("ps -aef | grep ogg123 | awk '{print $2}' | xargs kill -9");
487  system("ps -aef | grep mpg123 | awk '{print $2}' | xargs kill -9");
488
489  # Prepare to write status:
490  open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i sipbmp3@xcb -s "SIPB LPR music spooler"');
491
492  # fork for ogg123
493  my $pid = open(OGGSTATUS, "-|");
494  unless (defined $pid) {
495      print ZEPHYR "Couldn't fork: $!\n";
496      close(ZEPHYR);
497      return;
498  }
499  if ($pid) { # parent
500      # Check if there were any errors
501      if ($_ = <OGGSTATUS>) {
502          print ZEPHYR "Playback completed with the following errors:\n";
503          print ZEPHYR $_;
504          while (<OGGSTATUS>) {
505              print ZEPHYR $_;
506          }
507      } else {
508          print ZEPHYR "Playback completed successfully.\n";
509      }
510      close(OGGSTATUS) || print ZEPHYR "ogg123 exited $?\n";
511           
512      close(ZEPHYR);
513  }
514  else { #child
515      # redirect STDERR to STDOUT
516      open STDERR, '>&STDOUT';
517      exec("/mit/sipb/bin/ogg123","-b40000","-dau","-q","-f","-",$uri) ||
518          die "Couldn't exec";
519  }
520}
521
522# Play an OggVorbis audio file
523sub play_ogg_audio {
524  # Retrieve those command line opts.
525  my %opts = %{shift(@_)};
526
527  # Get ogginfo stuff
528  open(OGGINFO, "/mit/sipb/bin/ogginfo $opts{'d'}/$opts{'e'}|");
529  while (<OGGINFO>) {
530    if (/(.*)=(.*)/) {
531      $ogginfo{lc("$1")} = $2;
532    }
533  }
534  close(OGGINFO);
535
536  # If there's no title, print the filename
537  if (!$ogginfo{'title'}) {
538    print ZEPHYR "No ogginfo data found\n";
539    print ZEPHYR "Filename: $opts{'J'}\n";
540  } else {
541    print ZEPHYR "Title          : $ogginfo{'title'}\n";
542    print ZEPHYR "Artist         : $ogginfo{'artist'}\n";
543    print ZEPHYR "Album          : $ogginfo{'album'}\n";
544    print ZEPHYR "Track #        : $ogginfo{'tracknumber'}\n";
545
546    # others
547    %lessinfo = %ogginfo;
548    delete $lessinfo{'filename'};
549    delete $lessinfo{'title'};
550    delete $lessinfo{'artist'};
551    delete $lessinfo{'album'};
552    delete $lessinfo{'tracknumber'};
553    delete $lessinfo{'header_integrity'};
554    delete $lessinfo{'stream_integrity'};
555    delete $lessinfo{'file_truncated'};
556    delete $lessinfo{'version'};
557    delete $lessinfo{'channels'};
558    delete $lessinfo{'bitrate_upper'};
559    delete $lessinfo{'bitrate_nominal'};
560    delete $lessinfo{'bitrate_lower'};
561    delete $lessinfo{'bitrate_average'};
562    delete $lessinfo{'length'};
563    delete $lessinfo{'playtime'};
564    delete $lessinfo{'kbps'};
565    delete $lessinfo{'time'};
566    delete $lessinfo{'rg_radio'};
567    delete $lessinfo{'rg_audiophile'};
568    delete $lessinfo{'rg_peak'};
569    delete $lessinfo{'replaygain_album_peak'};
570    delete $lessinfo{'replaygain_track_peak'};
571    delete $lessinfo{'replaygain_album_gain'};
572    delete $lessinfo{'replaygain_track_gain'};
573
574    while (($key,$val) = each %lessinfo) {
575      printf ZEPHYR "%-15s: %s\n", $key, $val;
576    }
577  }
578
579  printf ZEPHYR "\nOgg Vorbis, average %.1f kbit/s, %d channels\n\n",
580    $ogginfo{'bitrate_average'}/1024, $ogginfo{'channels'};
581  print ZEPHYR "Track length: $ogginfo{'playtime'}s\n";
582  close(ZEPHYR);
583
584  # Play the file
585
586  system("chmod a+rw /dev/audio");
587  system("ps -aef | grep ogg123 | awk '{print $2}' | xargs kill -9");
588  system("ps -aef | grep mpg123 | awk '{print $2}' | xargs kill -9");
589  system("/mit/sipb/bin/ogg123 -b 40000 -dau -q -f - - 2> /tmp/ogg123.out | audioplay");
590
591  # Done. Status:
592  open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i sipbmp3@xcb -s "SIPB LPR music spooler"');
593
594  # Check if there were any errors
595  open(OGGSTATUS, "/tmp/ogg123.out");
596  if ($_ = <OGGSTATUS>) {
597    print ZEPHYR "Playback completed with the following errors:\n";
598    print ZEPHYR $_;
599    while (<OGGSTATUS>) {
600      print ZEPHYR $_;
601    }
602  } else {
603    print ZEPHYR "Playback completed successfully.\n";
604    # Repeat tag data for those playing at home
605    if (!$ogginfo{'title'}) {
606      print ZEPHYR "No ogginfo data found\n";
607      print ZEPHYR "Filename: $opts{'J'}\n\n";
608    } else {
609      print ZEPHYR "Title          : $ogginfo{'title'}\n";
610      print ZEPHYR "Artist         : $ogginfo{'artist'}\n";
611      print ZEPHYR "Album          : $ogginfo{'album'}\n";
612      print ZEPHYR "Track #        : $ogginfo{'tracknumber'}\n";
613
614      # others
615      while (($key,$val) = each %lessinfo) {
616        printf ZEPHYR "%-15s: %s\n", $key, $val;
617      }
618    }
619  }
620
621  close(OGGSTATUS);
622  unlink(OGGSTATUS);
623
624  close(ZEPHYR);
625}
Note: See TracBrowser for help on using the repository browser.