Commit 0f2705d2 authored by Matej Frančeškin's avatar Matej Frančeškin

User hardware video decoding on Android

parent 683ffe67
...@@ -184,6 +184,8 @@ public class QGCActivity extends QtActivity ...@@ -184,6 +184,8 @@ public class QGCActivity extends QtActivity
public static native void qgcLogDebug(String message); public static native void qgcLogDebug(String message);
public static native void qgcLogWarning(String message); public static native void qgcLogWarning(String message);
private static native void nativeInit();
// QGCActivity singleton // QGCActivity singleton
public QGCActivity() public QGCActivity()
{ {
...@@ -745,5 +747,10 @@ public class QGCActivity extends QtActivity ...@@ -745,5 +747,10 @@ public class QGCActivity extends QtActivity
} }
}).start(); }).start();
} }
public void jniOnLoad()
{
nativeInit();
}
} }
...@@ -71,6 +71,8 @@ VideoReceiver::VideoReceiver(QObject* parent) ...@@ -71,6 +71,8 @@ VideoReceiver::VideoReceiver(QObject* parent)
, _videoRunning(false) , _videoRunning(false)
, _showFullScreen(false) , _showFullScreen(false)
, _videoSettings(nullptr) , _videoSettings(nullptr)
, _hwDecoderName(nullptr)
, _swDecoderName("avdec_h264")
{ {
_videoSurface = new VideoSurface; _videoSurface = new VideoSurface;
_videoSettings = qgcApp()->toolbox()->settingsManager()->videoSettings(); _videoSettings = qgcApp()->toolbox()->settingsManager()->videoSettings();
...@@ -159,6 +161,10 @@ VideoReceiver::_restart_timeout() ...@@ -159,6 +161,10 @@ VideoReceiver::_restart_timeout()
void void
VideoReceiver::start() VideoReceiver::start()
{ {
if (_uri.isEmpty()) {
return;
}
qCDebug(VideoReceiverLog) << "start():" << _uri;
if(qgcApp()->runningUnitTests()) { if(qgcApp()->runningUnitTests()) {
return; return;
} }
...@@ -170,7 +176,6 @@ VideoReceiver::start() ...@@ -170,7 +176,6 @@ VideoReceiver::start()
#if defined(QGC_GST_STREAMING) #if defined(QGC_GST_STREAMING)
_stop = false; _stop = false;
qCDebug(VideoReceiverLog) << "start():" << _uri;
#if defined(QGC_GST_TAISYNC_ENABLED) && (defined(__android__) || defined(__ios__)) #if defined(QGC_GST_TAISYNC_ENABLED) && (defined(__android__) || defined(__ios__))
//-- Taisync on iOS or Android sends a raw h.264 stream //-- Taisync on iOS or Android sends a raw h.264 stream
...@@ -280,9 +285,12 @@ VideoReceiver::start() ...@@ -280,9 +285,12 @@ VideoReceiver::start()
break; break;
} }
if ((decoder = gst_element_factory_make(_decoderName, "decoder")) == nullptr) { if (!_hwDecoderName || (decoder = gst_element_factory_make(_hwDecoderName, "decoder")) == nullptr) {
qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('" << _decoderName << "')"; qWarning() << "VideoReceiver::start() hardware decoding not available " << ((_hwDecoderName) ? _hwDecoderName : "");
break; if ((decoder = gst_element_factory_make(_swDecoderName, "decoder")) == nullptr) {
qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('" << _swDecoderName << "')";
break;
}
} }
if ((queue1 = gst_element_factory_make("queue", nullptr)) == nullptr) { if ((queue1 = gst_element_factory_make("queue", nullptr)) == nullptr) {
...@@ -470,6 +478,8 @@ VideoReceiver::_shutdownPipeline() { ...@@ -470,6 +478,8 @@ VideoReceiver::_shutdownPipeline() {
void void
VideoReceiver::_handleError() { VideoReceiver::_handleError() {
qCDebug(VideoReceiverLog) << "Gstreamer error!"; qCDebug(VideoReceiverLog) << "Gstreamer error!";
// If there was an error we switch to software decoding only
_tryWithHardwareDecoding = false;
stop(); stop();
_restart_timer.start(_restart_time_ms); _restart_timer.start(_restart_time_ms);
} }
...@@ -580,17 +590,34 @@ VideoReceiver::_cleanupOldVideos() ...@@ -580,17 +590,34 @@ VideoReceiver::_cleanupOldVideos()
void void
VideoReceiver::setVideoDecoder(VideoEncoding encoding) VideoReceiver::setVideoDecoder(VideoEncoding encoding)
{ {
/*
#if defined(Q_OS_MAC)
_hwDecoderName = "vtdec";
#else
_hwDecoderName = "vaapidecode";
#endif
*/
if (encoding == H265_HW || encoding == H265_SW) { if (encoding == H265_HW || encoding == H265_SW) {
_depayName = "rtph265depay"; _depayName = "rtph265depay";
_parserName = "h265parse"; _parserName = "h265parse";
_decoderName = "avdec_h265"; #if defined(__android__)
_hwDecoderName = "amcviddec-omxgooglehevcdecoder";
#endif
_swDecoderName = "avdec_h265";
} else { } else {
_depayName = "rtph264depay"; _depayName = "rtph264depay";
_parserName = "h264parse"; _parserName = "h264parse";
_decoderName = "avdec_h264"; #if defined(__android__)
_hwDecoderName = "amcviddec-omxgoogleh264decoder";
#endif
_swDecoderName = "avdec_h264";
} }
}
if (!_tryWithHardwareDecoding) {
_hwDecoderName = nullptr;
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// When we finish our pipeline will look like this: // When we finish our pipeline will look like this:
// //
......
...@@ -152,6 +152,8 @@ protected: ...@@ -152,6 +152,8 @@ protected:
VideoSettings* _videoSettings; VideoSettings* _videoSettings;
const char* _depayName; const char* _depayName;
const char* _parserName; const char* _parserName;
const char* _decoderName; bool _tryWithHardwareDecoding = true;
const char* _hwDecoderName;
const char* _swDecoderName;
}; };
...@@ -47,6 +47,9 @@ ...@@ -47,6 +47,9 @@
GST_PLUGIN_STATIC_DECLARE(rtpmanager); GST_PLUGIN_STATIC_DECLARE(rtpmanager);
GST_PLUGIN_STATIC_DECLARE(isomp4); GST_PLUGIN_STATIC_DECLARE(isomp4);
GST_PLUGIN_STATIC_DECLARE(matroska); GST_PLUGIN_STATIC_DECLARE(matroska);
#endif
#if defined(__android__)
GST_PLUGIN_STATIC_DECLARE(androidmedia);
#endif #endif
G_END_DECLS G_END_DECLS
#endif #endif
...@@ -159,6 +162,7 @@ void initializeVideoStreaming(int &argc, char* argv[], char* logpath, char* debu ...@@ -159,6 +162,7 @@ void initializeVideoStreaming(int &argc, char* argv[], char* logpath, char* debu
GST_PLUGIN_STATIC_REGISTER(rtpmanager); GST_PLUGIN_STATIC_REGISTER(rtpmanager);
GST_PLUGIN_STATIC_REGISTER(isomp4); GST_PLUGIN_STATIC_REGISTER(isomp4);
GST_PLUGIN_STATIC_REGISTER(matroska); GST_PLUGIN_STATIC_REGISTER(matroska);
GST_PLUGIN_STATIC_REGISTER(androidmedia);
#endif #endif
#else #else
Q_UNUSED(argc); Q_UNUSED(argc);
......
...@@ -97,11 +97,13 @@ LinuxBuild { ...@@ -97,11 +97,13 @@ LinuxBuild {
-lgstrtpmanager \ -lgstrtpmanager \
-lgstisomp4 \ -lgstisomp4 \
-lgstmatroska \ -lgstmatroska \
-lgstandroidmedia
# Rest of GStreamer dependencies # Rest of GStreamer dependencies
LIBS += -L$$GST_ROOT/lib \ LIBS += -L$$GST_ROOT/lib \
-lgstfft-1.0 -lm \ -lgstfft-1.0 -lm \
-lgstnet-1.0 -lgio-2.0 \ -lgstnet-1.0 -lgio-2.0 \
-lgstphotography-1.0 -lgstgl-1.0 -lEGL \
-lgstaudio-1.0 -lgstcodecparsers-1.0 -lgstbase-1.0 \ -lgstaudio-1.0 -lgstcodecparsers-1.0 -lgstbase-1.0 \
-lgstreamer-1.0 -lgstrtp-1.0 -lgstpbutils-1.0 -lgstrtsp-1.0 -lgsttag-1.0 \ -lgstreamer-1.0 -lgstrtp-1.0 -lgstpbutils-1.0 -lgstrtsp-1.0 -lgsttag-1.0 \
-lgstvideo-1.0 -lavformat -lavcodec -lavutil -lx264 -lavfilter -lswresample \ -lgstvideo-1.0 -lavformat -lavcodec -lavutil -lx264 -lavfilter -lswresample \
......
/**************************************************************************** /****************************************************************************
* *
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> * (c) 2009-2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
* *
* QGroundControl is licensed according to the terms in the file * QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory. * COPYING.md in the root of the source code directory.
...@@ -85,6 +85,84 @@ int WindowsCrtReportHook(int reportType, char* message, int* returnValue) ...@@ -85,6 +85,84 @@ int WindowsCrtReportHook(int reportType, char* message, int* returnValue)
#include "qserialport.h" #include "qserialport.h"
#endif #endif
static jobject _class_loader = nullptr;
static jobject _context = nullptr;
//-----------------------------------------------------------------------------
extern "C" {
void gst_amc_jni_set_java_vm(JavaVM *java_vm);
jobject gst_android_get_application_class_loader(void)
{
return _class_loader;
}
}
//-----------------------------------------------------------------------------
static void
gst_android_init(JNIEnv* env, jobject context)
{
jobject class_loader = nullptr;
jclass context_cls = env->GetObjectClass(context);
if (!context_cls) {
return;
}
jmethodID get_class_loader_id = env->GetMethodID(context_cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return;
}
class_loader = env->CallObjectMethod(context, get_class_loader_id);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return;
}
_context = env->NewGlobalRef(context);
_class_loader = env->NewGlobalRef (class_loader);
}
//-----------------------------------------------------------------------------
static const char kJniClassName[] {"org/mavlink/qgroundcontrol/QGCActivity"};
void setNativeMethods(void)
{
JNINativeMethod javaMethods[] {
{"nativeInit", "(Landroid/content/Context;)V", reinterpret_cast<void *>(gst_android_init)}
};
QAndroidJniEnvironment jniEnv;
if (jniEnv->ExceptionCheck()) {
jniEnv->ExceptionDescribe();
jniEnv->ExceptionClear();
}
jclass objectClass = jniEnv->FindClass(kJniClassName);
if(!objectClass) {
qWarning() << "Couldn't find class:" << kJniClassName;
return;
}
jint val = jniEnv->RegisterNatives(objectClass, javaMethods, sizeof(javaMethods) / sizeof(javaMethods[0]));
if (val < 0) {
qWarning() << "Error registering methods: " << val;
} else {
qDebug() << "Main Native Functions Registered";
}
if (jniEnv->ExceptionCheck()) {
jniEnv->ExceptionDescribe();
jniEnv->ExceptionClear();
}
}
//-----------------------------------------------------------------------------
jint JNI_OnLoad(JavaVM* vm, void* reserved) jint JNI_OnLoad(JavaVM* vm, void* reserved)
{ {
Q_UNUSED(reserved); Q_UNUSED(reserved);
...@@ -93,6 +171,18 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) ...@@ -93,6 +171,18 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1; return -1;
} }
setNativeMethods();
QAndroidJniObject resultL = QAndroidJniObject::callStaticObjectMethod(
kJniClassName,
"jniOnLoad",
"();");
#if defined(QGC_GST_STREAMING)
// Tell the androidmedia plugin about the Java VM
gst_amc_jni_set_java_vm(vm);
#endif
#if !defined(NO_SERIAL_LINK) #if !defined(NO_SERIAL_LINK)
QSerialPort::setNativeMethods(); QSerialPort::setNativeMethods();
#endif #endif
...@@ -102,6 +192,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) ...@@ -102,6 +192,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
} }
#endif #endif
//-----------------------------------------------------------------------------
#ifdef __android__ #ifdef __android__
#include <QtAndroid> #include <QtAndroid>
bool checkAndroidWritePermission() { bool checkAndroidWritePermission() {
...@@ -117,6 +208,7 @@ bool checkAndroidWritePermission() { ...@@ -117,6 +208,7 @@ bool checkAndroidWritePermission() {
} }
#endif #endif
//-----------------------------------------------------------------------------
/** /**
* @brief Starts the application * @brief Starts the application
* *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment