Introduction: GStreamer И Android (GStreamer Android Studio Windows)

About: Вот, увлёкся Ардуино, Raspberry Pi, android.

Версия библиотек GStreamer:

  1. на RPi 3B (Raspberry OS Buster) 1.14.4,
  2. на Android 1.18.3.

Версия Android Studio 4.1.2, ОС Windows 7 x86-64.

Step 1: GStreamer На Raspbian

Установка gstreamer (https://gstreamer.freedesktop.org/documentation/installing/index.html) на Raspberry Pi:

  1. выполняем команду «aptitude search gstreamer1.0» она выведет доступные к установке пакеты;
  2. необходимо как минимум установить следующие пакеты: «gstreamer1.0-tools», «gstreamer1.0-plugins-good», «gstreamer1.0-plugins-good-doc», «gstreamer1.0-plugins-base», «gstreamer1.0-doc», «libgstreamer1.0-0», «gstreamer1.0-rtsp», «libgstreamer-plugins-base1.0-dev», «gstreamer1.0-plugins-base-apps», «gstreamer1.0-plugins-bad» и все зависимости. Команда будет «sudo apt-get install gstreamer1.0-tools gstreamer1.0-plugins-good gstreamer1.0-plugins-good-doc gstreamer1.0-plugins-base gstreamer1.0-doc libgstreamer1.0-0 gstreamer1.0-rtsp libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base-apps gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly»;
  3. для включения gstreamer в код,необходимо включить «#include », при компиляции программы необходимо добавлять «`pkg-config --cflags --libs gstreamer-1.0`», получится так «gcc `pkg-config --cflags --libs gstreamer-1.0` basic-tutorial-1.c -o basic-tutorial-1»;
  4. для получения исходников примеров gstreamer можно выполнить команду «git clone https://gitlab.freedesktop.org/gstreamer/gst-docs».

Для получения изображения с камеры RPI через gstreamer по сети с использованием плагина udpsink необходимо в консоли ввести команду:

  1. для RPi камеры: «raspivid -t 999999 -h 1080 -w 1920 -fps 25 -hf -vf -b 6000000 -o - | gst-launch-1.0 -v fdsrc ! h264parse ! rtph264pay config-interval=1 pt=96 ! udpsink host=192.168.1.223 port=5000»;
  2. для USB MJPEG камеры: «gst-launch-1.0 v4l2src device=/dev/video0 do-timestamp=true ! image/jpeg,width=1920,height=1080,framerate=30/1 ! avdec_mjpeg ! omxh264enc control-rate=1 target-bitrate=8000000 ! h264parse ! rtph264pay config-interval=1 pt=96 ! udpsink host=192.168.1.223 port=5000»,

где host — ip-адрес получателя потока (смартфон на андроид), что делает каждый плагин (fdsrc, h264parse, rtph264pay, ...) Вы можете узнать с помощью команды «gst-inspect-1.0 fdsrc», «gst-inspect-1.0 h264parse» и т.д.

Для получения видео потока на винде (после установки gstreamer) по сети с использованием плагина udpsrc необходимо в консоли ввести команду: «c:\gstreamer\1.0\msvc_x86_64\bin\gst-launch-1.0 -v udpsrc port=5000 ! application/x-rtp,encoding-name=H264 ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false» (у меня Windows 7 64 бит, GStreamer установлен в «c:\gstreamer\»).

Забегая вперёд скажу что для Samsung S7 получение видео потока на android (конвейер с использованием udpsrc) «udpsrc port=5000 ! application/x-rtp,encoding-name=H264 ! rtph264depay ! h264parse ! amcviddec-omxexynosavcdec ! videoconvert ! autovideosink».

Step 2: Android. Введение. Создание Приложения С NDK, JNI, Компиляция Ndk-build

Для начала нужно чтобы нормально создавалось приложение с NDK, JNI с компиляцией с помощью ndk-build (https://developer.android.com/ndk/guides, https://habr.com/ru/post/203014/):

  • устанавливаем в Windows переменную среды «GSTREAMER_ROOT_ANDROID», значение - путь до каталога с распакованными (установленными) библиотеками gstreamer (prebuilt) https://gstreamer.freedesktop.org/data/pkg/android/1.18.3/ (в моём случае это «C:\gstreamer», последний слэш ставить не нужно);
  • устанавливаем NDK, CMake, LLDB (тут только галочки расставить);
  • создаём новое приложение с типом «Native C++» (самый низ списка), дожидаемся пока все процессы завершатся;
  • в левом окне выбираем вид «Project» (выпадающий список в левом верхнем углу), раскрываем ветку до каталога «app» включительно, нажимаем по «app» правой клавишей мыши и выбираем пункт меню «New->Folder->JNI Folder», в появившемся окне ставим галочку «Change Folder Location», в появившейся строке «New Folder Location» оставляем строку «jni/», нажимаем кнопку «Finish», в каталоге «app» появится элемент «jni»;
  • жмём правой клавишей мыши по элементу «jni», выбираем пункт меню «New->C/C++ Source File», в появившемся окне выбираем расширение файла, и вводим название (если пока ничего не планируется то можно, например дать имя «dummy»), нажимаем кнопку «OK» и будет создан файл;
  • жмём правой клавишей мыши по элементу «jni», выбираем пункт меню «New->File», в появившемся окне вводим имя «Android.mk», минимальное содержимое, если вы создали «dummy.c» может быть таким:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := dummy
LOCAL_SRC_FILES := dummy.c
include $(BUILD_SHARED_LIBRARY)
  • жмём правой клавишей мыши по элементу «jni», выбираем пункт меню «New->File», в появившемся окне вводим имя «Application.mk», минимальное содержимое может быть таким:
APP_ABI := all

теперь в левом окне выбираем вид «Android» (выпадающий список в левом верхнем углу), раскрываем ветку «Gradle Scripts» и открываем файл «build.gradle Module: app», в файле удаляем оба включения таких строк:

ExternalNativeBuild {
    CMake...
}
  • сохраняемся и жмём «sync gradle» в верхнем правом углу, ждём когда процесс спокойно завершится;
  • после всех этих процедур нам необходимо нажать правой клавишей мыши на элемент «app» и наконец то в меню появится так нужный нам пункт меню «Link C++ Project with Gradle», нажимаем на него.
  • Появится окно, в нём в пункте «Build System» вместо «CMake» нужно в списке выбрать «ndk-build», после чего в «Project Path» указать путь до файла «Android.mk» (в окне выбора файла нажимаем комбинацию клавиш «Ctrl + 2», разворачиваем каталог проекта, далее «app/jni», выбираем созданный нами файл «Android.mk», нажимаем «ОК»), ждём пока завершится процесс синхронизации, либо инициируем его «File->Sync Project with Gradle Files».
  • В нашем дереве, в каталоге «cpp» появятся добавленные нами файлы, но самое главное — это то, что теперь проект нормально соберётся, если у Вас в коде ошибок нет (урок для начинающих: https://developer.android.com/ndk/samples/sample_hellojni#java).

Step 3: Прикручиваем GStreamer.

Процесс прикручивания библиотеки gstreamer (версия библиотек 1.18.3, версия Android Studio 4.1.2):

  • Создаём проект с NDK, JNI, компиляция ndk-build (Step 2).
  • Содержимое «Android.mk» взято из «tutorial_1» примеров gstreamer и модифицировано, я распаковал библиотеки для андроид в каталог «C:\gstreamer\», файл на языке си я назвал (и предварительно добавил в папку «jni» проекта) «gst_tut_01.c», соответственно shared-библиотеку (которая будет собрана из этого файла) я назвал «gst_tut_01» (сам файл библиотеки, соответственно будет называться «libgst_tut_01.so»).
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE    := gst_tut_01
LOCAL_SRC_FILES := gst_tut_01.c
LOCAL_SHARED_LIBRARIES := gstreamer_android
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
    GSTREAMER_ROOT := C:/gstreamer/armv7
    GSTREAMER_NDK_BUILD_PATH := C:/gstreamer/armv7/share/gst-android/ndk-build/
else ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
    GSTREAMER_ROOT := C:/gstreamer/arm64
    GSTREAMER_NDK_BUILD_PATH := C:/gstreamer/arm64/share/gst-android/ndk-build/
else ifeq ($(TARGET_ARCH_ABI), x86)
    GSTREAMER_ROOT := C:/gstreamer/x86
    GSTREAMER_NDK_BUILD_PATH := C:/gstreamer/x86/share/gst-android/ndk-build
else ifeq ($(TARGET_ARCH_ABI), x86_64)
    GSTREAMER_ROOT := C:/gstreamer/x86_64
    GSTREAMER_NDK_BUILD_PATH := C:/gstreamer/x86_64/share/gst-android/ndk-build/
endif

GSTREAMER_PLUGINS := coreelements
GSTREAMER_EXTRA_LIBS := -liconv
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk

Обращаю внимание, что в «Android.mk», не зависимо от платформы (Windows, Linux), путь прописывается через «/».

  • Нужно создать блок «externalNativeBuild {...}» в файле «build.gradle Module: app» в ветке «defaultConfig» (показано с начала блока, чтобы было видно куда вставлять).
defaultConfig {
    applicationId "com.example.test_gst_01"
    minSdkVersion 23
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    externalNativeBuild {
        ndkBuild {
            def gstRoot
            if (project.hasProperty('gstAndroidRoot'))
                gstRoot = project.gstAndroidRoot
            else
                gstRoot = System.env.GSTREAMER_ROOT_ANDROID
            arguments "NDK_APPLICATION_MK=jni/Application.mk", "GSTREAMER_JAVA_SRC_DIR=src", "GSTREAMER_ROOT_ANDROID=$gstRoot", "GSTREAMER_ASSETS_DIR=src/assets"
            targets "gst_tut_01"
            // All archs except MIPS and MIPS64 are supported
            abiFilters  'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
     }
   }
}
  1. создаём код на си;
  2. оказывается в общем случае android studio не всегда нормально по умолчанию прилинковывает библиотеку «libc++_shared.so», поэтому лучше если в Вашем «Android.mk» вместо строки «LOCAL_SHARED_LIBRARIES := gstreamer_android» будет «LOCAL_SHARED_LIBRARIES := gstreamer_android c++_shared» ,
    в Android Studio 3.5.1 также в файле «Application.mk» добавить строчку «APP_STL := c++_shared»;
  3. перед тем как начать писать код на Java, для исключения проблем с импортом «org.freedesktop.gstreamer.GStreamer» каталога с распакованными предварительно собранными библиотеками (у меня это «C:\gstreamer\arm64\share\gst-android\ndk-build\») скопировать файл «GStreamer.java» (эти файлы на всех архитектурах одинаковые) и вставить его в проект в пакет «org.freedesktop.gstreamer», в свою очередь для этого в android studio необходимо переключить вид дерева проектов на «Android», кликаем правой клавишей мыши на элемент «java» и выбираем пункт меню «New->Package», появится диалог «Choose Destination Directory», выбираем «...\app\src\main\java», жмём «ОК», появится диалог «New Package», в строке ввода нужно ввести «org.freedesktop.gstreamer», в дереве появится созданный элемент, теперь необходимо нажать правой клавишей по новому элементу и выбрать пункт меню «Paste». В файле «GStreamer.java» будет куча ошибок, чтобы их исправить нужно удалить все слова вида «@...что то написано...@»;
  4. если планируется использование androidmedia плагина (а это почти 100 %), то необходимо из каталога с распакованными предварительно собранными библиотеками (у меня это «C:\gstreamer\arm64\share\gst-android\ndk-build\») скопировать каталог «androidmedia» (в нём 3 файла .java) и вставить его в пакет «org.freedesktop.gstreamer»;
  5. в процессе создания кода на «си» android studio будет ругаться, что не может найти «#include », иногда это решается если выполнить команду «File->Invalidate Caches/Restart», этого достаточно сделать один раз, позже он всё равно будет ругаться на этот «#include » но сборка будет проходить нормально (в общем случае достаточно 1 раз сделать процедуру, после чего забить на ругань android studio на методы из «gst/gst.h»);
  6. если сборка заканчивается ошибкой например «android studio gstreamer make: *** No rule to make target tutorial_02.c, needed by tutorial_02.o», то просто внимательно проверьте ваш «Android.mk» скорее всего в нём лишний символ (в конце строк не должно быть пробелов);
  7. рекомендую ознакомиться с этой статьёй: https://habr.com/ru/post/270479/, в ней приводятся «Методы лечения различных ошибок в Android Studio при разработке проекта».

Step 4: Конвейер GStreamer В Коде *.c Android Studio

Итак, GStreamer вроде заработал, по крайней мере работают tutorial_1 … tutorial_3 из документации «GStreamer Android Tutorials» (не смотря на то, что в смартфоне они запустились, мой вариант, основанный на tutorial_3 в эмуляторе вылетает).

Но вот с конвейером беда, в соответствии с этой информацией: https://gstreamer.freedesktop.org/documentation/installing/for-android-development.html#using-androidstudio в андроид есть свой декодер видео, называется он «androidmedia» и расположен в категории «GSTREAMER_PLUGINS := $(GSTREAMER_PLUGINS_CODECS)», такой плагин в библиотеке 1.15.2 GStreamer для Android Studio есть, но приложение будет ругаться что найти его (androidmedia) не может.
А не может найти потому что, оказывается называется он немного по другому (http://www.ohandroid.com/67892.html), а именно (в моём случае для телефона Samsung S7) «amcviddec-omxexynosavcdec», где префикс «amcviddec-» всегда один и тот же (для всех телефонов), а вот наименование второй части можно составить исходя из информации в файле «/etc/media_codecs.xml» (файл находится в смартфоне, я смотрел с помощью Total Commander), в частности для моего телефона в файле содержится запись «<MediaCodec name=”OMX.Exynos.avc.dec” type=”video/avc”>»,(avc - это кодек h264, для кодека h265 будет hevc, а для h263 - h263) соответственно убирая из имени кодека точки и записывая всё маленькими буквами получаем «omxexynosavcdec», вот теперь мы и определились с именем плагина — декодера «amcviddec-omxexynosavcdec».

Далее необходимо установить права в манифесте приложения:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-feature android:glEsVersion="0x00020000"/>

Плагины в «Android.mk»:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE    := gstvideoget
LOCAL_SRC_FILES := gstvideoget.c
LOCAL_SHARED_LIBRARIES := gstreamer_android c++_shared
LOCAL_LDLIBS := -llog -landroid
include $(BUILD_SHARED_LIBRARY)

ifndef GSTREAMER_ROOT_ANDROID
$(error GSTREAMER_ROOT_ANDROID is not defined!)
endif

ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
GSTREAMER_ROOT := $(GSTREAMER_ROOT_ANDROID)/armv7
else ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
GSTREAMER_ROOT := $(GSTREAMER_ROOT_ANDROID)/arm64
else ifeq ($(TARGET_ARCH_ABI),x86)
GSTREAMER_ROOT := $(GSTREAMER_ROOT_ANDROID)/x86
else ifeq ($(TARGET_ARCH_ABI),x86_64)
GSTREAMER_ROOT:= $(GSTREAMER_ROOT_ANDROID)/x86_64
else
$(error Target arch ABI not supported: $(TARGET_ARCH_ABI))
endif

GSTREAMER_NDK_BUILD_PATH := $(GSTREAMER_ROOT)/share/gst-android/ndk-build
include $(GSTREAMER_NDK_BUILD_PATH)/plugins.mk
GSTREAMER_PLUGINS := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_PLAYBACK) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_NET) $(GSTREAMER_PLUGINS_SYS) $(GSTREAMER_PLUGINS_CODECS_RESTRICTED) $(GSTREAMER_CODECS_GPL) $(GSTREAMER_PLUGINS_ENCODING) $(GSTREAMER_PLUGINS_VIS) $(GSTREAMER_PLUGINS_EFFECTS) $(GSTREAMER_PLUGINS_NET_RESTRICTED)
GSTREAMER_EXTRA_DEPS := gstreamer-player-1.0 gstreamer-video-1.0 glib-2.0
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk

Содержимое «Application.mk» (https://developer.android.com/ndk/guides/cpp-support):

APP_ABI := all
APP_STL := c++_shared

Послесловие: возможно плагинов слишком много (при компиляции размер папки проекта вырастает до 1,9 Гбайт), но у меня конвейер не стартует при другом раскладе, может у Вас некоторые плагины окажутся не нужны - надо тестировать.