Executer un script afin de demarrer VLC avec un stream video

Je veux donc exécuter un script au démarrage me permettant de lancer le programme VLC avec un streaming vidéo d’une web cam.
Voila j’en reste la, toujours pas de vlc au démarrage… Donc un peux de conseil serait d’une grande aide ! Merci d’avance !

Voila mon script.

#!/bin/bash -xv
LOG_FILE=/tmp/vlc_rtsp.log
exec 3>&1 1>>${LOG_FILE} 2>&1

echo « Démarrage programme $0 - $(date ) »

sleep 5
vlc --fullscreen --no-osd --qt-minimal-view
–network-caching=300 --rtsp-tcp
–avcodec-hw=mmal-omx
« rtsp://admin:casra2607@192.168.1.120:554/cam/realmonitor?channel=1&subtype=1 »

echo « Arrêt programme $0 - $(date ) »

chmod +x ~/vlc_rtsp.sh
```bash
mkdir -p ~/.config/autostart
~/.config/autostart/vlc.desktop
Contenu:
```ini
[Desktop Entry]
Type=Application
Version=1.0
Name=VLC RTSP
Exec=/home/player/vlc_rtsp.sh
X-GNOME-Autostart-enabled=true
StartupNotify=false

Une information nous manque, es-tu en version Lite (ligne de commande) ou Desktop/Full (Interface Graphique) ?

Perso, j’ai abandonné l’idée de services, rencontrant pas mal de problème avec ça.

Ajoute dans `~/.profile’ ;

[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && /chemin/du/script-a-lancer.sh

Lance le script que si c’est la session avec l’affichage qui est utilisé, ceci ne le démarre pas sous SSH.

Si tu as une version « lite », il faudra un « bureau » (shell) pour afficher une vidéo.

Tu peux te baser sur ce guide et remplacer Chromium par VLC; RPi-Chromium Kiosk Mode — Wiki levelKro

Merci pour votre réponse.
Je suis donc en version desktop/full.

J’ai ajouter [[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] au code suivant
Dite moi si cela est correct. Merci
Pour le moment ca ne se lance pas au démarrage.

chmod +x ~/vlc_rtsp.sh

mkdir -p ~/.config/autostart
[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] ~/.config/autostart/vlc.desktop
Contenu:
```ini
[Desktop Entry]
Type=Application
Version=1.0
Name=VLC RTSP
Exec=/home/player/vlc_rtsp.sh
X-GNOME-Autostart-enabled=true
StartupNotify=false

Attention en version desktop/full

Si vous êtes sous Wayland

pour savoir lancer la commande
echo $WAYLAND_DISPLAY

et que votre compositeur est labwc
echo $DESKTOP_SESSION

.config/autostart ne fonctionnera PAS

il faut utiliser

.config/labwc/initrc

et ajouter une ligne exec

exemple de fichier

exec waybar
exec lxterminal
exec /home/pi/mon_script.sh

Si vous n’êtes pas sous wayland
votre script ne se lancera que lorsque l’utilisateur se sera connecté à l’interface graphique.

Pour que le script se lance sans interface graphique (en mode Lite/CLI), il faut passer par crontab, systemd ou /etc/rc.local à la place.

1 « J'aime »

Salut @Flipover

Inutile de chercher à tout faire dans tous les sens.
Il faut une approche étape par étape.

Donc 3 questions :

  1. Arrives-tu à lancer VLC au démarrage (juste le lancer) ?

  2. Arrives-tu à lancer VLC au démarrage et lui faire lire un fichier local ?

  3. Si tu lances le script à la main, fonctionne-t-il ?

++

P.S. : si tu n’as pas besoin du bureau, la version lite est suffisante et il existe cvlc qui utilise vlc mais en mode console.

1 « J'aime »

Merci pour les questions.

1 Non
2 Non
3 Oui

Salut @Flipover
Désolé, pas bcp présent ces dernier temps avec le boulot et les soucis autres.
Tu dois, dans un premier temps, être capable de lancer vlc ou cvlc de manière automatique au démarrage.
C’est là dessus que tu dois bosser.

As-tu vraiment besoin de la version Desktop ? (si le Rpi ne sert qu’à afficher VLC, la réponse est NON)
Es-tu sous BookWorm ?
++

Pour lancer un script au démarrage, tu peux utiliser cron / crontab :
@reboot tonscript

Si ça devient trop complexe, regarde ce projet; https://mp4museum.org/ , ça marche avec des fichiers locaux, mais, je crois que si tu crée un fichier sous forme de playlist, qui pointe vers la webcam (/dev/video0) et bien tu pourrais contourner le problème facilement, sinon voir ce que le projet permet de modifier pour l’ajuster.


