testdiff2/VideoReceiverApp/main.cpp

497 lines
12 KiB
C++

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QQuickItem>
#include <QRunnable>
#include <QCommandLineParser>
#include <QTimer>
#include <gst/gst.h>
#include "QGCLoggingCategory.h"
QGC_LOGGING_CATEGORY(AppLog, "VideoReceiverApp")
#if defined(__android__)
#include <QtAndroidExtras>
#include <jni.h>
#include <android/log.h>
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[] {"labs/mavlink/VideoReceiverApp/QGLSinkActivity"};
static void setNativeMethods(void)
{
JNINativeMethod javaMethods[] {
{"nativeInit", "()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)
{
Q_UNUSED(reserved);
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
setNativeMethods();
gst_amc_jni_set_java_vm(vm);
return JNI_VERSION_1_6;
}
#endif
#include <GStreamer.h>
#include <VideoReceiver.h>
class VideoReceiverApp : public QRunnable
{
public:
VideoReceiverApp(QCoreApplication& app, bool qmlAllowed)
: _app(app)
, _qmlAllowed(qmlAllowed)
{}
void run();
int exec();
void startStreaming();
void startDecoding();
void startRecording();
protected:
void _dispatch(std::function<void()> code);
private:
QCoreApplication& _app;
bool _qmlAllowed;
VideoReceiver* _receiver = nullptr;
QQuickWindow* _window = nullptr;
QQuickItem* _widget = nullptr;
void* _videoSink = nullptr;
QString _url;
unsigned _timeout = 5;
unsigned _connect = 1;
bool _decode = true;
unsigned _stopDecodingAfter = 0;
bool _record = false;
QString _videoFile;
unsigned int _fileFormat = VideoReceiver::FILE_FORMAT_MIN;
unsigned _stopRecordingAfter = 15;
bool _useFakeSink = false;
bool _streaming = false;
bool _decoding = false;
bool _recording = false;
};
void
VideoReceiverApp::run()
{
if((_videoSink = GStreamer::createVideoSink(nullptr, _widget)) == nullptr) {
qCDebug(AppLog) << "createVideoSink failed";
return;
}
_receiver->startDecoding(_videoSink);
}
int
VideoReceiverApp::exec()
{
QCommandLineParser parser;
parser.addHelpOption();
parser.addPositionalArgument("url",
QCoreApplication::translate("main", "Source URL."));
QCommandLineOption timeoutOption(QStringList() << "t" << "timeout",
QCoreApplication::translate("main", "Source timeout."),
QCoreApplication::translate("main", "seconds"));
parser.addOption(timeoutOption);
QCommandLineOption connectOption(QStringList() << "c" << "connect",
QCoreApplication::translate("main", "Number of connection attempts."),
QCoreApplication::translate("main", "attempts"));
parser.addOption(connectOption);
QCommandLineOption decodeOption(QStringList() << "d" << "decode",
QCoreApplication::translate("main", "Decode and render video."));
parser.addOption(decodeOption);
QCommandLineOption noDecodeOption("no-decode",
QCoreApplication::translate("main", "Don't decode and render video."));
parser.addOption(noDecodeOption);
QCommandLineOption stopDecodingOption("stop-decoding",
QCoreApplication::translate("main", "Stop decoding after time."),
QCoreApplication::translate("main", "seconds"));
parser.addOption(stopDecodingOption);
QCommandLineOption recordOption(QStringList() << "r" << "record",
QCoreApplication::translate("main", "Record video."),
QGuiApplication::translate("main", "file"));
parser.addOption(recordOption);
QCommandLineOption formatOption(QStringList() << "f" << "format",
QCoreApplication::translate("main", "File format."),
QCoreApplication::translate("main", "format"));
parser.addOption(formatOption);
QCommandLineOption stopRecordingOption("stop-recording",
QCoreApplication::translate("main", "Stop recording after time."),
QCoreApplication::translate("main", "seconds"));
parser.addOption(stopRecordingOption);
QCommandLineOption videoSinkOption("video-sink",
QCoreApplication::translate("main", "Use video sink: 0 - autovideosink, 1 - fakesink"),
QCoreApplication::translate("main", "sink"));
if (!_qmlAllowed) {
parser.addOption(videoSinkOption);
}
parser.process(_app);
const QStringList args = parser.positionalArguments();
if (args.size() != 1) {
parser.showHelp(0);
}
_url = args.at(0);
if (parser.isSet(timeoutOption)) {
_timeout = parser.value(timeoutOption).toUInt();
}
if (parser.isSet(connectOption)) {
_connect = parser.value(connectOption).toUInt();
}
if (parser.isSet(decodeOption) && parser.isSet(noDecodeOption)) {
parser.showHelp(0);
}
if (parser.isSet(decodeOption)) {
_decode = true;
}
if (parser.isSet(noDecodeOption)) {
_decode = false;
}
if (_decode && parser.isSet(stopDecodingOption)) {
_stopDecodingAfter = parser.value(stopDecodingOption).toUInt();
}
if (parser.isSet(recordOption)) {
_record = true;
_videoFile = parser.value(recordOption);
}
if (parser.isSet(formatOption)) {
_fileFormat += parser.value(formatOption).toUInt();
}
if (_record && parser.isSet(stopRecordingOption)) {
_stopRecordingAfter = parser.value(stopRecordingOption).toUInt();
}
if (parser.isSet(videoSinkOption)) {
_useFakeSink = parser.value(videoSinkOption).toUInt() > 0;
}
_receiver = GStreamer::createVideoReceiver(nullptr);
QQmlApplicationEngine engine;
if (_decode && _qmlAllowed) {
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
_window = static_cast<QQuickWindow*>(engine.rootObjects().first());
Q_ASSERT(_window != nullptr);
_widget = _window->findChild<QQuickItem*>("videoItem");
Q_ASSERT(_widget != nullptr);
}
startStreaming();
QObject::connect(_receiver, &VideoReceiver::timeout, [](){
qCDebug(AppLog) << "Streaming timeout";
});
QObject::connect(_receiver, &VideoReceiver::streamingChanged, [this](bool active){
_streaming = active;
if (_streaming) {
qCDebug(AppLog) << "Streaming started";
} else {
qCDebug(AppLog) << "Streaming stopped";
}
});
QObject::connect(_receiver, &VideoReceiver::decodingChanged, [this](bool active){
_decoding = active;
if (_decoding) {
qCDebug(AppLog) << "Decoding started";
} else {
qCDebug(AppLog) << "Decoding stopped";
if (_streaming) {
if (!_recording) {
_dispatch([this](){
_receiver->stop();
});
}
}
}
});
QObject::connect(_receiver, &VideoReceiver::recordingChanged, [this](bool active){
_recording = active;
if (_recording) {
qCDebug(AppLog) << "Recording started";
} else {
qCDebug(AppLog) << "Recording stopped";
if (_streaming) {
if (!_decoding) {
_dispatch([this](){
_receiver->stop();
});
}
}
}
});
QObject::connect(_receiver, &VideoReceiver::onStartComplete, [this](VideoReceiver::STATUS status){
if (status != VideoReceiver::STATUS_OK) {
qCDebug(AppLog) << "Video receiver start failed";
_dispatch([this](){
if (--_connect > 0) {
qCDebug(AppLog) << "Restarting ...";
_dispatch([this](){
startStreaming();
});
} else {
qCDebug(AppLog) << "Closing...";
delete _receiver;
_app.exit();
}
});
} else {
qCDebug(AppLog) << "Video receiver started";
}
});
QObject::connect(_receiver, &VideoReceiver::onStopComplete, [this](VideoReceiver::STATUS ){
qCDebug(AppLog) << "Video receiver stopped";
_dispatch([this](){
if (--_connect > 0) {
qCDebug(AppLog) << "Restarting ...";
_dispatch([this](){
startStreaming();
});
} else {
qCDebug(AppLog) << "Closing...";
delete _receiver;
_app.exit();
}
});
});
return _app.exec();
}
void
VideoReceiverApp::startStreaming()
{
_receiver->start(_url, _timeout);
if (_decode) {
startDecoding();
}
if (_record) {
startRecording();
}
}
void
VideoReceiverApp::startDecoding()
{
if (_qmlAllowed) {
_window->scheduleRenderJob(this, QQuickWindow::BeforeSynchronizingStage);
} else {
if (_videoSink == nullptr) {
if ((_videoSink = gst_element_factory_make(_useFakeSink ? "fakesink" : "autovideosink", nullptr)) == nullptr) {
qCDebug(AppLog) << "Failed to create video sink";
return;
}
}
_receiver->startDecoding(_videoSink);
}
if (_stopDecodingAfter > 0) {
unsigned connect = _connect;
QTimer::singleShot(_stopDecodingAfter * 1000, Qt::PreciseTimer, [this, connect](){
if (connect != _connect) {
return;
}
_receiver->stopDecoding();
});
}
}
void
VideoReceiverApp::startRecording()
{
_receiver->startRecording(_videoFile, static_cast<VideoReceiver::FILE_FORMAT>(_fileFormat));
if (_stopRecordingAfter > 0) {
unsigned connect = _connect;
QTimer::singleShot(_stopRecordingAfter * 1000, [this, connect](){
if (connect != _connect) {
return;
}
_receiver->stopRecording();
});
}
}
void
VideoReceiverApp::_dispatch(std::function<void()> code)
{
QTimer* timer = new QTimer();
timer->moveToThread(qApp->thread());
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, [=](){
code();
timer->deleteLater();
});
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}
static bool isQtApp(const char* app)
{
const char* s;
#if defined(Q_OS_WIN)
if ((s = strrchr(app, '\\')) != nullptr) {
#else
if ((s = strrchr(app, '/')) != nullptr) {
#endif
s += 1;
} else {
s = app;
}
return s[0] == 'Q' || s[0] == 'q';
}
int main(int argc, char *argv[])
{
if (argc < 1) {
return 0;
}
GStreamer::initialize(argc, argv, 3);
if (isQtApp(argv[0])) {
QGuiApplication app(argc, argv);
VideoReceiverApp videoApp(app, true);
return videoApp.exec();
} else {
QCoreApplication app(argc, argv);
VideoReceiverApp videoApp(app, false);
return videoApp.exec();
}
}