elcuervo / airplay

Airplay bindings to Ruby
MIT License
1.07k stars 69 forks source link

ERROR celluloid: Reel::Rack::Server crashed! #56

Closed bobbyduhbrain closed 10 years ago

bobbyduhbrain commented 10 years ago

Context:

I was using the CLI with my rails app calling commands using system(). As it stands, the CLI cannot be interrupted while sending a video. Here are the exact steps that happened to produce the following error:

1.)

system('air play "public/Videos/output.mp4"')

2.) Video began to play but I attempted to interrupt playback and send another video using fetch_video from the other thread, calling the above system(cmd) again and over-writing the output.mp4 with the new video.

3.) Nothing happened, the video continued to play. I hit ctrl+c and the CLI quit the current video and began playing the video I had requested earlier. A few more ctrl+c's and the server became unresponsive and repeatedly wanted to play output.mp4.

4.) I quit Terminal, the video remained playing despite Terminal being closed. I opened terminal, started the rails server, and then attempted to play another video generating the following errors:

ERROR celluloid: Reel::Rack::Server crashed! ] 0% cubo Errno::EADDRINUSE: Address already in use - bind(2) /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-io-0.15.0/lib/celluloid/io/tcp_server.rb:11:in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-io-0.15.0/lib/celluloid/io/tcp_server.rb:11:innew' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-io-0.15.0/lib/celluloid/io/tcp_server.rb:11:in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/reel-0.4.0/lib/reel/server.rb:13:innew' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/reel-0.4.0/lib/reel/server.rb:13:in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/reel-rack-0.1.0/lib/reel/rack/server.rb:20:ininitialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in public_send' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:indispatch' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:67:in dispatch' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/actor.rb:322:inblock in handle_message' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/actor.rb:416:in block in task' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/tasks.rb:55:inblock in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:13:in `block in create'

ERROR celluloid: Celluloid::SupervisionGroup crashed! Errno::EADDRINUSE: Address already in use - bind(2) /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-io-0.15.0/lib/celluloid/io/tcp_server.rb:11:in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-io-0.15.0/lib/celluloid/io/tcp_server.rb:11:innew' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-io-0.15.0/lib/celluloid/io/tcp_server.rb:11:in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/reel-0.4.0/lib/reel/server.rb:13:innew' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/reel-0.4.0/lib/reel/server.rb:13:in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/reel-rack-0.1.0/lib/reel/rack/server.rb:20:ininitialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in public_send' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:indispatch' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:67:in dispatch' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/actor.rb:322:inblock in handle_message' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/actor.rb:416:in block in task' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/tasks.rb:55:inblock in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:13:in block in create' (celluloid):0:inremote procedure call' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:92:in value' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/proxies/sync_proxy.rb:33:inmethod_missing' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/proxies/actor_proxy.rb:20:in _send_' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid.rb:200:innew_link' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/supervision_group.rb:136:in start' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/supervision_group.rb:124:ininitialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/supervision_group.rb:82:in new' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/supervision_group.rb:82:inadd' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/supervision_group.rb:73:in supervise_as' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:inpublic_send' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in dispatch' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/calls.rb:67:indispatch' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/actor.rb:322:in block in handle_message' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/actor.rb:416:inblock in task' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/tasks.rb:55:in block in initialize' /Users/Mike/.rvm/gems/ruby-2.0.0-p247/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:13:inblock in create'

bobbyduhbrain commented 10 years ago

This did the trick as far as being able to interrupt the CLI playback:

        command = Thread.new do
            system('air play "public/Videos/output.mp4"')
        end

First time I call fetch_video, it interrupts the feed causing the iOS spinning wheel animation on the screen. I call fetch_video again and it plays the correctly encoded output.mp4 file.

elcuervo commented 10 years ago

You are having that issue because you are running several instances of air.

Errno::EADDRINUSE: Address already in use - bind(2)

If you need to serve several files at once and play them I recommend you having the lib an an initializer and then accessing it from the main object instead of running several CLI versions at once

sodabrew commented 10 years ago

@elcuervo Does the air server only use one specific port to serve content? I would suggest making that a random available port, otherwise you won't be able to run several copies of air at different aTVs.

elcuervo commented 10 years ago

Yep. That's a good idea

sodabrew commented 10 years ago

Server port is here: https://github.com/elcuervo/airplay/blob/master/lib/airplay/server.rb#L14 Configuration is here: https://github.com/elcuervo/airplay/blob/master/lib/airplay/configuration.rb#L18 GitHub search has gotten a lot better! https://github.com/elcuervo/airplay/search?q=port&ref=cmdform

Ok, here's my suggestion: Default value for config.port should be nil. When you bind the socket with a nil port, the kernel will assign you one at random. Then you can query for the port number when you tell the aTV how to find the files. If a user is on Windows, or has some network restrictions, they are responsible for setting a non-nil port value.

http://stackoverflow.com/questions/5985822/how-do-you-find-a-random-open-port-in-ruby

http://www.ruby-doc.org/stdlib-2.0.0/libdoc/socket/rdoc/Socket.html#method-i-listen-label-Example+2+%28listening+on+an+arbitrary+port%2C+unix-based+systems+only%29%3A

bobbyduhbrain commented 10 years ago

Would it give the CLI a flag for a port? It might be more elegant to allow users to say which port. Additionally, is it out of scope to be able to close the connection on a given port?

bobbyduhbrain commented 10 years ago

@sodabrew I changed @port to nil and interruption works without any of the issues, as expected. I am worried about the fact that the network will still be consuming resources if we simply use nil and allow the server to pick the port. Perhaps I should be doing the maintenance of the port numbers from my application as opposed to having this implemented in the CLI or lib.

Edit: Nevermind, I don't think I understand what's happening here. I am confused, earlier I was using threads here there are no threads, therefore no open processes.

sodabrew commented 10 years ago

@CUBEMike anything I can help explain? I'm not sure what you meant by using network resources... of course it is?

bobbyduhbrain commented 10 years ago

@sodabrew I appreciate your concern. I just have a tendency to talk out loud when I am figuring things out for myself, I tend to confuse myself since I am new to this kind of stuff (working with other people's code libraries, looking at source).

Anyways, earlier I was calling the CLI from my rails app. I was using threads to interrupt playback without causing the error above. The thread was staying open after I interrupted the stream and continued consuming resources on the server, hence what I crossed out above. It was just a misunderstanding basically.

elcuervo commented 10 years ago

@sodabrew the thing is that Rack::Server needs a port or it will use the Handler default. I will use the Socket technique to get a random port instead of looking for a free one..

sodabrew commented 10 years ago

Gotcha @CUBEMike no worries! Thanks @elcuervo!