Pour afficher ta Webcam, il faut un bureau, soit l’interface graphique (GUI), cependant, charger un bureau complet, comme Raspbian Desktop ou Full, c’est trop pour l’usage. Alors la version Lite est la meilleur base.

En prenant la version Lite, il faut alors ajouter un GUI minimal, et quand ce GUI ce charge, il peut démarrer VLC en ligne de commande et utilisera l’affichage disponible par le GUI.

Ce que tu cherche est proche du mode Kiosk de Chromium/Chrome.

Suivant ce guide, tu aura la base graphique; RPi-Chromium Kiosk Mode — Wiki levelKro

Dans la section de l’autostart, cette partie est à remplacer par ta commande pour démarrer VLC;

sed -i 's/"exited_cleanly":false/"exited_cleanly":true/' ~/.config/chromium/'Local State'
sed -i 's/"exited_cleanly":false/"exited_cleanly":true/; s/"exit_type":"[^"]\+"/"exit_type":"Normal"/' 	~/.config/chromium/Default/Preferences
chromium-browser --disable-infobars --noerrdialogs --incognito --check-for-update-interval=1 --simulate-critical-update --kiosk '[http://localhost]'

C’est la base de script que j’utilise dans tout mes projet demandant un GUI minimum; piDeskboard, piNasbox, piVUMeter, etc…(une partie ne sont pas publique comme projet)

Tu peux démarrer au lieu de VLC, utiliser un script Python qui va lui diffuser la camera. J’ai pas de code spécifique, mais j’utilise ce genre de projet pour avoir 8 caméras sur 1 seul écran avec Python et un GUI minimum (projet non RPi).

Utiliser VLC c’est bien, surtout pour de la vidéo en fichier, vu la prise en charge de codec etc… mais le problème est qu’il peut manquer de flexibilité et charger plus ce que tu as besoin. Dans le cas d’uen caméra, en local ou un format connu et statique, tu peux utiliser un script Python qui sera peut-être plus dur à créer, mais qui sera plus flexible sur le rendu, comme le fps, le buffer etc…


Voici monde code pour la caméras multiples, ajuste le a tes besoin.

import cv2
import pygame
import threading
import time
import json
import sys
from datetime import datetime

# Chargement de la configuration
def load_config():
    try:
        with open("config.json", "r") as f:
            return json.load(f)
    except FileNotFoundError:
        return {"streams": [False,False,False,False,False,False,False,False]}

config = load_config()
STREAM_URLS = [f"{config['url']}{i}" for i in range(1, 9)]
active_streams = config.get("streams", [True] * 8)

debug_mode = "--debug" in sys.argv

def debug_log(message):
    if debug_mode:
        print(message)

