Skip to content

Commit

Permalink
Merge pull request #6 from reinzor/feat/stretch_mmalvideosink
Browse files Browse the repository at this point in the history
Raspbian Stretch with GST-MMAL Videosink
  • Loading branch information
reinzor authored Feb 15, 2019
2 parents 489d76d + da32db0 commit 4b50e37
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 103 deletions.
69 changes: 16 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,86 +14,47 @@ Video wall with multiple tiles that enables synchronized video playback, mirrore

## Installation

### Raspberry PI installation
### Raspberry PI

#### Installation prerequisites

- Raspbian Jessie
- Raspbian Stretch Lite
- Raspberry Pi 3 / Raspberry Pi Zero (other Pi's not tested)
- Videowall repository is your current working directory

#### Installation dependencies
#### Installation

Install debian requirements:
Installs the videowall and enables a client service on startup.

```
sudo apt-get -y install x-window-system \
gstreamer1.0-tools \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-omx \ # rpi
gstreamer1.0-libav \ # pc
gir1.2-gst-plugins-base-1.0 \
python-gst-1.0 \
pulseaudio \
python-colorlog
./install_raspberry_pi_stretch_lite_autostart.bash
```

Make sure `gstreamer is working properly` by downloading and playing an x264 encoded sample video:
### Ubuntu x86

```
wget https://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_30mb.mp4 -O $HOME/Videos/big_buck_bunny_720p_30mb.mp4
gst-launch-1.0 filesrc location=$HOME/Videos/big_buck_bunny_720p_30mb.mp4 ! qtdemux ! h264parse ! omxh264dec ! videoconvert ! queue ! ximagesink # rpi
gst-launch-1.0 filesrc location=$HOME/Videos/big_buck_bunny_720p_30mb.mp4 ! decodebin ! videoconvert ! queue ! ximagesink # pc
```

Install pip requirements:

```
pip install --user -r requirements.txt
```

#### Set-up videowall

Enable the x-server on startup:

```
sudo cp systemd/startx/override.conf /etc/systemd/system/[email protected]/
sudo systemctl daemon-reload
sudo systemctl restart [email protected]
```

If everything went well, the x server should now be running and you should see a black screen with a small green font: `pi@raspberrypi`.

Add the following to `/etc/X11/xinit/xinitrc` after the first line to disable screen blanking:
```
xset s off # don't activate screensaver
xset -dpms # disable DPMS (Energy Star) features.
xset s noblank # don't blank the video device
```
#### Installation prerequisites

Set boot config RPI so we can use more GPU memory (not sure whether this has any effect):
```
echo "gpu_mem = 386MB" | sudo tee -a /boot/config.txt
```
- Ubuntu x86 16.04 LTS (other versions not tested)
- Videowall repository is your current working directory

We should also net forget to set the `$DISPLAY` environment variable in order to connect with the `x-server` properly:
#### Installation

```
export DISPLAY=:0
./install_ubuntu_x86.bash
```

## Quick start

### Server

scripts/server

### Client

scripts/client

This is automaticall started on a raspberry pi after installation. Can be manually started on an ubuntu x86 environment.

## References

- [Cinder GST Sync Player](https://github.com/patrickFuerst/Cinder-GstVideoSyncPlayer)
Expand All @@ -109,3 +70,5 @@ export DISPLAY=:0
- [dbus omxplayer](https://github.com/popcornmix/omxplayer)
- [GPU memory 90 degrees omxplayer](https://github.com/popcornmix/omxplayer/issues/467)
- [Remote dbus control](https://stackoverflow.com/questions/10158684/connecting-to-dbus-over-tcp/13275973#13275973)
- [GST OMX](https://github.com/GStreamer/gst-omx)
- [GST MMAL](https://github.com/youviewtv/gst-mmal)
2 changes: 1 addition & 1 deletion cfg/rpi/etc/rc.local
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

if [ ! -f /etc/network/mac ]; then
mac=`echo -n 00:60:2F; dd bs=1 count=3 if=/dev/random 2>/dev/null |hexdump -v -e '/1 ":%02X"'`

echo -e " auto lo\n"\
"iface lo inet loopback\n"\
"\n"\
Expand Down
6 changes: 0 additions & 6 deletions cfg/rpi/etc/systemd/system/[email protected]/override.conf

This file was deleted.

54 changes: 54 additions & 0 deletions install_raspberry_pi_stretch_lite_autostart.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"

# Install system dependencies
sudo apt-get -y update
sudo apt-get -y install git \
libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base-apps \
gstreamer1.0-tools \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
python-pip

# Install python dependencies
sudo -H pip install -r requirements.txt

# Build & Install Gstreamer MMAL
git clone https://github.com/reinzor/gst-mmal /tmp/gst-mmal
cd /tmp/gst-mmal
LDFLAGS='-L/opt/vc/lib' CPPFLAGS='-I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux' ./autogen.sh --disable-gtk-doc
make
sudo make install

# Build & Install Gstreamer OMX
git clone https://github.com/reinzor/gst-omx /tmp/gst-omx
cd /tmp/gst-omx
git checkout 1.10.4
LDFLAGS='-L/opt/vc/lib' CFLAGS='-I/opt/vc/include -I/opt/vc/include/IL -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/IL' CPPFLAGS='-I/opt/vc/include -I/opt/vc/include/IL -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/IL' ./autogen.sh --disable-gtk-doc --with-omx-target=rpi
make
sudo make install

# Create videos folder and download sample video
wget https://github.com/reinzor/videowall/releases/download/0/big_buck_bunny_720p_30mb.mp4 -O $SCRIPT_DIR/videos/big_buck_bunny_720p_30mb.mp4

# Setup paths in bashrc
echo "source $SCRIPT_DIR/setup.bash" >> ~/.bashrc

# Copy additional raspberry pi system files
sudo rm /etc/network/mac
sudo cp -r $SCRIPT_DIR/cfg/rpi/* /

# Enable startup run and disable dhcpcd
sudo systemctl daemon-reload
sudo systemctl enable videowall
sudo systemctl disable dhcpcd

# Show installation done message
echo "======="
echo ""
echo "Installation complete, please source your ~/.bashrc and type the following command:"
echo ""
echo " gst-launch-1.0 -v filesrc location=$SCRIPT_DIR/videos/big_buck_bunny_720p_30mb.mp4 ! qtdemux ! h264parse ! omxh264dec ! videocrop bottom=10 ! mmalvideosink"
echo ""
echo "======"
19 changes: 19 additions & 0 deletions install_ubuntu_x86.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash

# Install system dependencies
sudo apt-get -y update
sudo apt-get -y install git \
gstreamer1.0-plugins-base-apps \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
python-pip

# Install python dependencies
sudo -H pip install -r requirements.txt

# Create videos folder and download sample video
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
wget https://github.com/reinzor/videowall/releases/download/0/big_buck_bunny_720p_30mb.mp4 -O $SCRIPT_DIR/videos/videos/big_buck_bunny_720p_30mb.mp4

# Setup paths in bashrc
echo "source $SCRIPT_DIR/setup.bash" >> ~/.bashrc
3 changes: 2 additions & 1 deletion scripts/client
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python
import argparse
import logging
import os
from argparse import ArgumentParser

from videowall.client import Client
Expand All @@ -11,7 +12,7 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('player_platform', choices=get_player_platform_strings())
parser.add_argument('ifname', choices=get_ifnames())
parser.add_argument('--media_path', default='~/Videos')
parser.add_argument('--media_path', default=os.path.join(os.path.dirname(os.path.realpath(__file__)), "../videos"))
parser.add_argument('--verbose', action='store_true')
parser.add_argument('--server_broadcast_port', type=validate_positive_int_argument, default=2000)
parser.add_argument('--server_play_broadcast_port', type=validate_positive_int_argument, default=2001)
Expand Down
3 changes: 1 addition & 2 deletions setup.bash
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export PYTHONPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"/src
export DISPLAY=:0
export GST_DEBUG=1

export GST_PLUGIN_PATH=/usr/local/lib/gstreamer-1.0:/usr/lib/arm-linux-gnueabihf/gstreamer-1.0
8 changes: 2 additions & 6 deletions src/videowall/gi_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
gi.require_version('GLib', '2.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstNet', '1.0')
gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0')
gi.require_version('GstVideo', '1.0')

from gi.repository import GLib, Gst, GstNet, GObject, Gdk, Gtk
from gi.repository import GdkX11, GstVideo # For window.get_xid(), xvimagesink.set_window_handle()
from gi.repository import GLib, Gst, GstNet, GObject

_ = Gst, GstNet, GObject, GLib, Gdk, Gtk, GdkX11, GstVideo
_ = Gst, GstNet, GObject, GLib
43 changes: 9 additions & 34 deletions src/videowall/player/player_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import threading
import time

from videowall.gi_version import GstNet, Gst, Gdk, Gtk
from videowall.gi_version import GstNet, Gst, GObject
from videowall.util import validate_ip_port

from .player_exceptions import PlayerException
Expand All @@ -23,18 +23,6 @@ def __init__(self, platform, clock_ip, clock_port, use_local_clock=False):
if not issubclass(platform, PlayerPlatform):
raise PlayerException("Invalid player platform {}, available platforms: {}".format(platform,
get_player_platforms()))
# Set-up window
screen = Gdk.Screen.get_default()
self._screen_width = screen.get_width()
self._screen_height = screen.get_height()
self._window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
self._window.set_title("PlayerClient")
self._window.set_default_size(self._screen_width, self._screen_height)
self._window.fullscreen()
self._movie_view = Gtk.DrawingArea()
self._window.add(self._movie_view)
self._window.show_all()

self._use_local_clock = use_local_clock

if not self._use_local_clock:
Expand All @@ -51,22 +39,18 @@ def __init__(self, platform, clock_ip, clock_port, use_local_clock=False):
self._pipeline = None

def run_player_thread():
Gtk.main()
GObject.MainLoop().run()

self._gobject_thread = threading.Thread(target=run_player_thread)
self._gobject_thread.start()

self._platform = platform

logger.info("Player constructed for window (%dx%d) and clock server (%s:%d)", self._screen_width,
self._screen_height, clock_ip, clock_port)
logger.info("Player constructed (platform=%s, clock_ip=%s, clock_port=%s, use_local_clock=%s)", platform, clock_ip, clock_port, use_local_clock)

def _construct_pipeline(self, filename, videocrop_config, text_overlay, time_overlay):
real_path = os.path.realpath(os.path.expanduser(filename))

if os.environ.get('DISPLAY') is None:
raise PlayerException("No $DISPLAY environment variable is set, the ximagesink will not work")

launch_cmd = ""
if os.path.isfile(real_path):
launch_cmd += "filesrc location={}".format(real_path)
Expand Down Expand Up @@ -96,32 +80,23 @@ def _construct_pipeline(self, filename, videocrop_config, text_overlay, time_ove
launch_cmd += ' ! textoverlay text="{}\n{}"'.format("%s not found" % filename, text_overlay)
launch_cmd += " ! videoconvert"

launch_cmd += " ! videoscale add-borders=false ! video/x-raw,width=%d,height=%d" % (self._screen_width, self._screen_height)
launch_cmd += " ! ximagesink" # or ! fakesink sync=true # sync required for realtime playback
if self._platform == PlayerPlatformPC:
launch_cmd += " ! ximagesink" # or ! fakesink sync=true # sync required for realtime playback
else:
launch_cmd += " ! mmalvideosink"

logger.debug("gst-launch-1.0 -v %s", launch_cmd)
self._pipeline = Gst.parse_launch(launch_cmd)

self._pipeline.get_bus().enable_sync_message_emission()
self._pipeline.get_bus().add_signal_watch()

self._pipeline.get_bus().connect('sync-message::element', self._on_bus_sync_msg)

def _destroy_pipeline(self):
if self._pipeline is not None:
self._pipeline.set_state(Gst.State.NULL)
self._pipeline = None

def _on_bus_sync_msg(self, bus, msg):
if msg is not None:
sink = msg.src
sink.set_property("force-aspect-ratio", True)
sink.set_window_handle(self._movie_view.get_property('window').get_xid())

def close(self):
self.stop()
Gtk.main_quit()
logger.info("Waiting for the GTK Thread to join ..")
GObject.MainLoop().quit()
logger.info("Waiting for the GObject Thread to join ..")
self._gobject_thread.join()

def play(self, filename, base_time_nsecs, videocrop_config, text_overlay, time_overlay):
Expand Down
1 change: 1 addition & 0 deletions videos/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*

0 comments on commit 4b50e37

Please sign in to comment.