Personal tools

Hardware-accelerated video playback on the VisionSOM-6ULL (GStreamer with support for PxP engine)

From SomLabs Wiki

Jump to: navigation, search

Hardware-accelerated video playback on the VisionSOM-6ULL (GStreamer with support for PxP engine)


Prerequisites

This tutorial based on Debian 9.2 image for VisionSOM-6ULL module. Please visit How to prepare SD Card with Debian 9.2 for VisionSOM-6ULL on Linux or How to prepare SD Card with Debian 9.2 for VisionSOM-6ULL on Windows for more detailed instruction how to create a bootable microSD card.

To enable support for LCD-TFT display, please download and apply enable-tft-lcd.patch for somlabs-visionsom-6ull.dts device tree file:

wget http://wiki.somlabs.com/images/c/cd/Enable-tft-lcd.zip
unzip Enable-tft-lcd.zip
patch /home/developer/source/somlabs-dts-1.0/somlabs-visionsom-6ull.dts ./enable-tft-lcd.patch

For more info how to customize, build and upload Device Tree file, please visit How to customize Debian 9.2 device tree.

Installing GStreamer multimedia framework

GStreamer is a pipeline-based multimedia framework which allows a programmer to create a variety of media-handling components, including simple audio playback, audio and video playback, recording and streaming. More info about gstreamer can be found here:

Install gstreamer1.x packages:

root@somlabs:~# apt-get update
root@somlabs:~# apt-get install gstreamer1.0-x gstreamer1.0-tools
root@somlabs:~# apt-get install gstreamer1.0-plugins-good 
root@somlabs:~# apt-get install gstreamer1.0-plugins-bad
root@somlabs:~# apt-get install gstreamer1.0-libav

At this point you could use:

root@somlabs:~# gst-launch-1.0 videotestsrc ! fbdevsink
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
<Ctrl+c>
Interrupt: Stopping pipeline ...
Execution ended after 0:00:04.732790131
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...

to display prebuild image/video pattern on the display, but it will not stretch to the display, and will not be hardware accelerated:

Wiki-somlabs-gstreamer-1.jpg

Adding GStreamer PxP engine support via gstreamer-imx

The multimedia performance of i.MX 6ULL processor is enhanced by a Pixel Processing Pipeline (PXP) to support 2D image processing, including color-space conversion, scaling, alpha-blending, and rotation. You can easily add PxP support via GStreamer and Gstreamer-imx plugins.

Install build dependencies for the gstreamer-imx package:

root@somlabs:~# apt-get install build-essential autoconf libtool
root@somlabs:~# apt-get install wget python pkg-config
root@somlabs:~# apt-get install libgstreamer1.0-dev
root@somlabs:~# apt-get install libgstreamer-plugins-base1.0-dev