class VideoStreamApp:
    def __init__(self):
        pygame.init()
        info = pygame.display.Info()
        self.screen_width, self.screen_height = info.current_w, info.current_h
        debug_log(f"Résolution détectée : {self.screen_width}x{self.screen_height}")
        self.screen = pygame.display.set_mode((self.screen_width, self.screen_height), pygame.NOFRAME)
        pygame.display.set_caption("Surveillance Grid")
        self.running = True
        self.frames = [None] * 8
        self.status = ["Démarrage" for _ in range(8)]
        self.threads = []
        self.start_streams()
    
    def start_streams(self):
        for idx, active in enumerate(active_streams):
            if active:
                self.status[idx] = f"Connexion à la caméra {idx+1}..."
                debug_log(f"Démarrage du thread pour la caméra {idx+1}")
                thread = threading.Thread(target=self.stream_video, args=(idx,))
                thread.daemon = True
                thread.start()
                self.threads.append(thread)
            else:
                self.status[idx] = f"Caméra {idx+1} désactivé"  # Afficher "Désactivé" pour les caméras désactivées
    
    def stream_video(self, index):
        url = STREAM_URLS[index]
        cap = None
        retry_interval = int(config['retry'])  # Intervalle de reconnexion en secondes
        previously_connected = False  # Variable pour suivre l'état de la connexion

        while self.running:
            if not active_streams[index]:  # Si la caméra est désactivée, ne tente pas de se connecter
                time.sleep(1)
                continue
            
            if cap is None or not cap.isOpened():
                self.status[index] = f"Connexion à la caméra {index+1}..."
                debug_log(f"[Camera {index+1}] Tentative de connexion à {url}")
                cap = cv2.VideoCapture(url)
                cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) 
                time.sleep(int(config['timeout']))

            if not cap.isOpened():
                debug_log(f"[Camera {index+1}] Impossible d'ouvrir le flux, reconnexion dans {retry_interval}s")
                self.status[index] = f"Attente de reconnexion à la caméra {index+1}..."
                self.frames[index] = None  # Retirer l'image pour laisser place au message de statut
                cap.release()  # Libération de l'objet cap
                cap = None  # Réinitialisation de cap
                previously_connected = False  # Variable pour suivre l'état de la connexion
                time.sleep(retry_interval)  # Reconnexion après l'intervalle

            else:
                #ret, frame = cap.read()
                for _ in range(1):  # Lire et ignorer les 5 dernières images pour vider le buffer
                    cap.grab()
                ret, frame = cap.read()  # Lire seulement la dernière frame après vidage du buffer

                if ret:
                    # Connexion réussie, on affiche uniquement une fois
                    if not previously_connected:
                        self.status[index] = f"Connecté à la caméra {index+1}"
                        debug_log(f"[Camera {index+1}] Connexion au flux réussie")
                        previously_connected = True

                    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    frame = cv2.resize(frame, (self.screen_width // 3, self.screen_height // 3))
                    frame = pygame.surfarray.make_surface(frame.swapaxes(0, 1))
                    self.frames[index] = frame
                    self.status[index] = ""
                else:
                    debug_log(f"[Camera {index+1}] Échec de réception du flux, reconnexion dans {retry_interval}s")
                    self.status[index] = f"Attente de reconnexion à la caméra {index+1}..."
                    self.frames[index] = None  # Retirer l'image pour laisser place au message de statut
                    cap.release()  # Libération de l'objet cap en cas d'erreur
                    cap = None  # Réinitialisation de cap
                    previously_connected = False  # Variable pour suivre l'état de la connexion
                    time.sleep(retry_interval)  # Reconnexion après l'intervalle
        
        if cap:
            cap.release()
    
    def run(self):
        debug_log("Boucle principale en cours")
        font = pygame.font.SysFont('Courier', int(config['font_camera']))  # Police monospace (compatible Win/Linux)
        font_time = pygame.font.SysFont('Courier', int(config['font_time']))  # Police plus grande pour l'heure et la date
        font_date = pygame.font.SysFont('Courier', int(config['font_date']))  # Police plus grande pour l'heure et la date
        cell_width = self.screen_width // 3
        cell_height = self.screen_height // 3
        
        while self.running:
            self.screen.fill((0, 0, 0))
            for event in pygame.event.get():
                if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_q]):
                    self.running = False
            # Affichage des caméras
            for idx in range(8):
                x, y = (idx % 3) * cell_width, (idx // 3) * cell_height
                if self.frames[idx]:
                    self.screen.blit(self.frames[idx], (x, y))
                else:
                    # Centrage du texte
                    text_surface = font.render(self.status[idx], True, (255, 255, 255))
                    text_rect = text_surface.get_rect(center=(x + cell_width // 2, y + cell_height // 2))
                    self.screen.blit(text_surface, text_rect)
            
            # Affichage de l'heure et de la date dans la 9e case (dernière case)
            x, y = (8 % 3) * cell_width, (8 // 3) * cell_height  # Calcul des coordonnées de la 9e case
            current_time = datetime.now().strftime("%H:%M:%S")
            current_date = datetime.now().strftime("%d/%m/%Y")
            
            # Affichage de l'heure (plus grand)
            time_surface = font_time.render(current_time, True, (255, 255, 255))
            time_rect = time_surface.get_rect(center=(x + cell_width // 2, y + cell_height // 3))
            self.screen.blit(time_surface, time_rect)
            
            # Affichage de la date (plus grand)
            date_surface = font_date.render(current_date, True, (255, 255, 255))
            date_rect = date_surface.get_rect(center=(x + cell_width // 2, y + 2 * cell_height // 3))
            self.screen.blit(date_surface, date_rect)
            
            pygame.display.flip()
            
            for event in pygame.event.get():
                if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_q]):
                    self.running = False
        
        pygame.quit()
        debug_log("Application fermée.")
        
if __name__ == "__main__":
    debug_log("Démarrage de l'application")
    app = VideoStreamApp()
    app.run()


Et le fichier de config;

config.json

{
    "streams": [true, true, true, true, true, true, false, true],
	"url": "http://192.168.0.106:8090/camera",
	"timeout": "1",
	"retry": "5",
	"font_camera": "16",
	"font_time": "90",
	"font_date": "70"
}

« STREAMS » est pour activer/désactiver des caméras, l’URLS est l’URL des caméras (dans mon cas), vu que c’est un serveur FFServer qui gère l’ensemble, je n’ai qu’un point de montage (URL) de base.

Salut Nabla,

Oui je suis sur Bookworm et en effet je pourrais me dispenser de la version Desktop.
Effectivement j’essaye de concentrer mes recherches sur le lancement automatique de VLC au démarrage. Toujours rien pour le moment. A +

Hello @Flipover
Ayant des soucis actuellement, je ne vais pas pouvoir y consacrer du temps.
Mais j’espère pouvoir dans les semaines à venir.
Désolé,
++

(regarde tes messages privés pour une piste de recherche :wink: )