/lib/Teto/Tatsumaki/Handler/Stream.pm

http://github.com/motemen/Teto · Perl · 59 lines · 49 code · 10 blank · 0 comment · 4 complexity · 24da24cb15772acb8e0149bfb237a9f8 MD5 · raw file

  1. package Teto::Tatsumaki::Handler::Stream;
  2. use strict;
  3. use warnings;
  4. use parent 'Teto::Tatsumaki::Handler';
  5. use Coro;
  6. use Teto::Worker::ReadBuffer;
  7. __PACKAGE__->asynchronous(1);
  8. sub get {
  9. my $self = shift;
  10. my $control = $self->build_control;
  11. my $icemeta = !!$self->request->header('ICY-Metadata');
  12. my $read_buffer = Teto::Worker::ReadBuffer->spawn(
  13. queue => $control->queue,
  14. ( $icemeta ? () : ( icemeta => undef ) ),
  15. );
  16. $self->binary(1);
  17. $self->response->content_type('audio/mp3');
  18. $self->response->header(
  19. $icemeta ? ( 'Icy-Metaint' => $read_buffer->icemeta->interval ) : (),
  20. Icy_Name => 'tetocast',
  21. Icy_Url => $self->request->uri,
  22. Ice_Audio_Info => 'ice-samplerate=44100;ice-bitrate=192000;ice-channels=2',
  23. );
  24. $self->flush; # iTunes seems to disconnect if header does not return immediately
  25. async {
  26. $Coro::current->desc('streamer coro');
  27. if ($self->get_writer->isa('Twiggy::Writer')) {
  28. $self->writer->{handle}->on_drain(unblock_sub {
  29. my $bytes = $read_buffer->channel->get;
  30. $self->writer->write($bytes);
  31. });
  32. $self->writer->{handle}->on_error(sub {
  33. my ($handle, $fatal, $msg) = @_;
  34. $self->log($fatal ? 'error' : 'warn', $msg);
  35. $self->writer->close;
  36. });
  37. } else {
  38. while (defined (my $bytes = $read_buffer->channel->get)) {
  39. eval { $self->stream_write($bytes) };
  40. if ($@) {
  41. warn $@;
  42. last;
  43. };
  44. }
  45. $self->finish;
  46. }
  47. };
  48. }
  49. 1;