Clone gstreamer-imx repository (if you don't already have git installed, simply install it with sudo apt install git):

root@somlabs:~# git clone git://github.com/Freescale/gstreamer-imx.git
Cloning into 'gstreamer-imx'...
remote: Counting objects: 4349, done.
remote: Total 4349 (delta 0), reused 0 (delta 0), pack-reused 4349
Receiving objects: 100% (4349/4349), 1.91 MiB | 1.35 MiB/s, done.
Resolving deltas: 100% (3129/3129), done.

To compile gstreamer-imx we need a Linux kernel headers with i.MX additions for the PxP subsystems (kernel headers where at least linux/pxp_device.h can be found). Let's install .deb package with kernel headers (prebuilt .deb package is available as a part of somlabs-visionsom-6ull-debian-rootfs-qemu.tar.xz image):

root@somlabs:~# dpkg -i linux-headers-4.1.15_4.1.15-1_armhf.deb

Gstreamer-imx project uses the waf meta build system, to configure project run:

root@somlabs:~# cd gstreamer-imx

root@somlabs:~/gstreamer-imx# ln -s /usr/lib/arm-linux-gnueabihf/gstreamer-1.0/ /usr/lib/gstreamer-1.0

root@somlabs:~/gstreamer-imx# ./waf configure --prefix=/usr --kernel-headers=/usr/src/linux-headers-4.1.15/include
Setting top to : /root/gstreamer-imx 
Setting out to : /root/gstreamer-imx/build 
Checking for 'gcc' (C compiler) : /usr/bin/gcc 
Checking for compiler switch -O2 : yes 
...
checking for linux/pxp_device.h : yes 
PxP elements will be built 
checking for linux/fb.h and the IPU header linux/ipu.h : yes 
IPU elements will be built 
Checking for 'libimxvpuapi >= 0.10.3' : not found 
could not find installed imxvpuapi library - VPU elements will not be built 
...
'configure' finished successfully (25.634s)

Now build and install gstreamer-imx:

root@somlabs:~/gstreamer-imx# ./waf 
Waf: Entering directory `/root/gstreamer-imx/build'
[ 1/35] Compiling src/common/canvas.c
[ 2/35] Compiling src/common/fd_object.c
[ 3/35] Compiling src/common/phys_mem_allocator.c
...
[27/35] Compiling src/compositor/gst-backport/gstimxbpvideoaggregator.c
[28/35] Compiling src/compositor/compositor.c
[29/35] Linking build/src/compositor/libgstimxcompositor.so
[30/35] Linking build/src/blitter/libgstimxblitter.so
[31/35] Linking build/src/ipu/libgstimxipu.so
[32/35] Linking build/src/pxp/libgstimxpxp.so
[33/35] Symlinking build/src/blitter/libgstimxblitter.so
[34/35] Symlinking build/src/compositor/libgstimxcompositor.so
[35/35] Symlinking build/src/common/libgstimxcommon.so
Waf: Leaving directory `/root/gstreamer-imx/build'
'build' finished successfully (1m45.921s)

root@somlabs:~/gstreamer-imx# ./waf install
Waf: Entering directory `/root/gstreamer-imx/build'
+ install /usr/lib/libgstimxcommon.so.0.13.0
+ symlink /usr/lib/libgstimxcommon.so (to libgstimxcommon.so.0.13.0)
+ symlink /usr/lib/libgstimxcommon.so.0 (to libgstimxcommon.so.0.13.0)
+ install /usr/lib/pkgconfig/gstimxcommon.pc
...
Waf: Leaving directory `/root/gstreamer-imx/build'
'install' finished successfully (0.584s)

After this step you should be able to see newly installed imx plugins with gst-inspect-1.0 tool (which allows to get documentation on available elements and detailed information on a specific element):

root@somlabs:~/gstreamer-imx# gst-inspect-1.0 | grep imx
imxpxp:  imxpxpvideotransform: Freescale PxP video transform
imxpxp:  imxpxpvideosink: Freescale PxP video sink

Inspect imxpxpvideosink element:

root@somlabs:~/gstreamer-imx# gst-inspect-1.0 imxpxpvideosink
Factory Details:
  Rank                     primary + 1 (257)
  Long-name                Freescale PxP video sink
  Klass                    Sink/Video
  Description              Video output using the Freescale PxP API
  Author                   Carlos Rafael Giani <dv@pseudoterminal.org>
…

Element Properties:
  name                : The name of the object
                        flags: readable, writable
                        String. Default: "imxpxpvideosink0"
  parent              : The parent of the object
                        flags: readable, writable
                        Object of type "GstObject"

Hardware-accelerated video-stream playback

Download sample video file:

wget http://craftymind.com/factory/html5video/BigBuckBunny_640x360.mp4

The easiest pipeline for decoding local file:

gst-launch-1.0 playbin uri=file://<file> video-sink=imxpxpvideosink

or

gst-launch-1.0 filesrc location=<file> ! decodebin ! imxpxpvideosink use-vsync=true
Wiki-somlabs-gstreamer-2.jpg

An example of more complex pipeline (picture-in-picture with videomixer component – logo.jpg file as a background, mixed with H264 video):

gst-launch-1.0 filesrc location=logo.jpg ! jpegdec ! imagefreeze ! videomixer name=mix sink_1::xpos=80 sink_1::ypos=100 ! imxpxpvideosink sync=false use-vsync=true filesrc location=video_001.mp4 ! decodebin ! mix.
Wiki-somlabs-gstreamer-3.jpg

‘Picture-in-picture’ example as a C application:

#include <stdlib.h>
#include <glib.h>
#include <gst/gst.h>

#define GST_VIDEOSINK	"imxpxpvideosink"
#define SINK1_X_OFFSET	80
#define SINK1_Y_OFFSET	100

#define SYNC_FALSE	1
#define VSYNC_TRUE	1

static GMainLoop *loop = NULL;
static guint timeout = 1;

/*
 * bus_call()
 */
static gboolean
bus_call (GstBus      *bus,
          GstMessage  *msg,
          gpointer     data)
{
  switch (GST_MESSAGE_TYPE (msg)) {

    /* due to ImageFreeze component in pipeline - 'EOS' case
     * should not be reached */
    case GST_MESSAGE_EOS:
      g_print ("   [error ] unexpected 'EOS' message\n");
      g_main_loop_quit (loop);
      break;

    case GST_MESSAGE_ERROR: {
      gchar  *debug;
      GError *error;

      gst_message_parse_error (msg, &error, &debug);
      g_free (debug);

      g_print ("   [error ] %s\n", error->message);
      g_error_free (error);

      g_main_loop_quit (loop);
      break;
    }

    default:
      break;
  }

  return TRUE;
}

/*
 * timeout_callback()
 *
 */
static gboolean
timeout_callback (gpointer data)
{
  g_print ("   [status] timeout callback\n");

  /* FIXME: send EOS to pipeline */
  g_main_loop_quit (loop);
  return FALSE;
}
static void
on_pad_added (GstElement  *element,
              GstPad      *pad,
              gpointer     data)
{
  GstElement *vidmixer = (GstElement *) data;
  GstPad     *sink_1;
  gchar      *name;

  name = gst_pad_get_name (pad);
  g_print ("   [status] new pad %s was created\n", name);

  /* set timeout */
  if (g_strcmp0 (name, "src_0") == 0)
    {
      g_print ("   [status] set timeout callback\n");
      g_timeout_add (timeout, timeout_callback, loop);
    }
  g_free (name);

  /* link decoder with videomixer */
  g_print ("   [status] on-pad-added callback\n");
  gst_element_link (element, vidmixer);

  /* set X/Y offsets for video sink_1 */
  sink_1 = gst_element_get_static_pad (vidmixer, "sink_1");
  g_object_set (sink_1, "ypos", SINK1_Y_OFFSET, NULL);
  g_object_set (sink_1, "xpos", SINK1_X_OFFSET, NULL);
  gst_object_unref (sink_1);
}

int
main (int    argc,
      char  *argv[])
{
  GstBus *bus;
  GstElement *pipeline, *im_source, *vd_source, *jpegdec;
  GstElement *imgfrez, *vidmixer, *decoder, *sink;
  gchar *logo, *filename;
  guint bus_watch_id;

  /* init */
  gst_init (&argc, &argv);
  if (argc != 4) {
    g_print ("   [error ] invalid input args\n");
    return -1;
  }

  logo = argv[1];
  filename = argv[2];
  timeout = (guint) atoi(argv[3]);
  loop = g_main_loop_new (NULL, FALSE);

  g_print ("   [status] configuration: logo=%s\n", filename);
  g_print ("   [status] configuration: filename=%s\n", filename);
  g_print ("   [status] configuration: timeout=%d\n", timeout);

  /* gstreamer elements */
  pipeline  = gst_pipeline_new ("somlabs-demo-player");
  im_source = gst_element_factory_make ("filesrc",     "image-source");
  vd_source = gst_element_factory_make ("filesrc",     "video-source");
  jpegdec   = gst_element_factory_make ("jpegdec",     "jpeg-decoder");
  imgfrez   = gst_element_factory_make ("imagefreeze", "image-freeze");
  vidmixer  = gst_element_factory_make ("videomixer",  "video-mixer");
  decoder   = gst_element_factory_make ("decodebin",   "decode-bin");
  sink      = gst_element_factory_make (GST_VIDEOSINK, "video-sink");

  if (!pipeline || !im_source || !vd_source || !jpegdec ||
      !imgfrez || !vidmixer || !decoder || !sink) {
    g_print ("   [error ] cannot create element for pipeline\n");
    return -1;
  }

  /* set up the pipeline */
  g_object_set (G_OBJECT (im_source), "location", logo, NULL);
  g_object_set (G_OBJECT (vd_source), "location", filename, NULL);

#if SYNC_FALSE
  g_print ("   [status] configuration: 'sync'=FALSE\n");
  g_object_set (G_OBJECT (sink), "sync", FALSE, NULL);
#endif

#if VSYNC_TRUE
  g_print ("   [status] configuration: 'use-vsync'=TRUE\n");
  g_object_set (G_OBJECT (sink), "use-vsync", TRUE, NULL);
#endif

  /* add message handler */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  bus_watch_id = gst_bus_add_watch (bus, bus_call, NULL);
  gst_object_unref (bus);

  /* add all elements into the pipeline */
  gst_bin_add_many (GST_BIN (pipeline), im_source, jpegdec, imgfrez,
                    vidmixer, sink, vd_source, decoder, NULL);

  /* link elements together */
  gst_element_link (im_source, jpegdec);
  gst_element_link (jpegdec, imgfrez);
  gst_element_link (vd_source, decoder);
  gst_element_link (imgfrez, vidmixer);
  gst_element_link (vidmixer,sink);
  g_signal_connect (decoder, "pad-added", G_CALLBACK (on_pad_added), vidmixer);

  /* set the pipeline to "playing" state */
  g_print ("   [status] pipeline: GST_STATE_PLAYING\n");
  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* setup timeout */
  g_timeout_add (timeout, timeout_callback, NULL);

  /* enter to main loop */
  g_print ("   [status] enter to mainloop...\n");
  g_main_loop_run (loop);

  /* out of the main loop, clean up nicely */
  g_print ("   [status] pipeline: GST_STATE_NULL\n");
  gst_element_set_state (pipeline, GST_STATE_NULL);

  g_print ("   [status] exiting...\n");
  gst_object_unref (GST_OBJECT (pipeline));
  g_source_remove (bus_watch_id);
  g_main_loop_unref (loop);

  return 0;
}

To compile and run app:

gcc som-player.c -o som-player `pkg-config --cflags --libs gstreamer-1.0`
./som-player


Ready to use image file

You can also use a prepared image file.

You need to download the image file: SoMLabs-VisionSOM-6ULL-Hardware-accelerated-video-playback.zip

Then you need to write the image file to microSD card. More information you can find in manuals: How to prepare SD Card with Debian 9.2 for VisionSOM-6ULL on Linux or How to prepare SD Card with Debian 9.2 for VisionSOM-6ULL on Windows.

NXP Partner ST Partner