python-omxplayer-wrapper icon indicating copy to clipboard operation
python-omxplayer-wrapper copied to clipboard

[Errno 24] after opening and closing videos constantly

Open acmele opened this issue 6 years ago • 13 comments

Issue Report

Description

Alternating between two player instances to put a video display on a tour. This eventually produces a [Errno 24] Too many open files after about an hour.

Problem reproduction

this is the current test code

# Project: Chart Compatible Impath Decoder Emulator for Raspberry pi
# Python 3 

#Test for video switching for monitors on tour







import time
import socket
import sys
from _thread import *
import os
import subprocess
import re
import logging
from omxplayer.player import OMXPlayer
from datetime import datetime

def LoadStream(val):
	#Val = 0 'tmpSDP.sdp dbus "omxplayer.player0'
	#Val = 1 'tmpSDP.sdp dbus "omxplayer.player1'
	#Val = 2 'cnv.sdp' dbus "omxplayer.player1
	
	omx_arg = ['--timeout', '6000', '--live', '--blank', '--refresh', '--no-keys']	
	
	if (val == 0):
		return OMXPlayer('tmpSDP1.sdp', args=omx_arg, dbus_name="omxplayer.player0")
	elif (val == 1):
		return OMXPlayer('tmpSDP2.sdp', args=omx_arg, dbus_name="omxplayer.player1")
	else:
		return OMXPlayer('cnv.sdp', args=omx_arg, dbus_name="omxplayer.player1")


	
log_name = datetime.now().strftime('decoder_%H_%M_%d_%m_%Y.log')	
logging.basicConfig(filename= log_name, level=logging.WARNING, format='%(asctime)s %(message)s') #change to 'WARNING' when in production 'DEBUG' and 'INFO' are valid choices

player_turn = True #true = Player1 will hide and player0 will play 
omx_test = True #The decoder is not in an error recovery state


player1 = LoadStream(1)
player1.play()

#code will load a new player instance, kill the old instance to give the illusion of a seamless transfer then repeat.

while True:
	time.sleep(3)
	try:
		if player_turn:
			player0 = LoadStream(0)
			time.sleep(2)
			player1.hide_video()
			player1.stop()
			player1.quit()
			time.sleep(1)
			player0.play()
			time.sleep(1)
			if player0.is_playing():				
				pass
			omx_test = True				
			player_turn = False
			logging.info('Playing on Player0')
		else:
			player1 = LoadStream(1)
			time.sleep(2)
			player0.hide_video()
			player0.stop()
			player0.quit()
			time.sleep(1)
			player1.play()	
			time.sleep(1)			
			if player1.is_playing():
				pass
			omx_test = True
			player_turn = True
			logging.info('Playing on Player1')	
	except Exception as e:
		error_check = True
		logging.warning('failure to load')
		logging.warning('failure to load error: %s', e)
		while error_check:
			player1.quit()
			time.sleep(2)
			player0.quit()
			time.sleep(2)
			try:
				player1 = LoadStream(2)
				player1.play()
				time.sleep(2)
				if player1.is_playing():
					player_turn = True
					omx_test = False
					error_check = False
			except Exception as e:
				logging.error('CNV display error: %s', e)				
				player1.quit()
	pass
pass

Please let me know if you need more details, amateur here and first time posting.

acmele avatar Jan 31 '19 19:01 acmele

SImilar problem here! Looking for a fix

gym1champ avatar Feb 13 '19 16:02 gym1champ

Same problem here. Fixing #78 would make this a lot easier to avoid, I would think.

psyferre avatar Apr 16 '19 18:04 psyferre

I've been trying to debug this problem, and I think I've found the issue - but not a fix. Each time the player gets loaded with a source, I see an entry in lsof of type STREAM for the main python process. When player.stop() or .quit() is called, that entry does not go away. For example, after playing a sound effect 7 times, here's an excerpt of lsof | grep 'python':

