Search code examples
pythonpygameframebufferlibvlcpygame-surface

Pygame slideshow delay anormally long


I'm setting up a Slideshow system mixing images and videos, from a directory. I'm using a Raspberry Pi B, pygame and vlc. I didn't install X so everything happens in framebuffer.

My actual code is working but :

  • The 4 seconds delay is not respected. The image is displayed +- 11 seconds.
  • One of the images witch has nothing particular, is displayed much longer, +- 1m30. (my real problem)

I tried a bash script with fbi, fim, vlc without suitable result. The closest was with vlc but it takes too long to render an image in framebuffer.

I'm quite new to pygame. Here is the code:

import pygame
import sys
import time
import vlc
import os


filesdir = '/home/pi/SMBmount/'

pygame.init()

size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
black = 0, 0, 0

screen = pygame.display.set_mode(size)

while True:
    # For every file in filesdir :
    for filename in os.listdir(filesdir):
        filenamelower = filename.lower()

        # If image:
        if filenamelower.endswith('.png') or filenamelower.endswith('.jpg') or filenamelower.endswith('.jpeg'):
            fullname = filesdir + filename
            img = pygame.image.load(fullname)
            img = pygame.transform.scale(img, size)
            imgrect = img.get_rect()

            screen.fill(black)
            screen.blit(img, imgrect)
            pygame.mouse.set_visible(False)
            pygame.display.flip()

            time.sleep(4)

        # Elif video:
        elif filenamelower.endswith('.mp4') or filenamelower.endswith('.mkv') or filenamelower.endswith('.avi'):
            fullname = filesdir + filename
            # Create instane of VLC and create reference to movie.
            vlcInstance = vlc.Instance("--aout=adummy")
            media = vlcInstance.media_new(fullname)

            # Create new instance of vlc player
            player = vlcInstance.media_player_new()

            # Load movie into vlc player instance
            player.set_media(media)

            # Start movie playback
            player.play()

            # Do not continue if video not finished
            while player.get_state() != vlc.State.Ended:
                # Quit if keyboard pressed during video
                for event in pygame.event.get():
                    if event.type == pygame.KEYDOWN:
                        pygame.display.quit()
                        pygame.quit()
                        sys.exit()
            player.stop()

        # Quit if keyboard pressed during video
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                pygame.display.quit()
                pygame.quit()
                sys.exit()

I'm open to any alternative able to work with pictures AND videos.

EDIT: It was finally the time it takes to pygame to resize the (next) image with pygame.transform.scale().

Is there any way to optimise that ? Like for example, to print fullscreen without resizing the large images ?


Solution

  • I figured out what were the issues, with the help of Valentino. He helped me to optimize the code to improve the loading times of every image, that fixed the first issue.

    See his answer.

    Additionnally, I added a block of code :

    # If image is not same dimensions
    if imgrect.size != size:
        img = Image.open(fullname)
        img = img.resize(size, Image.ANTIALIAS)
        img.save(fullname, optimize=True, quality=95)
        img = pygame.image.load(fullname).convert()
        imgrect = img.get_rect()
    

    If the picture is not the screen resolution, I use Pillow (PIL) to resize and reduce the color palette to 8-bit (256 colors). It reduces file sizes significantly (especially for big files) and allow pygame to load the image faster. It fixed the second issue.

    For those interested, the full code is :

    import pygame
    import sys
    import vlc
    import os
    import re
    from PIL import Image
    
    
    filesdir = '/home/pi/SMBmount/'
    
    imgexts = ['png', 'jpg', 'jpeg']
    videxts = ['mp4', 'mkv', 'avi']
    
    time = 5  # Time to display every img
    
    #filtering out non video and non image files in the directory using regex
    showlist = [filename for filename in os.listdir(filesdir) if re.search('[' + '|'.join(imgexts + videxts) + ']$', filename.lower())]
    
    pygame.init()
    
    size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
    
    screen = pygame.display.set_mode(size)
    
    clock = pygame.time.Clock()
    
    while True:
        # For every file in filesdir :
        for filename in showlist:
            filenamelower = filename.lower()
    
            # If image:
            if filenamelower.endswith('.png') or filenamelower.endswith('.jpg') or filenamelower.endswith('.jpeg'):
                fullname = filesdir + filename
                img = pygame.image.load(fullname).convert()
                imgrect = img.get_rect()
    
                # If image is not same dimensions
                if imgrect.size != size:
                    img = Image.open(fullname)
                    img = img.resize(size, Image.ANTIALIAS)
                    img.save(fullname, optimize=True, quality=95)
                    img = pygame.image.load(fullname).convert()
                    imgrect = img.get_rect()
    
                screen.blit(img, imgrect)
                pygame.mouse.set_visible(False)
                pygame.display.flip()
    
            # Elif video:
            elif filenamelower.endswith('.mp4') or filenamelower.endswith('.mkv') or filenamelower.endswith('.avi'):
                fullname = filesdir + filename
                # Create instane of VLC and create reference to movie.
                vlcInstance = vlc.Instance("--aout=adummy")
                media = vlcInstance.media_new(fullname)
    
                # Create new instance of vlc player
                player = vlcInstance.media_player_new()
    
                # Load movie into vlc player instance
                player.set_media(media)
    
                # Start movie playback
                player.play()
    
                # Do not continue if video not finished
                while player.get_state() != vlc.State.Ended:
                    # Quit if keyboard pressed during video
                    for event in pygame.event.get():
                        if event.type == pygame.KEYDOWN:
                            pygame.display.quit()
                            pygame.quit()
                            sys.exit()
                player.stop()
    
            clock.tick(1 / time)  # framerate = 0.25 means 1 frame each 4 seconds
    
            # Quit if keyboard pressed during video
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    pygame.display.quit()
                    pygame.quit()
                    sys.exit()