Introduction: GStreamer И Android (GStreamer Android Studio Windows)
Версия библиотек GStreamer:
- на RPi 3B (Raspberry OS Buster) 1.14.4,
- на Android 1.18.3.
Версия Android Studio 4.1.2, ОС Windows 7 x86-64.
Attachments
Step 1: GStreamer На Raspbian
Установка gstreamer (https://gstreamer.freedesktop.org/documentation/installing/index.html) на Raspberry Pi:
- выполняем команду «aptitude search gstreamer1.0» она выведет доступные к установке пакеты;
- необходимо как минимум установить следующие пакеты: «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»;
- для включения 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»;
- для получения исходников примеров gstreamer можно выполнить команду «git clone https://gitlab.freedesktop.org/gstreamer/gst-docs».
Для получения изображения с камеры RPI через gstreamer по сети с использованием плагина udpsink необходимо в консоли ввести команду:
- для 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»;
- для 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' } } }
- Остальные настройки по этому описанию: https://stackoverflow.com/questions/45044210/gstreamer-examples-in-android-studio. Если резюмировать, то получится так:
- создаём код на си;
- оказывается в общем случае 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»; - перед тем как начать писать код на 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» будет куча ошибок, чтобы их исправить нужно удалить все слова вида «@...что то написано...@»;
- если планируется использование androidmedia плагина (а это почти 100 %), то необходимо из каталога с распакованными предварительно собранными библиотеками (у меня это «C:\gstreamer\arm64\share\gst-android\ndk-build\») скопировать каталог «androidmedia» (в нём 3 файла .java) и вставить его в пакет «org.freedesktop.gstreamer»;
- в процессе создания кода на «си» android studio будет ругаться, что не может найти «#include », иногда это решается если выполнить команду «File->Invalidate Caches/Restart», этого достаточно сделать один раз, позже он всё равно будет ругаться на этот «#include » но сборка будет проходить нормально (в общем случае достаточно 1 раз сделать процедуру, после чего забить на ругань android studio на методы из «gst/gst.h»);
- если сборка заканчивается ошибкой например «android studio gstreamer make: *** No rule to make target tutorial_02.c, needed by tutorial_02.o», то просто внимательно проверьте ваш «Android.mk» скорее всего в нём лишний символ (в конце строк не должно быть пробелов);
- рекомендую ознакомиться с этой статьёй: 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 Гбайт), но у меня конвейер не стартует при другом раскладе, может у Вас некоторые плагины окажутся не нужны - надо тестировать.