5

I am trying to implement a radio player using RPi. The goal is to setup a playlist and start playing once playlist is populated. The player and radio processes are expected to exit once stop code is executed.

The radio process terminates nicely but the player process still remains on wait even after calling terminate. If the stop code is called again then player process terminates

Things tried:

  1. reordering wait commands (player,radio)/(radio,player)
  2. similarly reordering terminate commands
  3. using kill instead of terminate hangs the RPi

Player Code:

while playlist:
    player = subprocess.Popen(
            ["avconv", "-i", "Music/%s" % playlist[0], "-f", "s16le", "-ar", 
            "22.05k", "-ac", "1", "-"], stdout=subprocess.PIPE)

    radio = subprocess.Popen(["./pifm", "-", freq], stdin=player.stdout)

    radio.wait()
    print "************ exiting from radio :)"
    player.wait()
    print "************ exiting from player :)"

    playlist.pop(0)
player = None
radio = None

Player Stop code (called from another thread):

print "************ stop requested"

if radio and radio.poll() is None:
    print "************ terminating radio :)"
    radio.terminate()

if player and player.poll() is None:
    print "************ terminating player :)"
    player.terminate()

Alternative:

Another alternative was to have a persistent sink for radio and on demand process for player

def start_radio():
    global radio
    radio = subprocess.Popen(["./pifm"...], stdin=subprocess.PIPE)

def play():
    global player
    while playlist and radio:
        player = subprocess.Popen(["avconv"...], stdout=radio.stdin)
        player.wait()
        playlist.pop(0)

def stop():
   if player and player.poll() is None:
      print "************ terminating player :)"
      player.terminate()

But in this case calling player.terminate() closes the player while playing last packet repeatedly on the radio process (like a stuck record). This stuck record plays till I start a new player or terminate the radio.

CuriousCat
  • 429
  • 5
  • 16
  • 2
    try `player.join()` then `player.terminate()`? – ritlew Jul 01 '16 at 20:49
  • @ritlew `player` is Popen object and doesn't have join method. – CuriousCat Jul 01 '16 at 21:20
  • 2
    1- *"using kill instead of terminate hangs the RPi"* -- you should fix that issue first (it might enable you to answer the current question too). 2- You should call `player.stdout.close()` after `radio = Popen(...)` so that the player process exits if the radio process exits. – jfs Jul 02 '16 at 07:22
  • @J.F.Sebastian it works. Kindly post the suggestion as an answer and I will accept it. – CuriousCat Jul 02 '16 at 19:12
  • @J.F.Sebastian Can you help me with the alternative section? – CuriousCat Jul 02 '16 at 19:18
  • @CuriousCat You aren't passing the reference `radio` anywhere in your alternative case and I don't think that works like that... – shackra Jul 02 '16 at 23:22
  • @shackra `radio` and `player` are global objects. Thanks for noticing that. – CuriousCat Jul 03 '16 at 02:15
  • If you have a solution, you could post it as your own answer ([it is encouraged](http://stackoverflow.com/help/self-answer)). – jfs Jul 04 '16 at 10:25

1 Answers1

2

As @J.F.Sebastian mentioned, using player.stdout.close() works. The entire code base is published here https://github.com/hex007/pifm_server

So the final code looks something like this

while playlist:
    player = subprocess.Popen(
            ["avconv", "-i", "Music/%s" % playlist[0], "-f", "s16le", "-ar", 
            "22.05k", "-ac", "1", "-"], stdout=subprocess.PIPE)

    radio = subprocess.Popen(["./pifm", "-", freq], stdin=player.stdout)

    player.stdout.close()

    radio.wait()
    player.wait()

    if stop_requested:
        stop_requested = False
        break

    playlist.pop(0)

player = None
radio = None

And the stopping code:

stop_requested = True

if radio and radio.poll() is None:
    radio.terminate()

if player and player.poll() is None:
    player.terminate()
CuriousCat
  • 429
  • 5
  • 16
  • unrelated: to implement `avconv ... | pifm ...` pipeline in Python, you could start the programs in the reverse order (it doesn't affect the result -- they should run in parallel anyway): `pifm = Popen(['pifm', '...'], stdin=PIPE); player = Popen(['avconv', '...'], stdout=pifm.stdin); pifm.communicate(); player.wait()` (in this case, if `pifm` dies then `avconv` will know even if you forget to close `pifm.stdin`). See @Cristian's answer to [How do I use subprocess.Popen to connect multiple processes by pipes?](http://stackoverflow.com/q/295459/4279) – jfs Jul 07 '16 at 23:01
  • @J.F.Sebastian The main idea for the Alternative section was to have a sink for pifm as pifm transmits silence if stdin is blank; a favourable feature. And then connect the pipe to another process to which dumps to pifm stdin ondemand. Such behaviour asks for persistent radio process which does not terminate even if stdin is closed. I am looking into the article you mentioned. – CuriousCat Jul 07 '16 at 23:05
  • to be clear **both** methods emulate **the same** shell pipeline: `avconv | pifm` (if you expect a different result then the expectation is wrong). – jfs Jul 07 '16 at 23:08