Tuesday, November 7, 2017

Fix Matlab VideoReader plugin libmwgstreamerplugin failed to load properly issue

When I run the following command on Matlab R2017a from CentOS 6.9
v = VideoReader('test.avi');
I get the following error:
Error using VideoReader/init (line 619)
The VideoReader plugin libmwgstreamerplugin failed to load properly.
Error in VideoReader (line 172)
            obj.init(fileName);
The reason is that old versions of Matlab used to rely on gstreamer-0.10 to process video but from at lease R2014a the dependency of gstreamer library has changed to gstreamer-1.0 if you want to use VideoReader('video.avi') command. So upgrade your OS will be the easiest way to solve this issue but if this is not possible as I am facing, then upgrade the gstreamer library is the only solution.

To check which gstreamer you need check the dependencies of Matlab libmwgstreamerplugin.so by readelf command:
# cd /usr/local/MATLAB/R2017a/toolbox/shared/multimedia/bin/glnxa64/reader
# readelf -d libmwgstreamerplugin.so

Dynamic section at offset 0xd780 contains 43 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libmwmultimediacommonexceptions.so]
 0x0000000000000001 (NEEDED)             Shared library: [libmwtamimframe.so]
 0x0000000000000001 (NEEDED)             Shared library: [libmwi18n.so]
 0x0000000000000001 (NEEDED)             Shared library: [libboost_system.so.1.56.0]
 0x0000000000000001 (NEEDED)             Shared library: [libmwcpp11compat.so]
 0x0000000000000001 (NEEDED)             Shared library: [libtbb.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libtbbmalloc.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libgstvideo-1.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgstbase-1.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgstreamer-1.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgobject-2.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgmodule-2.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgthread-2.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [librt.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libxml2.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libglib-2.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgstapp-1.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [$ORIGIN:$ORIGIN/../../../../../../bin/glnxa64:$ORIGIN/../../../../../../sys/os/glnxa64]
 0x000000000000000c (INIT)               0x43e8
 0x000000000000000d (FINI)               0xacf8
 0x0000000000000004 (HASH)               0x1b8
 0x000000006ffffef5 (GNU_HASH)           0x5e8
 0x0000000000000005 (STRTAB)             0x1368
 0x0000000000000006 (SYMTAB)             0x6d8
 0x000000000000000a (STRSZ)              4804 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x20db48
 0x0000000000000002 (PLTRELSZ)           1896 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x3c80
 0x0000000000000007 (RELA)               0x27c8
 0x0000000000000008 (RELASZ)             5304 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x2738
 0x000000006fffffff (VERNEEDNUM)         4
 0x000000006ffffff0 (VERSYM)             0x262c
 0x000000006ffffff9 (RELACOUNT)          114
 0x0000000000000000 (NULL)               0x0
Above is R2017a library dependencies and libgstreamer-1.0.so.0 is required. But on CentOS 6.x there is no existing gstreamer-1.0 package AFAIK, build it from the source code is my only option and here are the steps.

Step 1: Check your OS version

# cat /etc/centos-release
CentOS release 6.9 (Final)

If your CentOS is under version 7, then you need do following steps, otherwise these steps will not fit.

Step 2:  Upgrade GCC

To compile gstreamer-1.0 requires gcc 4.5 or above but CentOS 6.x only ships gcc 4.4.7 which need be upgraded first.

# gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-18)
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# yum install centos-release-scl
# yum install devtoolset-3-toolchain
After install devtoolset, you need enable it

# scl enable devtoolset-3 bash
Now to check the gcc version

# gcc --version
gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6)
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Step 3: Upgrade Python

Python 2.7 or above is required to compile gstreamer-1.0 but CentOS 6.x only provides Python 2.6.x and the operating system is heavily depends on it which should NOT be replaced by Python 2.7 or newer version. So make sure when you install Python 2.7, choose "make altinstall"!

# yum install -y centos-release-SCL
# yum install -y python27
# cd ~
# wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz
# tar xzf Python-2.7.14.tgz
# cd Python-2.7.14
# ./configure
# make altinstall
# echo "alias python='/usr/local/bin/python2.7'" >> ~/.bashrc
# . ~/.bashrc

Optional install pip:
# curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
# python2.7 get-pip.py

 Step 4: Install yasm 1.3.0

The performance of gstreamer-1.0 openh264 and x264 plugins can be dramatically improved by hardware acceleration which requires yasm optimization. But again the CentOS 6.x default yasm is too old which is not supported by gstreamer-1.0.

# cd ~
# curl -O -L http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
# tar xzvf yasm-1.3.0.tar.gz
# cd yasm-1.3.0
# ./configure
# make
# make install

Step 5: Prepare cerbero

Cerbero is the tool recommended by gstreamer team and it need some prerequisites to work properly. First is to create the default profile and target folder:

# mkdir -p ~/.cerbero/
# vi ~/.cerbero/cerbero.cbc
Paste following content to the file
----------------------------------------------------------
import os
from cerbero.config import Platform, Architecture, Distro

packages_prefix = 'gstreamer-sdk'
packager = 'GStreamer SDK packagers <packages@gstreamer.com>'
prefix = "/opt/gstreamer-sdk"

git_root = "git://anongit.freedesktop.org/gstreamer-sdk"

# Uncomment to allow parallel builds
# allow_parallel_build = True

# Uncomment this to allow cross-building for another architecture
# target_arch = Architecture.X86_64
----------------------------------------------------------

# mkdir -p /opt/gstreamer-sdk
 I am using /opt/gstreamer-sdk/ as the target folder. Then you need download the cerbero framework and set it up:

# cd ~
# git clone git://anongit.freedesktop.org/gstreamer/cerbero
# echo "alias cerbero='~/cerbero/cerbero-uninstalled'" >> ~/.bashrc
# . ~/.bashrc
# cd ~/cerbero
# cerbero bootstrap

Step 6: Build gstreamer-1.0

Finally we can start building gstreamer-1.0 but there are couple of catches in the recipes need be fixed first.

cdparanoia: post install step will fail because the global environment FilesProvider.LIBS_CAT is empty and I don't know how to change it. So my simple solution is to hard coded it!

# vi ~/cerbero/recipes/cdparanoia.recipe
----------------------------------------------------------
            #libs = self.files_list_by_category(FilesProvider.LIBS_CAT)
            #for lib in libs:
            for lib in self.files_libs:
                f = os.path.join(self.config.prefix + '/lib', lib + '.so.0.' + self.version)
                print(f)
----------------------------------------------------------
The post install steps are just to change the permissions on the generated .so files, so find the files in target folder first and work out the hard code values. Be aware of the indents of your code and print(f) is just for fun to show you the changes worked :D.

# cd /opt/gstreamer-sdk/lib
# ls | grep cdda
libcdda_interface.a
libcdda_interface.so
libcdda_interface.so.0
libcdda_interface.so.0.10.2
libcdda_paranoia.a
libcdda_paranoia.so
libcdda_paranoia.so.0
libcdda_paranoia.so.0.10.2
 nettle: has the exact issue as cdparanoia but a bit more complex with two different libraries with two different versions XD.

# vi ~/cerbero/recipes/nettle/nettle.recipe
----------------------------------------------------------
            #libs = self.files_list_by_category(FilesProvider.LIBS_CAT)
            #for lib in libs:
            for lib in self.files_libs:
                f = os.path.join(self.config.prefix + '/lib', lib)
                print(f)
                if fnmatch(f, '*libnettle*'):
                   f = f + '.so.6.3'
                if fnmatch(f, '*libhogweed*'):
                   f = f + '.so.4.3'
                print(f)
----------------------------------------------------------

# cd /opt/gstreamer-sdk/lib
# ls | grep nettle
libnettle.a
libnettle.la
libnettle.so
libnettle.so.6
libnettle.so.6.3
# ls | grep hogweed
libhogweed.a
libhogweed.la
libhogweed.so
libhogweed.so.4
libhogweed.so.4.3
 flac: you need specify -lrt to enable real-time functions, otherwise the linker will complain undefined reference to 'clock_gettime'.

# vi ~/cerbero/recipes/flac.recipe
----------------------------------------------------------
    def prepare(self):
        if self.config.target_platform in [Platform.DARWIN, Platform.IOS]:
            if self.config.target_arch == Architecture.X86:
                self.configure_options += ' --disable-asm-optimizations'
        if self.config.target_platform in [Platform.ANDROID, Platform.IOS]:
            self.autoreconf = True
        if self.config.target_platform == Platform.LINUX:
            self.new_env['LIBS'] = '-lrt'
----------------------------------------------------------
 OK, we have everything ready, pull the trigger:

# cerbero package gstreamer-1.0
 Unfortunately the above command cannot generate the proper RPM package and since I only need solve the issue for Matlab, this is not a big problem to me, I only need the compiled run-time libraries.

Step 7: Let Matlab using the new gstreamer-1.0

The quick way is to change LD_LIBRARY_PATH

# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/gstreamer-sdk/lib
# ldconfig

And the permanent way is to update ld.so.conf.d

# vi /etc/ld.so.conf.d/gstreamer.conf
----------------------------------------------------------
/opt/gstreamer-sdk/lib
----------------------------------------------------------
# reboot
Now Matlab R2017a is able to load video files:

>> obj=VideoReader('test.avi');
0:00:00.138478543  7933 0x7f339c007b70 ERROR                  libav :0:: Context scratch buffers could not be allocated due to unknown size.
0:00:00.138711800  7933 0x7f339c007b70 ERROR                  libav :0:: Context scratch buffers could not be allocated due to unknown size.
>>
The ERROR is caused by the leading black frames in the video file, not the gstreamer-1.0 package issue.

Just uploaded the compiled folder to Google Drive https://drive.google.com/open?id=1jUL2q9LvpTcNtEbVtpaugJUPVBHoYsJ8. After you download the gstreamer-sdk.tgz file, just do following

# cd /opt/
# mv ~/Downloads/gstreamer-sdk.tgz .
# tar zxf gstreamer-sdk.tgz
Then add gstreamer.conf as Step 7 described. I have tested it on CentOS 6.9, CentOS 7.5, Ubuntu 18.04 lts along with Matlab R2017a, R2017b and R2018b.

WARNING about Ubuntu 18.04 lts with NVidia video card, the gstreamer libraries will cause you desktop failed to start, in this case, please boot into recovery mode and delete the file /etc/ld.so.conf.d/gstreamer.conf and run ldconfig. Use the official packages instead:

# apt-get install libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools

SIZEOFINFINITY[∞]

Thursday, June 29, 2017

Modify Dell multimedia keyboard from USB bus powered to self-powered

As a hobby I really enjoy thinking and tinkering electronic components and circuit board. That's why I rushed into online shop Core Electronics and fetched my Raspberry Pi Zero Wireless when it was released. Soon I discovered RP Zero is not like RP 3 as a self-contained computer, it requires quite a few attachments to make it work especially the USB hub. RP Zero only provides one USB port and you have to use a powered USB hub in order to connect multiple devices.

So I took out my Dell multimedia keyboard model number KB522 which looks like this
The keyboard comes with two USB ports which is very useful when you connect it to RP Zero, I have no problem to connect the keyboard with a mouse hook to one of the keyboard USB ports. Now the fun part comes, I need copy some files from my thumb drive to RP Zero and of course I just plug my thumb drive to another port and think it will work but the answer is NO. I cannot access my thumb drive from RP Zero at all! What happened?

As a sanity test, I plugged the keyboard to my desktop which is running Windows 7 and as soon as I inserted the thumb drive, Windows complained USB Hub Power Exceeded. What does this error mean? Microsoft's answer is as useless as usual with suggestions that you should change to another port or you have connected too many devices to your hub. OK, I'll find the real answer by myself. By checking the Generic USB Hub properties from Device Manager I discovered the USB keyboard is bus powered and the maximum current for each port is 100mA but my thumb drive need 200mA to run. Alright the answer is my keyboard USB hub need more power from external.

I started my searching of adding external power to USB hub and Google gives me these two very useful article Adding an External Power Supply to a Cheap USB Hub and How to add an external power supply to a USB hub. The first article implies we can simply connect an external power supply to the input USB positive and ground pin to provide additional current to the hub. The later article is more elegant to allow both bus powered and self powered modes. So I tried the first solution by soldering an additional USB cable to the input USB pins but no success, no matter I plug the external power supply or not, Windows still recognise the USB hub as bus powered.

Then I have to go deeper to check the chip

(This picture already contains my external power supply cable.)

The chip is FE1.1 and soon I found the datasheet and demo board picture of this chip.
Apparently the chip supports powered Hub feature but how can I make the Dell keyboard USB hub to powered hub? So I started searching the USB hub schematic design and found following very useful information


Pin 36 BUS_B is used to control the working mode of FE1.1 to be bus powered or self powered. Then I used my multi-meter to find out that the blank R21 point is connected to pin 36.
(The red rectangle contains the two connected points.)
I have tried to short the resistor R24 to see whether the hub will be self powered or not but failed. According to above schematic designs the BUS_B has to connect to 5V to enable self powered mode. So here we go

(Left cut the computer 5v line and insert a 2A diode, right connect the R21 point with the external 5v)
Finally put everything together, I do need enlarge the cable hole on the back cover to run the additional power cable

I use a 5v 1A power supply and bingo everything is working happily now :D.
Happy hacking and enjoy!
BTW, this is my Tictac RetroPi and my son is playing Pokémon on it :)




SIZEOFINFINITY[∞]