python    12611                   pi    0u      CHR      136,2      0t0          5 /dev/pts/2
python    12611                   pi    1u      CHR      136,2      0t0          5 /dev/pts/2
python    12611                   pi    2u      CHR      136,2      0t0          5 /dev/pts/2
python    12611                   pi    3u     unix 0x9be72d00      0t0     219115 type=STREAM
python    12611                   pi    4u     unix 0x9be74c00      0t0     219245 type=STREAM
python    12611                   pi    5u     unix 0x9be71b00      0t0     218986 type=STREAM
python    12611                   pi    6u     unix 0x9be71800      0t0     219102 type=STREAM
python    12611                   pi    7u     unix 0x9be70000      0t0     219122 type=STREAM
python    12611                   pi    8u     unix 0x9be76a00      0t0     219986 type=STREAM
python    12611                   pi    9u     unix 0x9be76700      0t0     220066 type=STREAM

So far I haven't figured out a way to kill those open streams without killing the main python process, but I'll report back if I do.

psyferre avatar May 03 '19 18:05 psyferre

I ran into the same problem and solved it for me with the following workaround:

You should not create new instances of OMXPlayer, only create them once (player0 and player1 for you). Instead call load() with the new video to play. With this approach the number of open streams does not increase anymore.

michaelnimbs avatar Jun 06 '19 11:06 michaelnimbs

@michaelnimbs You are my savior, everything works. You just need to use the method load()

megapegabot avatar Feb 01 '20 07:02 megapegabot

Looking at the lsof output, the open fds are UNIX sockets, so I suspect this is the connection to the dbus daemon. Looking at the source, I indeed see that OMXPlayer._connection is created, but it is never actually closed.

I suspect that it would be solved by disconnecting dbus on quit, e.g. by adding this to the end of the quit() method (alternatively, you can call this after you call quit from your program as well so you do not need to modify the library, then replace self with your player instance):

 self._connection._bus.close()
 self._connection = None

Anyone care to test this?

matthijskooijman avatar Feb 14 '20 23:02 matthijskooijman

Hi, using a single instance and only calling load does not seem like it will help because the DBus connection is opened every time a new process is spawned. (see https://github.com/willprice/python-omxplayer-wrapper/blob/master/omxplayer/player.py#L163)

It may be that in most cases the garbage collector closes the connections but I encountered resource exhaustion at least once with 140 DBus sockets open in a long running Python process which used omxplayer-wrapper.

I'll monkey-patch the quit method to call self._connection._bus.close() and see if this resolves the problem.

jpc avatar Nov 12 '20 13:11 jpc

In case it helps someone, a quick patch looks like this:

def patch_OMXPLayer_quit():
    old_quit = OMXPlayer.quit
    def new_quit(self):
        self._connection._bus.close()
        old_quit(self)
    OMXPlayer.quit = new_quit
patch_OMXPLayer_quit()

I'll let you know if it solved the problem definitely.

jpc avatar Nov 12 '20 13:11 jpc

Ok, I've tested this for quite a long time now and the change I posted definitively fixed the problem with leaking bus connections.

jpc avatar Dec 05 '20 13:12 jpc

Thanks @jpc for looking into this, would you be able to open a PR with this change and i'll merge it in!

willprice avatar Dec 05 '20 13:12 willprice

I add @matthijskooijman suggestion in player.py at function quit() and it's work.(Tested 72 hours none stop playing 9 mp4 files, until now pi still playing without [Errno 24])

def quit():
    if self._connection._bus is not None:
        self._connection._bus.close()
        logger.debug('[Errno 24][patch]BusConnection closed')
    """
    Quit the player, blocking until the process has died
    """
    ......

bonnyone avatar Oct 24 '21 13:10 bonnyone

Thanks for the confirmation @bonnyone. I'll make the change and make a new release later today.

willprice avatar Oct 24 '21 13:10 willprice

@willprice U r welcome.

This is my code for test

def threadingPlayer():
    while True:
        try:
            movie = movies[movie_index]
            print('********************************')
            print(movie)
            print('********************************')
            player = OMXPlayer(Path(movie),
                               args=['--no-osd', '--no-keys', '-o', 'hdmi'],
                               dbus_name='org.mpris.MediaPlayer2.omxplayer1')
            player.set_volume(player_volume)
            sleep(2.5)
            player.play_sync()
            player.quit()
            player = None
            print('Play Next')
            movie_index = movie_index + 1
            if movie_index == len(movies):
                movie_index = 0
        except OSError as e:
            logger.error(e)
        except Exception as e:
            logger.error(e)


threading.Thread(target=threadingPlayer).start()

bonnyone avatar Oct 24 '21 13:10 bonnyone