Я создаю pipeline с помощью функции gstreamer gst_parse_launch, но всегда получаю 0, даже тестовые несложные конструкции не проходят. Мне необходимо передавать видеопоток с веб-камеры между двумя android устройствами. Устройства подключаются к ubuntu-серверу (через gRPC). Приложение написано на Unity. Gstreamer подключаю из libgstreamer_android.so, который сгенерировала с помощью CMake.
private string BuildGStreamerPipeline(string address, string port)
{
string pipelineStr = "videotestsrc ! videoconvert ! autovideosink";
return pipelineStr;
}
private void StartGStreamerPipeline(string serverAddress)
{
if (!GStreamerPlugin.isInitialized)
{
Debug.LogError("GStreamer is not Init!");
return;
}
InitializeVideoTexture();
var ipAddress = serverAddress.Split(':')[0];
string pipelineStr = BuildGStreamerPipeline(ipAddress, GST_PORT.ToString());
Debug.Log($"Using pipeline: {pipelineStr}");
IntPtr pipeline = GStreamerPlugin.CreatePipeline(pipelineStr);
Debug.Log("Pipeline: " + pipeline);
if (pipeline == IntPtr.Zero)
{
Debug.Log("Failed pipeline");
return;
}
bool stateResult = GStreamerPlugin.SetPipelineState(pipeline, GstState.GST_STATE_PLAYING);
if (!stateResult)
{
Debug.LogError("Pipeline state change failed");
return;
}
StartCoroutine(UpdatePreviewRoutine(pipeline));
}
Генерирую .so файл следующим образом:
cmake_minimum_required(VERSION 3.10...3.31)
set(CMAKE_SYSTEM_NAME Android)
set(ANDROID_PLATFORM android-29)
set(ANDROID_ABI arm64-v8a)
set(CMAKE_ANDROID_ARCH_ABI arm64-v8a)
project(gstreamer_android)
# GStreamer paths
set(GSTREAMER_ROOT_DIR "D:/gstreamer-1.0-android-universal-1.14.5")
set(GSTREAMER_ROOT_ANDROID ${GSTREAMER_ROOT_DIR})
set(GSTREAMER_ARCH "arm64")
set(GSTREAMER_LIB_DIR "${GSTREAMER_ROOT_ANDROID}/${GSTREAMER_ARCH}/lib")
set(GSTREAMER_PLUGINS_DIR "${GSTREAMER_ROOT_ANDROID}/${GSTREAMER_ARCH}/lib/gstreamer-1.0")
# Add core plugins needed for video streaming
set(GST_PLUGIN_CORE "${GSTREAMER_PLUGINS_DIR}/libgstcoreelements.a")
set(GST_PLUGIN_VIDEOCONVERT "${GSTREAMER_PLUGINS_DIR}/libgstvideoconvert.a")
set(GST_PLUGIN_VIDEOTESTSRC "${GSTREAMER_PLUGINS_DIR}/libgstvideotestsrc.a")
set(GST_PLUGIN_X264 "${GSTREAMER_PLUGINS_DIR}/libgstx264.a")
set(GST_PLUGIN_RTP "${GSTREAMER_PLUGINS_DIR}/libgstrtp.a")
set(GST_PLUGIN_UDP "${GSTREAMER_PLUGINS_DIR}/libgstudp.a")
set(GST_PLUGIN_APP "${GSTREAMER_PLUGINS_DIR}/libgstapp.a")
# Find Android log library
find_library(log-lib log)
# Include directories
include_directories(
${GSTREAMER_ROOT_ANDROID}/${GSTREAMER_ARCH}/include/gstreamer-1.0
${GSTREAMER_ROOT_ANDROID}/${GSTREAMER_ARCH}/include/glib-2.0
${GSTREAMER_ROOT_ANDROID}/${GSTREAMER_ARCH}/lib/glib-2.0/include
)
# Create the shared library
add_library(gstreamer_android SHARED
src/gstreamer_android.cpp
)
target_link_libraries(gstreamer_android
# Core GStreamer - order is important!
${GSTREAMER_LIB_DIR}/libgstreamer-1.0.a
${GSTREAMER_LIB_DIR}/libgstbase-1.0.a
${GSTREAMER_LIB_DIR}/libgstpbutils-1.0.a
${GSTREAMER_LIB_DIR}/libgstvideo-1.0.a
${GSTREAMER_LIB_DIR}/libgstapp-1.0.a
# Core GLib - these must come before other glib-dependent libraries
${GSTREAMER_LIB_DIR}/libglib-2.0.a
${GSTREAMER_LIB_DIR}/libgobject-2.0.a
${GSTREAMER_LIB_DIR}/libgmodule-2.0.a
${GSTREAMER_LIB_DIR}/libgthread-2.0.a
${GSTREAMER_LIB_DIR}/libgio-2.0.a
# Plugin elements - after core libraries
${GSTREAMER_PLUGINS_DIR}/libgstcoreelements.a
${GSTREAMER_PLUGINS_DIR}/libgstapp.a
${GSTREAMER_PLUGINS_DIR}/libgstvideotestsrc.a
${GSTREAMER_PLUGINS_DIR}/libgstvideoconvert.a
${GSTREAMER_PLUGINS_DIR}/libgstx264.a
${GSTREAMER_PLUGINS_DIR}/libgstrtp.a
${GSTREAMER_PLUGINS_DIR}/libgstudp.a
# Additional GStreamer libs
${GSTREAMER_LIB_DIR}/libgstnet-1.0.a
${GSTREAMER_LIB_DIR}/libgstcontroller-1.0.a
${GSTREAMER_LIB_DIR}/libgsttag-1.0.a
${GSTREAMER_LIB_DIR}/libgstrtp-1.0.a
${GSTREAMER_LIB_DIR}/libgstsdp-1.0.a
${GSTREAMER_LIB_DIR}/libgstrtsp-1.0.a
# Support libraries
${GSTREAMER_LIB_DIR}/libffi.a
${GSTREAMER_LIB_DIR}/libiconv.a
${GSTREAMER_LIB_DIR}/libintl.a
${GSTREAMER_LIB_DIR}/libz.a
# System libraries
${log-lib}
-landroid
-ldl
-lm
-latomic
# Link some libraries twice to resolve circular dependencies
${GSTREAMER_LIB_DIR}/libglib-2.0.a
${GSTREAMER_LIB_DIR}/libgobject-2.0.a
${GSTREAMER_LIB_DIR}/libgstreamer-1.0.a
)
# Add definitions
target_compile_definitions(gstreamer_android PRIVATE
ANDROID_NDK
GST_DISABLE_DEPRECATED
GST_USE_UNSTABLE_API
_GNU_SOURCE
HAVE_CONFIG_H
GST_PLUGIN_BUILD_STATIC
GLIB_STATIC_COMPILATION
G_DISABLE_DEPRECATED
)
#include <jni.h>
#include <android/log.h>
#include <gst/gst.h>
#include <gst/app/gstappsink.h>
#include <string>
#define TAG "GStreamer"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
static GstElement* pipeline = nullptr;
extern "C" {
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
LOGD("JNI_OnLoad called");
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
LOGE("Failed to get JNI environment");
return -1;
}
gst_init(nullptr, nullptr);
LOGD("GStreamer initialized successfully");
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
LOGD("JNI_OnUnload called");
if (pipeline) {
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
}
gst_deinit();
}
JNIEXPORT jboolean JNICALL
Java_com_yourpackage_GStreamerPlugin_gst_1is_1initialized(JNIEnv* env, jclass type) {
return static_cast<jboolean>(gst_is_initialized());
}
JNIEXPORT void JNICALL
Java_com_yourpackage_GStreamerPlugin_gst_1init(JNIEnv* env, jclass type) {
if (!gst_is_initialized()) {
gst_init(nullptr, nullptr);
}
}
JNIEXPORT jobject JNICALL
Java_com_yourpackage_GStreamerPlugin_gst_1parse_1launch(JNIEnv* env, jobject thiz, jstring pipelineStr) {
const char* pipeline_desc = env->GetStringUTFChars(pipelineStr, nullptr);
GError* error = nullptr;
pipeline = gst_parse_launch(pipeline_desc, &error);
if (error != nullptr) {
LOGE("Failed to create pipeline: %s", error->message);
g_error_free(error);
env->ReleaseStringUTFChars(pipelineStr, pipeline_desc);
return nullptr;
}
env->ReleaseStringUTFChars(pipelineStr, pipeline_desc);
return env->NewDirectByteBuffer(pipeline, 0);
}
JNIEXPORT jboolean JNICALL
Java_com_yourpackage_GStreamerPlugin_gst_1element_1set_1state(JNIEnv* env, jobject thiz, jobject pipelineRef, jint state) {
GstElement* pipeline = static_cast<GstElement*>(env->GetDirectBufferAddress(pipelineRef));
GstStateChangeReturn ret = gst_element_set_state(pipeline, static_cast<GstState>(state));
return ret != GST_STATE_CHANGE_FAILURE;
}
JNIEXPORT jboolean JNICALL
Java_com_yourcompany_GStreamerNative_gst_1app_1sink_1pull_1sample(JNIEnv* env, jobject thiz, jlong sink_ptr, jbyteArray buffer)
{
GstAppSink* appsink = GST_APP_SINK(reinterpret_cast<GstElement*>(sink_ptr));
GstSample* sample = gst_app_sink_pull_sample(appsink);
if (sample) {
GstBuffer* buf = gst_sample_get_buffer(sample);
GstMapInfo map;
if (gst_buffer_map(buf, &map, GST_MAP_READ)) {
env->SetByteArrayRegion(buffer, 0, map.size, reinterpret_cast<jbyte*>(map.data));
gst_buffer_unmap(buf, &map);
gst_sample_unref(sample);
return JNI_TRUE;
}
gst_sample_unref(sample);
}
return JNI_FALSE;
}
}
Перемещено Dimez из general