Compare commits
10 Commits
94ee66acd7
...
4ab5a162c4
Author | SHA1 | Date | |
---|---|---|---|
4ab5a162c4 | |||
|
e7caeb4d49 | ||
|
6496f8e360 | ||
|
8fa72b4f43 | ||
|
e2b6985d87 | ||
|
cf77993f3c | ||
|
618a014100 | ||
|
efb9d8bc6f | ||
|
658005da61 | ||
|
e940b8036e |
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@
|
||||
lib/
|
||||
bin/
|
||||
.idea/
|
||||
build/
|
||||
|
||||
CMakeLists.txt.user
|
||||
cmake-build-*/
|
||||
|
@ -1,16 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.5.1)
|
||||
|
||||
set (openauto_VERSION_MAJOR 2)
|
||||
set (openauto_VERSION_MINOR 1)
|
||||
set (openauto_VERSION_PATCH 0)
|
||||
|
||||
project(openauto
|
||||
VERSION ${openauto_VERSION_MAJOR}.${openauto_VERSION_MINOR}.${openauto_VERSION_PATCH}
|
||||
LANGUAGES CXX)
|
||||
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
|
||||
project(openauto CXX)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(base_directory ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
||||
@ -40,8 +47,10 @@ find_package(Protobuf REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(rtaudio REQUIRED)
|
||||
find_package(aasdk REQUIRED)
|
||||
find_package(h264 REQUIRED)
|
||||
find_package(Threads)
|
||||
|
||||
include(${base_directory}/cmake_modules/gitversion.cmake)
|
||||
if(RPI_BUILD)
|
||||
find_package(libomx)
|
||||
endif()
|
||||
@ -60,12 +69,12 @@ if(GST_BUILD)
|
||||
add_definitions(-DRPI)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/functions.cmake)
|
||||
findRpiRevision( RPI_REVISION )
|
||||
math(EXPR RPI_MODEL "(0x${RPI_REVISION}>>4)&0xFF")
|
||||
math(EXPR RPI_MODEL "(0x${RPI_REVISION}>>4)&0xFF")
|
||||
message( "-- Raspberry Pi Model: ${RPI_MODEL}" )
|
||||
if(RPI_MODEL EQUAL 17)
|
||||
message("Raspberry Pi 4 Found")
|
||||
add_definitions(-DPI4)
|
||||
endif(RPI_REVISION EQUAL 17)
|
||||
endif(RPI_MODEL EQUAL 17)
|
||||
endif(RPI_BUILD)
|
||||
message(STATUS "${GST_LIBRARIES}")
|
||||
endif(GST_BUILD)
|
||||
@ -77,3 +86,43 @@ include_directories(${BTSERVICE_PROTO_INCLUDE_DIRS})
|
||||
add_subdirectory(openauto)
|
||||
add_subdirectory(autoapp)
|
||||
add_dependencies(autoapp btservice_proto)
|
||||
|
||||
set (openauto_VERSION_PATCH ${_build_version})
|
||||
set (openauto_VERSION_STRING ${openauto_VERSION_MAJOR}.${openauto_VERSION_MINOR}.${openauto_VERSION_PATCH})
|
||||
set_target_properties(openauto PROPERTIES VERSION ${openauto_VERSION_STRING}
|
||||
SOVERSION ${openauto_VERSION_MAJOR})
|
||||
message(INFO " Project Version: ${openauto_VERSION_STRING}")
|
||||
|
||||
install(DIRECTORY lib DESTINATION lib COMPONENT libraries)
|
||||
install(DIRECTORY include DESTINATION include COMPONENT headers)
|
||||
install(DIRECTORY bin DESTINATION bin COMPONENT applications)
|
||||
|
||||
SET(CPACK_GENERATOR "DEB")
|
||||
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "OpenDsh") #required
|
||||
SET(CPACK_PACKAGE_VENDOR "OpenDsh")
|
||||
set(CPACK_PACKAGE_VERSION ${openauto_VERSION_STRING})
|
||||
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
set(CPACK_COMPONENTS_ALL applications libraries headers Unspecified)
|
||||
set(CPACK_COMPONENT_APPLICATIONS_DISPLAY_NAME "Applications")
|
||||
set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries")
|
||||
set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C++ Headers")
|
||||
set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION
|
||||
"Applications provided by OpenAuto")
|
||||
set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION
|
||||
"Static libraries used to build programs with OpenAuto")
|
||||
set(CPACK_COMPONENT_HEADERS_DESCRIPTION
|
||||
"C/C++ header files for use with OpenAuto")
|
||||
set(CPACK_COMPONENT_LIBRARIES_GROUP "Development")
|
||||
set(CPACK_COMPONENT_HEADERS_GROUP "Development")
|
||||
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_EXPANDED ON)
|
||||
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DESCRIPTION
|
||||
"All of the tools you'll ever need to develop software")
|
||||
set(CPACK_COMPONENT_HEADERS_DEPENDS libraries)
|
||||
set(CPACK_COMPONENT_APPLICATIONS_DEPENDS libraries)
|
||||
set(CPACK_ALL_INSTALL_TYPES Full Developer)
|
||||
set(CPACK_INSTALL_TYPE_FULL_DISPLAY_NAME "Everything")
|
||||
set(CPACK_COMPONENT_LIBRARIES_INSTALL_TYPES Developer Full)
|
||||
set(CPACK_COMPONENT_HEADERS_INSTALL_TYPES Developer Full)
|
||||
set(CPACK_COMPONENT_APPLICATIONS_INSTALL_TYPES Full)
|
||||
INCLUDE(CPack)
|
@ -17,29 +17,157 @@
|
||||
*/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QtWebSockets/QWebSocket>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusMessage>
|
||||
#include <QDebug>
|
||||
|
||||
#include "autoapp/UI/MainWindow.hpp"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
namespace autoapp
|
||||
{
|
||||
namespace ui
|
||||
{
|
||||
namespace ui
|
||||
{
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, ui_(new Ui::MainWindow)
|
||||
{
|
||||
ui_->setupUi(this);
|
||||
connect(ui_->pushButtonSettings, &QPushButton::clicked, this, &MainWindow::openSettings);
|
||||
connect(ui_->pushButtonExit, &QPushButton::clicked, this, &MainWindow::exit);
|
||||
connect(ui_->pushButtonToggleCursor, &QPushButton::clicked, this, &MainWindow::toggleCursor);
|
||||
connect(ui_->pushButtonWirelessConnection, &QPushButton::clicked, this, &MainWindow::openConnectDialog);
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent), ui_(new Ui::MainWindow), webSocket(new QWebSocket),
|
||||
reconnectTimer(new QTimer(this)),
|
||||
metadataTimer(new QTimer(this)),
|
||||
shuttingDown(false)
|
||||
{
|
||||
ui_->setupUi(this);
|
||||
connect(ui_->pushButtonSettings, &QPushButton::clicked, this, &MainWindow::openSettings);
|
||||
connect(ui_->pushButtonExit, &QPushButton::clicked, this, &MainWindow::exit);
|
||||
connect(ui_->pushButtonToggleCursor, &QPushButton::clicked, this, &MainWindow::toggleCursor);
|
||||
connect(ui_->pushButtonWirelessConnection, &QPushButton::clicked, this, &MainWindow::openConnectDialog);
|
||||
|
||||
// BT NOW PLAYING UPDATE
|
||||
connect(metadataTimer, &QTimer::timeout, this, &MainWindow::updateNowPlaying);
|
||||
metadataTimer->start(1000); // refresh every 5 seconds
|
||||
|
||||
// RADIO WEBSOCKET STUFF
|
||||
reconnectTimer->setSingleShot(true);
|
||||
reconnectTimer->setInterval(3000);
|
||||
|
||||
connect(reconnectTimer, &QTimer::timeout, this, &MainWindow::connectWebSocket);
|
||||
|
||||
connect(webSocket, &QWebSocket::connected, this, [this]()
|
||||
{
|
||||
qDebug() << "WebSocket connected";
|
||||
webSocket->sendTextMessage(QStringLiteral("Hello from MainWindow WebSocket!")); });
|
||||
|
||||
connect(webSocket, &QWebSocket::textMessageReceived, this, [this](const QString &message)
|
||||
{
|
||||
qDebug() << "Message received:" << message;
|
||||
handleIncomingMessage(message); });
|
||||
|
||||
connect(webSocket, &QWebSocket::disconnected, this, [this]()
|
||||
{
|
||||
qDebug() << "WebSocket disconnected";
|
||||
if (!shuttingDown) {
|
||||
qDebug() << "Attempting to reconnect in 3 seconds...";
|
||||
reconnectTimer->start();
|
||||
} });
|
||||
|
||||
connect(webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error),
|
||||
this, [this](QAbstractSocket::SocketError)
|
||||
{ qDebug() << "WebSocket error:" << webSocket->errorString(); });
|
||||
|
||||
connectWebSocket();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
shuttingDown = true;
|
||||
webSocket->abort();
|
||||
webSocket->deleteLater();
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
void f1x::openauto::autoapp::ui::MainWindow::updateBtNowPlaying()
|
||||
{
|
||||
delete ui_;
|
||||
QDBusInterface manager(
|
||||
"org.bluez",
|
||||
"/",
|
||||
"org.freedesktop.DBus.ObjectManager",
|
||||
QDBusConnection::systemBus());
|
||||
|
||||
if (!manager.isValid())
|
||||
{
|
||||
qDebug() << "Failed to connect to BlueZ D-Bus";
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusReply<QVariantMap> reply = manager.call("GetManagedObjects");
|
||||
|
||||
if (!reply.isValid())
|
||||
{
|
||||
qDebug() << "D-Bus call failed:" << reply.error().message();
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap allObjects = reply.value();
|
||||
|
||||
for (auto it = allObjects.begin(); it != allObjects.end(); ++it)
|
||||
{
|
||||
QString path = it.key();
|
||||
QVariantMap interfaces = it.value().toMap();
|
||||
|
||||
if (interfaces.contains("org.bluez.MediaPlayer1"))
|
||||
{
|
||||
qDebug() << "Found MediaPlayer at:" << path;
|
||||
|
||||
QDBusInterface mediaPlayer(
|
||||
"org.bluez",
|
||||
path,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
QDBusConnection::systemBus());
|
||||
|
||||
QDBusReply<QVariant> trackReply = mediaPlayer.call("Get", "org.bluez.MediaPlayer1", "Track");
|
||||
if (trackReply.isValid())
|
||||
{
|
||||
QVariantMap track = trackReply.value().toMap();
|
||||
QString artist = track["Artist"].toString();
|
||||
QString title = track["Title"].toString();
|
||||
QString album = track["Album"].toString();
|
||||
|
||||
QString status = statusReply.value().toString();
|
||||
|
||||
qDebug() << "Now playing:" << artist << "-" << title << "from" << album;
|
||||
qDebug() << "Status:" << status;
|
||||
|
||||
if (status == "Playing")
|
||||
{
|
||||
ui_->stackedWidget->setCurrentIndex(0);
|
||||
ui_->btSongName->setText(title);
|
||||
ui_->btArtistName->setText(artist);
|
||||
ui_->btAlbumName->setText(album);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui_->stackedWidget->setCurrentIndex(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void f1x::openauto::autoapp::ui::MainWindow::connectWebSocket()
|
||||
{
|
||||
QUrl url(QStringLiteral("ws://127.0.0.1:5000")); // Change to your real server
|
||||
qDebug() << "Connecting to WebSocket:" << url;
|
||||
webSocket->open(url);
|
||||
}
|
||||
|
||||
void f1x::openauto::autoapp::ui::MainWindow::handleIncomingMessage(const QString &message)
|
||||
{
|
||||
// Your custom message processing logic here
|
||||
qDebug() << "[Handler] Processing message:" << message;
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>480</height>
|
||||
<width>1046</width>
|
||||
<height>571</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -18,231 +18,477 @@
|
||||
color: rgb(238, 238, 236);</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QLabel" name="labelWaitingForDevice">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>290</x>
|
||||
<y>30</y>
|
||||
<width>281</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:22pt; font-weight:600; font-style:italic; color:#3465a4;">Waiting for device...</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>180</x>
|
||||
<y>20</y>
|
||||
<width>101</width>
|
||||
<height>101</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/ico_androidauto.png"/></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButtonSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>630</x>
|
||||
<y>330</y>
|
||||
<width>161</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButtonExit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>630</x>
|
||||
<y>380</y>
|
||||
<width>161</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelPluginDeviceText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>340</x>
|
||||
<y>70</y>
|
||||
<width>301</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-style:italic; color:#eeeeec;">Plug in your device to start AndroidAuto (tm).</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelProjectHomePage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>440</y>
|
||||
<width>271</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="https://github.com/f1xpl/openauto"><span style=" text-decoration: underline; color:#007af4;">https://github.com/f1xpl/openauto</span></a></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelDistractionWarningIcon">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>170</y>
|
||||
<width>31</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/ico_warning.png"/></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelDistractionWarning">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>80</x>
|
||||
<y>130</y>
|
||||
<width>531</width>
|
||||
<height>101</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"><span style=" color:#ef2929;">WARNING!</span></p><p><span style=" color:#ef2929;">Distraction may cause accidents. Do not attempt to operate while driving. Always concentrate on driving and obey Traffic Regulations. You assume total responsibility and risk for using this software.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelCertificationWarning">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>80</x>
|
||||
<y>240</y>
|
||||
<width>531</width>
|
||||
<height>121</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" color:#ef2929;">This software is not certified by Google Inc. It is created for R&amp;D purposes and may not work as expected by the original authors. Do not use while driving.</span></p><p><span style=" color:#ef2929;">You use this software at your own risk.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelCertificationWarningIcon">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>270</y>
|
||||
<width>31</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/ico_warning.png"/></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelCopyrightsInfoIcon">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>410</x>
|
||||
<y>440</y>
|
||||
<width>21</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><img src=":/ico_info.png"/></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelTrademark">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>440</x>
|
||||
<y>440</y>
|
||||
<width>351</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-style:italic;">AndroidAuto is registered trademark of Google Inc.</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButtonToggleCursor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>630</x>
|
||||
<y>230</y>
|
||||
<width>161</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Toggle cursor</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButtonWirelessConnection">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>630</x>
|
||||
<y>280</y>
|
||||
<width>161</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Wireless connection</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>370</y>
|
||||
<width>551</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"><span style=" font-weight:600;">If you are looking for a Raspberry PI compatible 12V power supply equipped with an ignition switch, visit </span><a href="https://bluewavestudio.io/"><span style=" text-decoration: underline; color:#007af4;">https://bluewavestudio.io</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<widget class="QFrame" name="verticalFrame">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1021</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item alignment="Qt::AlignVCenter">
|
||||
<widget class="QFrame" name="verticalFrame">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QWidget" name="btNowPlaying" native="true">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../assets/resources.qrc">:/bluetooth.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../assets/resources.qrc">:/song.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="btSongName">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>24</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lorem Ipsum Dorem</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../assets/resources.qrc">:/artist.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="btArtistName">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Example Artist</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../assets/resources.qrc">:/album.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="btAlbumName">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Example Album</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignHCenter|Qt::AlignBottom">
|
||||
<widget class="QLabel" name="btRadioScreen">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>24</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ABCD,EFGH</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<widget class="QFrame" name="horizontalFrame">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1024</width>
|
||||
<height>550</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>550</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>550</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
|
||||
<widget class="QWidget" name="horizontalWidget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="radioIcon">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../assets/resources.qrc">:/radio.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="radioScreen">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>40</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ABCD,EFGH</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="radioPreset">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="radioSecondLine">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tuner List</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignHCenter|Qt::AlignBottom">
|
||||
<widget class="QWidget" name="horizontalWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>50</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonExit">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonSettings">
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonWirelessConnection">
|
||||
<property name="text">
|
||||
<string>Wireless connection</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonToggleCursor">
|
||||
<property name="text">
|
||||
<string>Toggle cursor</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<tabstops>
|
||||
@ -251,6 +497,8 @@ color: rgb(238, 238, 236);</string>
|
||||
<tabstop>pushButtonSettings</tabstop>
|
||||
<tabstop>pushButtonExit</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../assets/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
BIN
autoapp/assets/album.png
Normal file
BIN
autoapp/assets/album.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 896 B |
BIN
autoapp/assets/artist.png
Normal file
BIN
autoapp/assets/artist.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 935 B |
BIN
autoapp/assets/bluetooth.png
Normal file
BIN
autoapp/assets/bluetooth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 931 B |
BIN
autoapp/assets/radio.png
Normal file
BIN
autoapp/assets/radio.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 816 B |
@ -1,9 +1,14 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>ico_androidauto.png</file>
|
||||
<file>ico_warning.png</file>
|
||||
<file>ico_setting.png</file>
|
||||
<file>ico_info.png</file>
|
||||
<file>aa_video.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/">
|
||||
<file>radio.png</file>
|
||||
<file>album.png</file>
|
||||
<file>artist.png</file>
|
||||
<file>bluetooth.png</file>
|
||||
<file>song.png</file>
|
||||
<file>ico_androidauto.png</file>
|
||||
<file>ico_warning.png</file>
|
||||
<file>ico_setting.png</file>
|
||||
<file>ico_info.png</file>
|
||||
<file>aa_video.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
BIN
autoapp/assets/song.png
Normal file
BIN
autoapp/assets/song.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 821 B |
@ -43,20 +43,33 @@ btservice::btservice(openauto::configuration::IConfiguration::Pointer config)
|
||||
|
||||
void btservice::connectToBluetooth(QBluetoothAddress addr, QBluetoothAddress controller)
|
||||
{
|
||||
// Update 07-12-22, with bluez update and krnbt, raspberry pi is behaving as expected. For this reason
|
||||
// the RPI specific code has been commented out. The commented out code (and comments below this one)
|
||||
// exist as legacy now - in case there's a scenario where someone cannot update to krnbt or newer bluez.
|
||||
|
||||
|
||||
// The raspberry pi has a really tough time using bluetoothctl (or really anything) to connect to an Android phone
|
||||
// even though phone connecting to the pi is fine.
|
||||
// I found a workaround where you can make the pi attempt an rfcomm connection to the phone, and it connects immediately
|
||||
// This might require setting u+s on rfcomm though
|
||||
// Other computers with more sane bluetooth shouldn't have an issue using bluetoothctl
|
||||
|
||||
// Update 01-10-21, latest firmware/package updates seem to have made bluetoothctl more stable
|
||||
// and it won't drop a connection anymore. Only issue, is that the connection will fail
|
||||
// if the desired target hasn't been "seen" yet
|
||||
// we can use hcitool to force the pi to recognize that we're trying to connect to a valid device.
|
||||
// this causes the target device to be "seen"
|
||||
// bluetoothctl can then connect as normal.
|
||||
// Why don't we just use rfcomm (as we had previously on pis)? Because an rfcomm initiated connection doesn't connect HFP, which breaks the use of
|
||||
// pi mic/speakers for android auto phone calls. bluetoothctl will connect all profiles.
|
||||
|
||||
#ifdef RPI
|
||||
// tries to open an rfcomm serial on channel 2
|
||||
// channel doesn't really matter here, 2 is just "somewhat standard"
|
||||
QString program = QString::fromStdString("sudo stdbuf -oL rfcomm connect hci0 ")+addr.toString()+QString::fromStdString(" 2");
|
||||
btConnectProcess = new QProcess();
|
||||
OPENAUTO_LOG(info)<<"[btservice] Attempting to connect to last bluetooth device, "<<addr.toString().toStdString()<<" with `"<<program.toStdString();
|
||||
btConnectProcess->start(program, QProcess::Unbuffered | QProcess::ReadWrite);
|
||||
#else
|
||||
// QString program = QString::fromStdString("sudo hcitool cc ")+addr.toString();
|
||||
// btConnectProcess = new QProcess();
|
||||
// OPENAUTO_LOG(info)<<"[btservice] Attempting to connect to last bluetooth device, "<<addr.toString().toStdString()<<" using hcitool/bluetoothctl hybrid";
|
||||
// btConnectProcess->start(program, QProcess::Unbuffered | QProcess::ReadWrite);
|
||||
// btConnectProcess->waitForFinished();
|
||||
#endif
|
||||
btConnectProcess = new QProcess();
|
||||
btConnectProcess->setProcessChannelMode(QProcess::SeparateChannels);
|
||||
OPENAUTO_LOG(info)<<"[btservice] Attempting to connect to last bluetooth device, "<<addr.toString().toStdString()<<" with bluetoothctl";
|
||||
@ -66,9 +79,6 @@ void btservice::connectToBluetooth(QBluetoothAddress addr, QBluetoothAddress con
|
||||
btConnectProcess->write(QString("connect %1\n").arg(addr.toString()).toUtf8());
|
||||
btConnectProcess->closeWriteChannel();
|
||||
btConnectProcess->waitForFinished();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
cmake_modules/Findh264.cmake
Normal file
26
cmake_modules/Findh264.cmake
Normal file
@ -0,0 +1,26 @@
|
||||
set (H264_INCLUDE_DIR /usr/local/include/h264bitstream)
|
||||
set (H264_LIB_DIR /usr/local/lib)
|
||||
|
||||
set(H264_FOUND TRUE)
|
||||
|
||||
if (H264_FOUND)
|
||||
if (NOT h264_FIND_QUIETLY)
|
||||
message(STATUS "Found h264bitstream:")
|
||||
message(STATUS " - Includes: ${H264_INCLUDE_DIR}")
|
||||
message(STATUS " - Libraries: ${H264_LIB_DIR}")
|
||||
endif()
|
||||
add_library(h264 INTERFACE)
|
||||
target_include_directories(h264 INTERFACE ${H264_INCLUDE_DIR})
|
||||
set_target_properties(h264 PROPERTIES INTERFACE_LINK_DIRECTORIES ${H264_LIB_DIR})
|
||||
target_link_libraries(h264 INTERFACE libh264bitstream.so)
|
||||
else()
|
||||
if (h264_FIND_REQUIRED)
|
||||
if(H264_INCLUDE_DIR)
|
||||
message(FATAL_ERROR "h264 was found but not built. Perform an in-source build.")
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find h264")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(H264_INCLUDE_DIR H264_LIBRARIES)
|
51
cmake_modules/gitversion.cmake
Normal file
51
cmake_modules/gitversion.cmake
Normal file
@ -0,0 +1,51 @@
|
||||
# cmake/gitversion.cmake
|
||||
cmake_minimum_required(VERSION 3.0.0)
|
||||
|
||||
message(STATUS "Resolving GIT Version")
|
||||
|
||||
set(_build_version "unknown")
|
||||
set(_commit_timestamp "unknown")
|
||||
|
||||
find_package(Git)
|
||||
if(GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
|
||||
WORKING_DIRECTORY "${local_dir}"
|
||||
OUTPUT_VARIABLE _build_version
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY "${local_dir}"
|
||||
OUTPUT_VARIABLE _build_branch
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} log -1 --format=%at
|
||||
WORKING_DIRECTORY "${local_dir}"
|
||||
OUTPUT_VARIABLE _commit_timestamp
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
message( STATUS "GIT hash: ${_build_version}; branch: ${_build_branch}; Commit epoch: ${_commit_timestamp};")
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} diff --no-ext-diff --quiet
|
||||
WORKING_DIRECTORY "${local_dir}"
|
||||
RESULT_VARIABLE ret
|
||||
)
|
||||
if(ret EQUAL "1")
|
||||
set(_build_changes "*")
|
||||
else()
|
||||
set(_build_changes "")
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "GIT not found")
|
||||
endif()
|
||||
|
||||
# branch name
|
||||
# git rev-parse --abbrev-ref HEAD
|
||||
# changed
|
||||
# git diff --no-ext-diff --quiet
|
@ -20,33 +20,42 @@
|
||||
|
||||
#include <memory>
|
||||
#include <QMainWindow>
|
||||
#include <QtWebSockets/QWebSocket>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
namespace autoapp
|
||||
{
|
||||
namespace ui
|
||||
{
|
||||
namespace ui
|
||||
{
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
|
||||
signals:
|
||||
void exit();
|
||||
void openSettings();
|
||||
void toggleCursor();
|
||||
void openConnectDialog();
|
||||
signals:
|
||||
void exit();
|
||||
void openSettings();
|
||||
void toggleCursor();
|
||||
void openConnectDialog();
|
||||
|
||||
private:
|
||||
Ui::MainWindow* ui_;
|
||||
};
|
||||
private slots:
|
||||
void connectWebSocket();
|
||||
void handleIncomingMessage(const QString &message);
|
||||
void updateBtNowPlaying();
|
||||
|
||||
}
|
||||
private:
|
||||
Ui::MainWindow *ui_;
|
||||
QWebSocket *webSocket;
|
||||
QTimer *reconnectTimer;
|
||||
bool shuttingDown;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,8 @@ public:
|
||||
int32_t getOMXLayerIndex() const override;
|
||||
void setVideoMargins(QRect value) override;
|
||||
QRect getVideoMargins() const override;
|
||||
void setWhitescreenWorkaround(bool value) override;
|
||||
bool getWhitescreenWorkaround() const override;
|
||||
|
||||
bool getTouchscreenEnabled() const override;
|
||||
void setTouchscreenEnabled(bool value) override;
|
||||
@ -91,6 +93,7 @@ private:
|
||||
size_t screenDPI_;
|
||||
int32_t omxLayerIndex_;
|
||||
QRect videoMargins_;
|
||||
bool whitescreenWorkaround_;
|
||||
bool enableTouchscreen_;
|
||||
ButtonCodes buttonCodes_;
|
||||
BluetoothAdapterType bluetoothAdapterType_;
|
||||
@ -115,6 +118,7 @@ private:
|
||||
static const std::string cVideoOMXLayerIndexKey;
|
||||
static const std::string cVideoMarginWidth;
|
||||
static const std::string cVideoMarginHeight;
|
||||
static const std::string cVideoWhitescreenWorkaround;
|
||||
|
||||
static const std::string cAudioMusicAudioChannelEnabled;
|
||||
static const std::string cAudioSpeechAudioChannelEnabled;
|
||||
|
@ -59,6 +59,8 @@ public:
|
||||
virtual int32_t getOMXLayerIndex() const = 0;
|
||||
virtual void setVideoMargins(QRect value) = 0;
|
||||
virtual QRect getVideoMargins() const = 0;
|
||||
virtual void setWhitescreenWorkaround(bool value) = 0;
|
||||
virtual bool getWhitescreenWorkaround() const = 0;
|
||||
|
||||
virtual bool getTouchscreenEnabled() const = 0;
|
||||
virtual void setTouchscreenEnabled(bool value) = 0;
|
||||
|
@ -51,6 +51,46 @@ namespace openauto
|
||||
namespace projection
|
||||
{
|
||||
|
||||
// enum of possible h264 decoders dash will attempt to use
|
||||
enum H264_Decoder {
|
||||
nvcodec,
|
||||
v4l2,
|
||||
omx,
|
||||
vaapi,
|
||||
libav,
|
||||
unknown
|
||||
};
|
||||
|
||||
// order of priority for decoders - dash will use the first decoder it can find in this list
|
||||
// for sake of the code, don't include "unknown" as a decoder to search for - this is the default case.
|
||||
const H264_Decoder H264_Decoder_Priority_List[] = { nvcodec, v4l2, omx, libav };
|
||||
|
||||
// A map of enum to actual pad name we want to use
|
||||
inline const char* ToString(H264_Decoder v)
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
case nvcodec: return "nvh264dec";
|
||||
case v4l2: return "v4l2h264dec";
|
||||
case omx: return "omxh264dec";
|
||||
case libav: return "avdec_h264";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
// A map of enum to pipeline steps to insert (because for some we need some video converting)
|
||||
inline const char* ToPipeline(H264_Decoder v)
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
// we're going to assume that any machine with an nvidia card has a cpu powerful enough for video convert.
|
||||
case nvcodec: return "nvh264dec ! videoconvert";
|
||||
case v4l2: return "v4l2h264dec";
|
||||
case omx: return "omxh264dec";
|
||||
case libav: return "avdec_h264";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
class GSTVideoOutput: public QObject, public VideoOutput, boost::noncopyable
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -72,13 +112,19 @@ protected slots:
|
||||
void onStartPlayback();
|
||||
void onStopPlayback();
|
||||
|
||||
public slots:
|
||||
void dumpDot();
|
||||
private:
|
||||
static GstPadProbeReturn convertProbe(GstPad* pad, GstPadProbeInfo* info, void*);
|
||||
static gboolean busCallback(GstBus*, GstMessage* message, gpointer*);
|
||||
H264_Decoder findPreferredVideoDecoder();
|
||||
|
||||
bool firstHeaderParsed = false;
|
||||
|
||||
QGst::ElementPtr videoSink_;
|
||||
QQuickWidget* videoWidget_;
|
||||
GstElement* vidPipeline_;
|
||||
GstVideoFilter* vidCrop_;
|
||||
GstAppSrc* vidSrc_;
|
||||
QWidget* videoContainer_;
|
||||
QGst::Quick::VideoSurface* surface_;
|
||||
|
@ -19,6 +19,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "InputEvent.hpp"
|
||||
#include "aasdk_proto/InputEventIndicationMessage.pb.h"
|
||||
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
@ -31,7 +33,9 @@ public:
|
||||
virtual ~IInputDeviceEventHandler() = default;
|
||||
|
||||
virtual void onButtonEvent(const ButtonEvent& event) = 0;
|
||||
virtual void onTouchEvent(const TouchEvent& event) = 0;
|
||||
virtual void onTouchEvent(aasdk::proto::messages::InputEventIndication inputEventIndication) = 0;
|
||||
virtual void onMouseEvent(const projection::TouchEvent& event) = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <QKeyEvent>
|
||||
#include "IInputDevice.hpp"
|
||||
#include "openauto/Configuration/IConfiguration.hpp"
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
@ -48,6 +49,7 @@ private:
|
||||
bool handleKeyEvent(QEvent* event, QKeyEvent* key);
|
||||
void dispatchKeyEvent(ButtonEvent event);
|
||||
bool handleTouchEvent(QEvent* event);
|
||||
bool handleMouseEvent(QEvent* event);
|
||||
|
||||
QObject& parent_;
|
||||
configuration::IConfiguration::Pointer configuration_;
|
||||
@ -55,6 +57,10 @@ private:
|
||||
QRect displayGeometry_;
|
||||
IInputDeviceEventHandler* eventHandler_;
|
||||
std::mutex mutex_;
|
||||
|
||||
std::priority_queue <int, std::vector<int>, std::greater<int> > pointer_id_queue;
|
||||
QMap<int, int> pointer_map;
|
||||
int max_pointers = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ private:
|
||||
uint32_t sampleRate_;
|
||||
SequentialBuffer audioBuffer_;
|
||||
std::unique_ptr<RtAudio> dac_;
|
||||
std::mutex mutex_;
|
||||
static std::mutex mutex_;
|
||||
};
|
||||
|
||||
}
|
||||
|
88
include/openauto/Service/IAndroidAutoInterface.hpp
Normal file
88
include/openauto/Service/IAndroidAutoInterface.hpp
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of openauto project.
|
||||
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
|
||||
*
|
||||
* openauto is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* openauto is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with openauto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "aasdk_proto/ButtonCodeEnum.pb.h"
|
||||
#include "openauto/Projection/InputEvent.hpp"
|
||||
#include "aasdk_proto/MediaInfoChannelMetadataData.pb.h"
|
||||
#include "aasdk_proto/MediaInfoChannelPlaybackData.pb.h"
|
||||
#include "aasdk_proto/NavigationStatusMessage.pb.h"
|
||||
#include "aasdk_proto/NavigationDistanceEventMessage.pb.h"
|
||||
#include "aasdk_proto/NavigationTurnEventMessage.pb.h"
|
||||
#include "openauto/Service/ServiceFactory.hpp"
|
||||
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
|
||||
class IAndroidAutoInterface: public std::enable_shared_from_this<IAndroidAutoInterface>
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<IAndroidAutoInterface> getPtr()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
typedef std::shared_ptr<IAndroidAutoInterface> Pointer;
|
||||
|
||||
virtual ~IAndroidAutoInterface() = default;
|
||||
|
||||
virtual void mediaPlaybackUpdate(const aasdk::proto::messages::MediaInfoChannelPlaybackData& playback) = 0;
|
||||
virtual void mediaMetadataUpdate(const aasdk::proto::messages::MediaInfoChannelMetadataData& metadata) = 0;
|
||||
virtual void navigationStatusUpdate(const aasdk::proto::messages::NavigationStatus& navStatus) = 0;
|
||||
virtual void navigationTurnEvent(const aasdk::proto::messages::NavigationTurnEvent& turnEvent) = 0;
|
||||
virtual void navigationDistanceEvent(const aasdk::proto::messages::NavigationDistanceEvent& distanceEvent) = 0;
|
||||
void setServiceFactory(ServiceFactory* serviceFactory)
|
||||
{
|
||||
this->m_serviceFactory = serviceFactory;
|
||||
|
||||
}
|
||||
void injectButtonPress(aasdk::proto::enums::ButtonCode::Enum buttonCode, projection::WheelDirection wheelDirection=openauto::projection::WheelDirection::NONE, projection::ButtonEventType buttonEventType = projection::ButtonEventType::NONE)
|
||||
{
|
||||
if(m_serviceFactory != NULL)
|
||||
{
|
||||
|
||||
m_serviceFactory->sendButtonPress(buttonCode, wheelDirection, buttonEventType);
|
||||
}
|
||||
}
|
||||
void injectButtonPress(aasdk::proto::enums::ButtonCode::Enum buttonCode, projection::ButtonEventType buttonEventType)
|
||||
{
|
||||
this->injectButtonPress(buttonCode, projection::WheelDirection::NONE, buttonEventType);
|
||||
}
|
||||
void setNightMode(bool mode)
|
||||
{
|
||||
if(m_serviceFactory != NULL)
|
||||
{
|
||||
|
||||
m_serviceFactory->setNightMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
using std::enable_shared_from_this<IAndroidAutoInterface>::shared_from_this;
|
||||
ServiceFactory* m_serviceFactory;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ class InputService:
|
||||
public:
|
||||
InputService(boost::asio::io_service& ioService, aasdk::messenger::IMessenger::Pointer messenger, projection::IInputDevice::Pointer inputDevice);
|
||||
|
||||
void sendButtonPress(aasdk::proto::enums::ButtonCode::Enum buttonCode, projection::WheelDirection wheelDirection = projection::WheelDirection::NONE, projection::ButtonEventType buttonEventType = projection::ButtonEventType::NONE);
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void fillFeatures(aasdk::proto::messages::ServiceDiscoveryResponse& response) override;
|
||||
@ -45,7 +46,8 @@ public:
|
||||
void onBindingRequest(const aasdk::proto::messages::BindingRequest& request) override;
|
||||
void onChannelError(const aasdk::error::Error& e) override;
|
||||
void onButtonEvent(const projection::ButtonEvent& event) override;
|
||||
void onTouchEvent(const projection::TouchEvent& event) override;
|
||||
void onTouchEvent(aasdk::proto::messages::InputEventIndication inputEventIndication) override;
|
||||
void onMouseEvent(const projection::TouchEvent& event) override;
|
||||
|
||||
private:
|
||||
using std::enable_shared_from_this<InputService>::shared_from_this;
|
||||
@ -53,6 +55,7 @@ private:
|
||||
boost::asio::io_service::strand strand_;
|
||||
aasdk::channel::input::InputServiceChannel::Pointer channel_;
|
||||
projection::IInputDevice::Pointer inputDevice_;
|
||||
bool serviceActive = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
52
include/openauto/Service/MediaStatusService.hpp
Normal file
52
include/openauto/Service/MediaStatusService.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of openauto project.
|
||||
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
|
||||
*
|
||||
* openauto is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* openauto is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with openauto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "aasdk/Channel/AV/MediaStatusServiceChannel.hpp"
|
||||
#include "IService.hpp"
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
class IAndroidAutoInterface;
|
||||
class MediaStatusService: public aasdk::channel::av::IMediaStatusServiceChannelEventHandler, public IService, public std::enable_shared_from_this<MediaStatusService>
|
||||
{
|
||||
public:
|
||||
MediaStatusService(boost::asio::io_service& ioService, aasdk::messenger::IMessenger::Pointer messenger, IAndroidAutoInterface* aa_interface);
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void fillFeatures(aasdk::proto::messages::ServiceDiscoveryResponse& response) override;
|
||||
void onChannelOpenRequest(const aasdk::proto::messages::ChannelOpenRequest& request) override;
|
||||
void onChannelError(const aasdk::error::Error& e) override;
|
||||
void onMetadataUpdate(const aasdk::proto::messages::MediaInfoChannelMetadataData& metadata) override;
|
||||
void onPlaybackUpdate(const aasdk::proto::messages::MediaInfoChannelPlaybackData& playback) override;
|
||||
void setAndroidAutoInterface(IAndroidAutoInterface* aa_interface);
|
||||
|
||||
|
||||
private:
|
||||
using std::enable_shared_from_this<MediaStatusService>::shared_from_this;
|
||||
|
||||
boost::asio::io_service::strand strand_;
|
||||
aasdk::channel::av::MediaStatusServiceChannel::Pointer channel_;
|
||||
IAndroidAutoInterface* aa_interface_ = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
54
include/openauto/Service/NavigationStatusService.hpp
Normal file
54
include/openauto/Service/NavigationStatusService.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of openauto project.
|
||||
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
|
||||
*
|
||||
* openauto is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* openauto is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with openauto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "aasdk/Channel/Navigation/NavigationStatusServiceChannel.hpp"
|
||||
#include "IService.hpp"
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
class IAndroidAutoInterface;
|
||||
class NavigationStatusService: public aasdk::channel::navigation::INavigationStatusServiceChannelEventHandler, public IService, public std::enable_shared_from_this<NavigationStatusService>
|
||||
{
|
||||
public:
|
||||
NavigationStatusService(boost::asio::io_service& ioService, aasdk::messenger::IMessenger::Pointer messenger, IAndroidAutoInterface* aa_interface);
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void fillFeatures(aasdk::proto::messages::ServiceDiscoveryResponse& response) override;
|
||||
void onChannelOpenRequest(const aasdk::proto::messages::ChannelOpenRequest& request) override;
|
||||
void onChannelError(const aasdk::error::Error& e) override;
|
||||
void onTurnEvent(const aasdk::proto::messages::NavigationTurnEvent& turnEvent) override;
|
||||
void onDistanceEvent(const aasdk::proto::messages::NavigationDistanceEvent& distanceEvent) override;
|
||||
void onStatusUpdate(const aasdk::proto::messages::NavigationStatus& navStatus) override;
|
||||
void setAndroidAutoInterface(IAndroidAutoInterface* aa_interface);
|
||||
|
||||
|
||||
private:
|
||||
using std::enable_shared_from_this<NavigationStatusService>::shared_from_this;
|
||||
|
||||
boost::asio::io_service::strand strand_;
|
||||
aasdk::channel::navigation::NavigationStatusServiceChannel::Pointer channel_;
|
||||
IAndroidAutoInterface* aa_interface_ = nullptr;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -24,14 +24,17 @@
|
||||
#include "openauto/Projection/OMXVideoOutput.hpp"
|
||||
#include "openauto/Projection/GSTVideoOutput.hpp"
|
||||
#include "openauto/Projection/QtVideoOutput.hpp"
|
||||
#include "openauto/Service/MediaStatusService.hpp"
|
||||
#include "openauto/Service/NavigationStatusService.hpp"
|
||||
#include "openauto/Service/SensorService.hpp"
|
||||
#include "openauto/Service/InputService.hpp"
|
||||
#include "btservice/btservice.hpp"
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
|
||||
class IAndroidAutoInterface;
|
||||
class ServiceFactory : public IServiceFactory
|
||||
{
|
||||
public:
|
||||
@ -40,7 +43,9 @@ public:
|
||||
void setOpacity(unsigned int alpha);
|
||||
void resize();
|
||||
void setNightMode(bool nightMode);
|
||||
void sendButtonPress(aasdk::proto::enums::ButtonCode::Enum buttonCode, projection::WheelDirection wheelDirection = projection::WheelDirection::NONE, projection::ButtonEventType buttonEventType = projection::ButtonEventType::NONE);
|
||||
void sendKeyEvent(QKeyEvent* event);
|
||||
void setAndroidAutoInterface(IAndroidAutoInterface* aa_interface);
|
||||
static QRect mapActiveAreaToGlobal(QWidget* activeArea);
|
||||
#ifdef USE_OMX
|
||||
static projection::DestRect QRectToDestRect(QRect rect);
|
||||
@ -49,7 +54,9 @@ public:
|
||||
private:
|
||||
IService::Pointer createVideoService(aasdk::messenger::IMessenger::Pointer messenger);
|
||||
IService::Pointer createBluetoothService(aasdk::messenger::IMessenger::Pointer messenger);
|
||||
IService::Pointer createInputService(aasdk::messenger::IMessenger::Pointer messenger);
|
||||
std::shared_ptr<NavigationStatusService> createNavigationStatusService(aasdk::messenger::IMessenger::Pointer messenger);
|
||||
std::shared_ptr<MediaStatusService> createMediaStatusService(aasdk::messenger::IMessenger::Pointer messenger);
|
||||
std::shared_ptr<InputService> createInputService(aasdk::messenger::IMessenger::Pointer messenger);
|
||||
void createAudioServices(ServiceList& serviceList, aasdk::messenger::IMessenger::Pointer messenger);
|
||||
|
||||
boost::asio::io_service& ioService_;
|
||||
@ -68,6 +75,10 @@ private:
|
||||
btservice::btservice btservice_;
|
||||
bool nightMode_;
|
||||
std::weak_ptr<SensorService> sensorService_;
|
||||
std::weak_ptr<InputService> inputService_;
|
||||
std::weak_ptr<MediaStatusService> mediaStatusService_;
|
||||
std::weak_ptr<NavigationStatusService> navStatusService_;
|
||||
IAndroidAutoInterface* aa_interface_ = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ add_library(openauto SHARED
|
||||
Service/Pinger.cpp
|
||||
Service/AndroidAutoEntity.cpp
|
||||
Service/VideoService.cpp
|
||||
Service/NavigationStatusService.cpp
|
||||
Service/MediaStatusService.cpp
|
||||
Configuration/RecentAddressesList.cpp
|
||||
Configuration/Configuration.cpp
|
||||
Projection/RemoteBluetoothDevice.cpp
|
||||
@ -28,6 +30,7 @@ add_library(openauto SHARED
|
||||
Projection/RtAudioOutput.cpp
|
||||
Projection/QtAudioOutput.cpp
|
||||
${CMAKE_SOURCE_DIR}/btservice/AndroidBluetoothServer.cpp
|
||||
${CMAKE_SOURCE_DIR}/btservice/AndroidBluetoothServer.cpp
|
||||
${CMAKE_SOURCE_DIR}/btservice/AndroidBluetoothService.cpp
|
||||
${CMAKE_SOURCE_DIR}/btservice/btservice.cpp
|
||||
${CMAKE_SOURCE_DIR}/include/btservice/AndroidBluetoothServer.hpp
|
||||
@ -42,7 +45,9 @@ add_library(openauto SHARED
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Configuration/IRecentAddressesList.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Configuration/HandednessOfTrafficType.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Configuration/BluetootAdapterType.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Service/MediaAudioService.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Service/NavigationStatusService.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Service/MediaStatusService.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Service/MediaAudioService.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Service/SensorService.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Service/IPinger.hpp
|
||||
${CMAKE_SOURCE_DIR}/include/openauto/Service/IAndroidAutoEntity.hpp
|
||||
@ -110,6 +115,7 @@ target_link_libraries(openauto PUBLIC
|
||||
Threads::Threads
|
||||
${Boost_LIBRARIES}
|
||||
aasdk
|
||||
h264
|
||||
rtaudio
|
||||
Qt5::Bluetooth
|
||||
Qt5::MultimediaWidgets
|
||||
|
@ -35,6 +35,8 @@ const std::string Configuration::cVideoScreenDPIKey = "Video.ScreenDPI";
|
||||
const std::string Configuration::cVideoOMXLayerIndexKey = "Video.OMXLayerIndex";
|
||||
const std::string Configuration::cVideoMarginWidth = "Video.MarginWidth";
|
||||
const std::string Configuration::cVideoMarginHeight = "Video.MarginHeight";
|
||||
const std::string Configuration::cVideoWhitescreenWorkaround = "Video.WhitesreenWorkaround";
|
||||
|
||||
|
||||
const std::string Configuration::cAudioMusicAudioChannelEnabled = "Audio.MusicAudioChannelEnabled";
|
||||
const std::string Configuration::cAudioSpeechAudioChannelEnabled = "Audio.SpeechAudioChannelEnabled";
|
||||
@ -93,6 +95,7 @@ void Configuration::load()
|
||||
|
||||
omxLayerIndex_ = iniConfig.get<int32_t>(cVideoOMXLayerIndexKey, 1);
|
||||
videoMargins_ = QRect(0, 0, iniConfig.get<int32_t>(cVideoMarginWidth, 0), iniConfig.get<int32_t>(cVideoMarginHeight, 0));
|
||||
whitescreenWorkaround_ = iniConfig.get<bool>(cVideoWhitescreenWorkaround, true);
|
||||
|
||||
enableTouchscreen_ = iniConfig.get<bool>(cInputEnableTouchscreenKey, true);
|
||||
this->readButtonCodes(iniConfig);
|
||||
@ -130,6 +133,7 @@ void Configuration::reset()
|
||||
screenDPI_ = 140;
|
||||
omxLayerIndex_ = 1;
|
||||
videoMargins_ = QRect(0, 0, 0, 0);
|
||||
whitescreenWorkaround_ = true;
|
||||
enableTouchscreen_ = true;
|
||||
buttonCodes_.clear();
|
||||
bluetoothAdapterType_ = BluetoothAdapterType::NONE;
|
||||
@ -151,6 +155,7 @@ void Configuration::save()
|
||||
iniConfig.put<int32_t>(cVideoOMXLayerIndexKey, omxLayerIndex_);
|
||||
iniConfig.put<uint32_t>(cVideoMarginWidth, videoMargins_.width());
|
||||
iniConfig.put<uint32_t>(cVideoMarginHeight, videoMargins_.height());
|
||||
iniConfig.put<bool>(cVideoWhitescreenWorkaround, whitescreenWorkaround_);
|
||||
|
||||
iniConfig.put<bool>(cInputEnableTouchscreenKey, enableTouchscreen_);
|
||||
this->writeButtonCodes(iniConfig);
|
||||
@ -240,6 +245,16 @@ QRect Configuration::getVideoMargins() const
|
||||
return videoMargins_;
|
||||
}
|
||||
|
||||
void Configuration::setWhitescreenWorkaround(bool value)
|
||||
{
|
||||
whitescreenWorkaround_ = value;
|
||||
}
|
||||
|
||||
bool Configuration::getWhitescreenWorkaround() const
|
||||
{
|
||||
return whitescreenWorkaround_;
|
||||
}
|
||||
|
||||
bool Configuration::getTouchscreenEnabled() const
|
||||
{
|
||||
return enableTouchscreen_;
|
||||
|
@ -15,12 +15,18 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with openauto. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_GST
|
||||
|
||||
#include "aasdk/Common/Data.hpp"
|
||||
#include "openauto/Projection/GSTVideoOutput.hpp"
|
||||
#include "OpenautoLog.hpp"
|
||||
#include "h264_stream.h"
|
||||
#include <QTimer>
|
||||
// these are needed only for pretty printing of data, to be removed
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
@ -42,28 +48,13 @@ GSTVideoOutput::GSTVideoOutput(configuration::IConfiguration::Pointer configurat
|
||||
|
||||
videoSink_ = surface_->videoSink();
|
||||
|
||||
|
||||
GError* error = nullptr;
|
||||
const char* vidLaunchStr = "appsrc name=mysrc is-live=true block=false max-latency=100 do-timestamp=true stream-type=stream ! queue ! h264parse ! "
|
||||
#ifdef RPI
|
||||
#ifdef PI4
|
||||
"v4l2h264dec ! "
|
||||
#else
|
||||
"omxh264dec ! "
|
||||
#endif
|
||||
#else
|
||||
"avdec_h264 ! "
|
||||
#endif
|
||||
"capsfilter caps=video/x-raw name=mycapsfilter";
|
||||
#ifdef RPI
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] RPI Build, running with " <<
|
||||
#ifdef PI4
|
||||
"v4l2h264dec";
|
||||
#else
|
||||
"omxh264dec";
|
||||
#endif
|
||||
#endif
|
||||
std::string vidLaunchStr = "appsrc name=mysrc is-live=true block=false max-latency=100 do-timestamp=true stream-type=stream ! queue ! h264parse ! capssetter caps=\"video/x-h264,colorimetry=bt709\" ! ";
|
||||
vidLaunchStr += ToPipeline(findPreferredVideoDecoder());
|
||||
vidLaunchStr += " ! videocrop top=0 bottom=0 name=videocropper ! capsfilter caps=video/x-raw name=mycapsfilter";
|
||||
|
||||
vidPipeline_ = gst_parse_launch(vidLaunchStr, &error);
|
||||
vidPipeline_ = gst_parse_launch(vidLaunchStr.c_str(), &error);
|
||||
GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(vidPipeline_));
|
||||
gst_bus_add_watch(bus, (GstBusFunc)&GSTVideoOutput::busCallback, this);
|
||||
gst_object_unref(bus);
|
||||
@ -80,6 +71,8 @@ GSTVideoOutput::GSTVideoOutput(configuration::IConfiguration::Pointer configurat
|
||||
vidSrc_ = GST_APP_SRC(gst_bin_get_by_name(GST_BIN(vidPipeline_), "mysrc"));
|
||||
gst_app_src_set_stream_type(vidSrc_, GST_APP_STREAM_TYPE_STREAM);
|
||||
|
||||
vidCrop_ = GST_VIDEO_FILTER(gst_bin_get_by_name(GST_BIN(vidPipeline_), "videocropper"));
|
||||
|
||||
connect(this, &GSTVideoOutput::startPlayback, this, &GSTVideoOutput::onStartPlayback, Qt::QueuedConnection);
|
||||
connect(this, &GSTVideoOutput::stopPlayback, this, &GSTVideoOutput::onStopPlayback, Qt::QueuedConnection);
|
||||
}
|
||||
@ -90,6 +83,27 @@ GSTVideoOutput::~GSTVideoOutput()
|
||||
gst_object_unref(vidSrc_);
|
||||
}
|
||||
|
||||
|
||||
H264_Decoder GSTVideoOutput::findPreferredVideoDecoder()
|
||||
{
|
||||
for (H264_Decoder decoder : H264_Decoder_Priority_List) {
|
||||
GstElementFactory *decoder_factory = gst_element_factory_find (ToString(decoder));
|
||||
if(decoder_factory != nullptr){
|
||||
gst_object_unref(decoder_factory);
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] Selecting the " << ToString(decoder) << " h264 decoder";
|
||||
return decoder;
|
||||
}
|
||||
}
|
||||
OPENAUTO_LOG(error) << "[GSTVideoOutput] Couldn't find a decoder to use!";
|
||||
return H264_Decoder::unknown;
|
||||
}
|
||||
|
||||
void GSTVideoOutput::dumpDot()
|
||||
{
|
||||
gst_debug_bin_to_dot_file(GST_BIN(vidPipeline_), GST_DEBUG_GRAPH_SHOW_VERBOSE, "pipeline");
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] Dumped dot debug info";
|
||||
}
|
||||
|
||||
gboolean GSTVideoOutput::busCallback(GstBus*, GstMessage* message, gpointer*)
|
||||
{
|
||||
gchar* debug;
|
||||
@ -166,17 +180,103 @@ bool GSTVideoOutput::init()
|
||||
|
||||
void GSTVideoOutput::write(uint64_t timestamp, const aasdk::common::DataConstBuffer& buffer)
|
||||
{
|
||||
GstBuffer* buffer_ = gst_buffer_new_and_alloc(buffer.size);
|
||||
gst_buffer_fill(buffer_, 0, buffer.cdata, buffer.size);
|
||||
int ret = gst_app_src_push_buffer((GstAppSrc*)vidSrc_, buffer_);
|
||||
if(ret != GST_FLOW_OK)
|
||||
if(!firstHeaderParsed && this->configuration_->getWhitescreenWorkaround())
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] push buffer returned " << ret << " for " << buffer.size << "bytes";
|
||||
// I really really really hate this.
|
||||
|
||||
// Raspberry Pi hardware h264 decode appears broken if video_signal_type VUI parameters are given in the h264 header
|
||||
// And we don't have control over Android Auto putting these parameters in (which it does.. on some model phones)
|
||||
// So we pull in h264bitstream to edit this header on the fly
|
||||
|
||||
// This is not a fix, I want to be very clear about that. I don't know what else I'm breaking, or run the
|
||||
// risk of breaking by doing this. This code should only remain here as long as the Pi Engineers haven't released
|
||||
// a firmware/driver fix for this yet. An issue has been opened upstream at https://github.com/raspberrypi/firmware/issues/1673
|
||||
|
||||
// Android Auto seems nice enough to always start a message with a new h264 packet,
|
||||
// but that doesn't mean we don't have multiple within the message.
|
||||
// So if we have a message that _could_ fit two packets (which are delimited by 0x00000001)
|
||||
// then we try to find the second and save the data it contains, while editing and replacing the first.
|
||||
|
||||
// This header should also always be within the first video message we receive from a device... I think
|
||||
|
||||
std::vector<uint8_t> delimit_sequence{0x00, 0x00, 0x00, 0x01};
|
||||
std::vector<uint8_t>::iterator sequence_split;
|
||||
std::vector<uint8_t> incoming_buffer(&buffer.cdata[0], &buffer.cdata[buffer.size]);
|
||||
|
||||
int nal_start, nal_end;
|
||||
uint8_t* buf = (uint8_t *) buffer.cdata;
|
||||
int len = buffer.size;
|
||||
h264_stream_t* h = h264_new();
|
||||
// finds the first NAL packet
|
||||
find_nal_unit(buf, len, &nal_start, &nal_end);
|
||||
// parses it
|
||||
read_nal_unit(h, &buf[nal_start], nal_end - nal_start);
|
||||
// wipe all the color description stuff that breaks Pis
|
||||
h->sps->vui.video_signal_type_present_flag = 0x00;
|
||||
h->sps->vui.video_format = 0x00;
|
||||
h->sps->vui.video_full_range_flag = 0x00;
|
||||
h->sps->vui.colour_description_present_flag = 0x00;
|
||||
h->sps->vui.video_format = 0x00;
|
||||
h->sps->vui.video_full_range_flag = 0x00;
|
||||
h->sps->vui.colour_description_present_flag = 0x00;
|
||||
h->sps->vui.colour_primaries = 0x00;
|
||||
h->sps->vui.transfer_characteristics = 0x00;
|
||||
h->sps->vui.matrix_coefficients = 0x00;
|
||||
|
||||
// grab some storage
|
||||
uint8_t* out_buf = new uint8_t[30];
|
||||
|
||||
// write it to the storage's 3rd index, because h264bitstream seems to have a bug
|
||||
// where it both doesn't write the delimiter, and it prepends a leading 0x00
|
||||
len = write_nal_unit(h, &out_buf[3], 30) + 3;
|
||||
// write the delimiter back
|
||||
out_buf[0] = 0x00;
|
||||
out_buf[1] = 0x00;
|
||||
out_buf[2] = 0x00;
|
||||
out_buf[3] = 0x01;
|
||||
|
||||
// output to gstreamer
|
||||
GstBuffer* buffer_ = gst_buffer_new_and_alloc(len);
|
||||
gst_buffer_fill(buffer_, 0, out_buf, len);
|
||||
int ret = gst_app_src_push_buffer((GstAppSrc*)vidSrc_, buffer_);
|
||||
if(ret != GST_FLOW_OK)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] Injecting header failed";
|
||||
}
|
||||
|
||||
// then check if there's data we need to save
|
||||
if(incoming_buffer.size() >= 8){
|
||||
sequence_split = std::search(incoming_buffer.begin()+4, incoming_buffer.end(), delimit_sequence.begin(), delimit_sequence.end());
|
||||
if(sequence_split != incoming_buffer.end()){
|
||||
std::vector<uint8_t> incoming_data_saved(sequence_split, incoming_buffer.end());
|
||||
GstBuffer* buffer_ = gst_buffer_new_and_alloc(incoming_data_saved.size());
|
||||
gst_buffer_fill(buffer_, 0, incoming_data_saved.data(), incoming_data_saved.size());
|
||||
int ret = gst_app_src_push_buffer((GstAppSrc*)vidSrc_, buffer_);
|
||||
if(ret != GST_FLOW_OK)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] Injecting partial header failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] Intercepted and replaced h264 header";
|
||||
|
||||
firstHeaderParsed=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
GstBuffer* buffer_ = gst_buffer_new_and_alloc(buffer.size);
|
||||
gst_buffer_fill(buffer_, 0, buffer.cdata, buffer.size);
|
||||
int ret = gst_app_src_push_buffer((GstAppSrc*)vidSrc_, buffer_);
|
||||
if(ret != GST_FLOW_OK)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] push buffer returned " << ret << " for " << buffer.size << "bytes";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSTVideoOutput::onStartPlayback()
|
||||
{
|
||||
firstHeaderParsed = false;
|
||||
if(activeCallback_ != nullptr)
|
||||
{
|
||||
activeCallback_(true);
|
||||
@ -195,6 +295,7 @@ void GSTVideoOutput::onStartPlayback()
|
||||
videoWidget_->resize(videoContainer_->size());
|
||||
}
|
||||
videoWidget_->show();
|
||||
QTimer::singleShot(10000, this, SLOT(dumpDot()));
|
||||
}
|
||||
|
||||
void GSTVideoOutput::stop()
|
||||
@ -204,6 +305,8 @@ void GSTVideoOutput::stop()
|
||||
|
||||
void GSTVideoOutput::onStopPlayback()
|
||||
{
|
||||
firstHeaderParsed = false;
|
||||
|
||||
if(activeCallback_ != nullptr)
|
||||
{
|
||||
activeCallback_(false);
|
||||
@ -222,6 +325,50 @@ void GSTVideoOutput::resize()
|
||||
{
|
||||
videoWidget_->resize(videoContainer_->size());
|
||||
}
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int containerWidth = videoContainer_->width();
|
||||
int containerHeight = videoContainer_->height();
|
||||
|
||||
switch(this->getVideoResolution()){
|
||||
case aasdk::proto::enums::VideoResolution_Enum__1080p:
|
||||
width = 1920;
|
||||
height = 1080;
|
||||
break;
|
||||
case aasdk::proto::enums::VideoResolution_Enum__720p:
|
||||
width = 1280;
|
||||
height = 720;
|
||||
break;
|
||||
case aasdk::proto::enums::VideoResolution_Enum__480p:
|
||||
width = 800;
|
||||
height = 480;
|
||||
break;
|
||||
}
|
||||
|
||||
double marginWidth = 0;
|
||||
double marginHeight = 0;
|
||||
|
||||
double widthRatio = (double)containerWidth / width;
|
||||
double heightRatio = (double)containerHeight / height;
|
||||
|
||||
if(widthRatio > heightRatio){
|
||||
//cropping height
|
||||
marginHeight = (widthRatio * height - containerHeight)/widthRatio;
|
||||
marginHeight /= 2;
|
||||
}else{
|
||||
//cropping width
|
||||
marginWidth = (heightRatio * width - containerWidth)/heightRatio;
|
||||
marginWidth /= 2;
|
||||
}
|
||||
|
||||
|
||||
OPENAUTO_LOG(info) << "[GSTVideoOutput] Android Auto is "<< width << "x" << height << ", calculated margins of: " << marginWidth << "x" << marginHeight;
|
||||
g_object_set(vidCrop_, "top", (int)marginHeight, nullptr);
|
||||
g_object_set(vidCrop_, "bottom", (int)marginHeight, nullptr);
|
||||
g_object_set(vidCrop_, "left", (int)marginWidth, nullptr);
|
||||
g_object_set(vidCrop_, "right", (int)marginWidth, nullptr);
|
||||
this->configuration_->setVideoMargins(QRect(0,0,(int)(marginWidth*2), (int)(marginHeight*2)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "OpenautoLog.hpp"
|
||||
#include "openauto/Projection/IInputDeviceEventHandler.hpp"
|
||||
#include "openauto/Projection/InputDevice.hpp"
|
||||
#include <QDebug>
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
@ -33,6 +34,7 @@ InputDevice::InputDevice(QObject& parent, configuration::IConfiguration::Pointer
|
||||
, eventHandler_(nullptr)
|
||||
{
|
||||
this->moveToThread(parent.thread());
|
||||
pointer_id_queue.push(INT_MAX);
|
||||
}
|
||||
|
||||
void InputDevice::start(IInputDeviceEventHandler& eventHandler)
|
||||
@ -67,10 +69,14 @@ bool InputDevice::eventFilter(QObject* obj, QEvent* event)
|
||||
return this->handleKeyEvent(event, key);
|
||||
}
|
||||
}
|
||||
else if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove)
|
||||
else if(event->type() == QEvent::TouchBegin || event->type() == QEvent::TouchUpdate || event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel)
|
||||
{
|
||||
return this->handleTouchEvent(event);
|
||||
}
|
||||
else if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove)
|
||||
{
|
||||
return this->handleMouseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
return QObject::eventFilter(obj, event);
|
||||
@ -175,7 +181,6 @@ bool InputDevice::handleKeyEvent(QEvent* event, QKeyEvent* key)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputDevice::handleTouchEvent(QEvent* event)
|
||||
{
|
||||
if(!configuration_->getTouchscreenEnabled())
|
||||
@ -183,6 +188,81 @@ bool InputDevice::handleTouchEvent(QEvent* event)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QTouchEvent* qtTouchEvent = static_cast<QTouchEvent*>(event);
|
||||
aasdk::proto::enums::TouchAction::Enum type;
|
||||
auto timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch());
|
||||
aasdk::proto::messages::InputEventIndication inputEventIndication;
|
||||
inputEventIndication.set_timestamp(timestamp.count());
|
||||
auto touchEvent = inputEventIndication.mutable_touch_event();
|
||||
switch(event->type()){
|
||||
case QEvent::TouchBegin:
|
||||
type = aasdk::proto::enums::TouchAction::PRESS;
|
||||
break;
|
||||
case QEvent::TouchUpdate:
|
||||
if(qtTouchEvent->touchPointStates().testFlag(Qt::TouchPointPressed)) type = aasdk::proto::enums::TouchAction::POINTER_DOWN;
|
||||
else if(qtTouchEvent->touchPointStates().testFlag(Qt::TouchPointReleased)) type = aasdk::proto::enums::TouchAction::POINTER_UP;
|
||||
else{
|
||||
type = aasdk::proto::enums::TouchAction::DRAG;
|
||||
touchEvent->set_action_index(0);
|
||||
}
|
||||
break;
|
||||
case QEvent::TouchEnd:
|
||||
type = aasdk::proto::enums::TouchAction::RELEASE;
|
||||
break;
|
||||
case QEvent::TouchCancel:
|
||||
type = aasdk::proto::enums::TouchAction::RELEASE;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
touchEvent->set_touch_action(type);
|
||||
|
||||
auto pointers = qtTouchEvent->touchPoints();
|
||||
|
||||
// What's with this pointer map and pointer id queue?
|
||||
// Pointers get a unique id during their lifespan, and android auto gets pissy when you try and just use the unique id qt assigns
|
||||
// but it's happier with smaller valued ids
|
||||
// and since there are no garuntees about id range, or similar (because of drivers and whatnot)
|
||||
// pointer id queue is a min heap, with the first item being the next availible "smaller id"
|
||||
// and pointer maps stores the qt id: small id relation
|
||||
// the pointer id queue will expand as needed
|
||||
|
||||
// I kinda hate this, but it works
|
||||
|
||||
for(int i=0; i<pointers.count(); i++)
|
||||
{
|
||||
if(pointers[i].state() == Qt::TouchPointPressed){
|
||||
touchEvent->set_action_index(i);
|
||||
if(pointer_id_queue.top() == INT_MAX){
|
||||
pointer_id_queue.push(max_pointers++);
|
||||
}
|
||||
pointer_map.insert(pointers[i].id(), pointer_id_queue.top());
|
||||
pointer_id_queue.pop();
|
||||
}
|
||||
else if(pointers[i].state() == Qt::TouchPointReleased){
|
||||
touchEvent->set_action_index(i);
|
||||
pointer_id_queue.push(pointer_map.take(pointers[i].id()));
|
||||
}
|
||||
|
||||
auto touchLocation = touchEvent->add_touch_location();
|
||||
touchLocation->set_x((static_cast<float>(pointers[i].pos().x()) / touchscreenGeometry_.width()) * displayGeometry_.width());
|
||||
touchLocation->set_y((static_cast<float>(pointers[i].pos().y()) / touchscreenGeometry_.height()) * displayGeometry_.height());
|
||||
touchLocation->set_pointer_id(pointer_map[pointers[i].id()]);
|
||||
}
|
||||
|
||||
eventHandler_->onTouchEvent(inputEventIndication);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
bool InputDevice::handleMouseEvent(QEvent* event)
|
||||
{
|
||||
if(!configuration_->getTouchscreenEnabled())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
aasdk::proto::enums::TouchAction::Enum type;
|
||||
|
||||
switch(event->type())
|
||||
@ -205,7 +285,7 @@ bool InputDevice::handleTouchEvent(QEvent* event)
|
||||
{
|
||||
const uint32_t x = (static_cast<float>(mouse->pos().x()) / touchscreenGeometry_.width()) * displayGeometry_.width();
|
||||
const uint32_t y = (static_cast<float>(mouse->pos().y()) / touchscreenGeometry_.height()) * displayGeometry_.height();
|
||||
eventHandler_->onTouchEvent({type, x, y, 0});
|
||||
eventHandler_->onMouseEvent({type, x, y, 0});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -24,6 +24,8 @@ namespace openauto
|
||||
namespace projection
|
||||
{
|
||||
|
||||
std::mutex RtAudioOutput::mutex_;
|
||||
|
||||
RtAudioOutput::RtAudioOutput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate)
|
||||
: channelCount_(channelCount)
|
||||
, sampleSize_(sampleSize)
|
||||
|
@ -30,7 +30,7 @@ InputService::InputService(boost::asio::io_service& ioService, aasdk::messenger:
|
||||
, channel_(std::make_shared<aasdk::channel::input::InputServiceChannel>(strand_, std::move(messenger)))
|
||||
, inputDevice_(std::move(inputDevice))
|
||||
{
|
||||
|
||||
OPENAUTO_LOG(info) << "[InputService] Created";
|
||||
}
|
||||
|
||||
void InputService::start()
|
||||
@ -39,6 +39,7 @@ void InputService::start()
|
||||
OPENAUTO_LOG(info) << "[InputService] start.";
|
||||
channel_->receive(this->shared_from_this());
|
||||
});
|
||||
serviceActive = true;
|
||||
}
|
||||
|
||||
void InputService::stop()
|
||||
@ -47,6 +48,7 @@ void InputService::stop()
|
||||
OPENAUTO_LOG(info) << "[InputService] stop.";
|
||||
inputDevice_->stop();
|
||||
});
|
||||
serviceActive = false;
|
||||
}
|
||||
|
||||
void InputService::fillFeatures(aasdk::proto::messages::ServiceDiscoveryResponse& response)
|
||||
@ -128,11 +130,12 @@ void InputService::onBindingRequest(const aasdk::proto::messages::BindingRequest
|
||||
|
||||
void InputService::onChannelError(const aasdk::error::Error& e)
|
||||
{
|
||||
OPENAUTO_LOG(error) << "[SensorService] channel error: " << e.what();
|
||||
OPENAUTO_LOG(error) << "[InputService] channel error: " << e.what();
|
||||
}
|
||||
|
||||
void InputService::onButtonEvent(const projection::ButtonEvent& event)
|
||||
{
|
||||
if(!serviceActive) return;
|
||||
auto timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch());
|
||||
|
||||
strand_.dispatch([this, self = this->shared_from_this(), event = std::move(event), timestamp = std::move(timestamp)]() {
|
||||
@ -160,7 +163,39 @@ void InputService::onButtonEvent(const projection::ButtonEvent& event)
|
||||
});
|
||||
}
|
||||
|
||||
void InputService::onTouchEvent(const projection::TouchEvent& event)
|
||||
void InputService::sendButtonPress(aasdk::proto::enums::ButtonCode::Enum buttonCode, projection::WheelDirection wheelDirection, projection::ButtonEventType buttonEventType)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[InputService] injecting button press";
|
||||
if(buttonCode == aasdk::proto::enums::ButtonCode::SCROLL_WHEEL)
|
||||
{
|
||||
onButtonEvent({projection::ButtonEventType::NONE, wheelDirection, buttonCode});
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(buttonEventType == projection::ButtonEventType::NONE){
|
||||
onButtonEvent({projection::ButtonEventType::PRESS, projection::WheelDirection::NONE, buttonCode});
|
||||
onButtonEvent({projection::ButtonEventType::RELEASE, projection::WheelDirection::NONE, buttonCode});
|
||||
}
|
||||
else
|
||||
{
|
||||
onButtonEvent({buttonEventType, projection::WheelDirection::NONE, buttonCode});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputService::onTouchEvent(aasdk::proto::messages::InputEventIndication inputEventIndication)
|
||||
{
|
||||
|
||||
strand_.dispatch([this, self = this->shared_from_this(), inputEventIndication = std::move(inputEventIndication)]() {
|
||||
|
||||
auto promise = aasdk::channel::SendPromise::defer(strand_);
|
||||
promise->then([]() {}, std::bind(&InputService::onChannelError, this->shared_from_this(), std::placeholders::_1));
|
||||
channel_->sendInputEventIndication(inputEventIndication, std::move(promise));
|
||||
});
|
||||
}
|
||||
|
||||
void InputService::onMouseEvent(const projection::TouchEvent& event)
|
||||
{
|
||||
auto timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch());
|
||||
|
||||
|
97
openauto/Service/MediaStatusService.cpp
Normal file
97
openauto/Service/MediaStatusService.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include "OpenautoLog.hpp"
|
||||
#include "openauto/Service/MediaStatusService.hpp"
|
||||
#include "openauto/Service/IAndroidAutoInterface.hpp"
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
|
||||
MediaStatusService::MediaStatusService(boost::asio::io_service& ioService, aasdk::messenger::IMessenger::Pointer messenger, IAndroidAutoInterface* aa_interface)
|
||||
: strand_(ioService)
|
||||
, channel_(std::make_shared<aasdk::channel::av::MediaStatusServiceChannel>(strand_, std::move(messenger)))
|
||||
{
|
||||
aa_interface_ = aa_interface;
|
||||
}
|
||||
|
||||
void MediaStatusService::start()
|
||||
{
|
||||
strand_.dispatch([this, self = this->shared_from_this()]() {
|
||||
OPENAUTO_LOG(info) << "[MediaStatusService] start.";
|
||||
channel_->receive(this->shared_from_this());
|
||||
});
|
||||
}
|
||||
|
||||
void MediaStatusService::stop()
|
||||
{
|
||||
strand_.dispatch([this, self = this->shared_from_this()]() {
|
||||
OPENAUTO_LOG(info) << "[MediaStatusService] stop.";
|
||||
});
|
||||
}
|
||||
|
||||
void MediaStatusService::fillFeatures(aasdk::proto::messages::ServiceDiscoveryResponse& response)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[MediaStatusService] fill features";
|
||||
|
||||
auto* channelDescriptor = response.add_channels();
|
||||
auto mediaStatusChannel = channelDescriptor->mutable_media_infochannel();
|
||||
channelDescriptor->set_channel_id(static_cast<uint32_t>(channel_->getId()));
|
||||
}
|
||||
|
||||
void MediaStatusService::onChannelOpenRequest(const aasdk::proto::messages::ChannelOpenRequest& request)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[MediaStatusService] open request, priority: " << request.priority();
|
||||
const aasdk::proto::enums::Status::Enum status = aasdk::proto::enums::Status::OK;
|
||||
OPENAUTO_LOG(info) << "[MediaStatusService] open status: " << status;
|
||||
|
||||
aasdk::proto::messages::ChannelOpenResponse response;
|
||||
response.set_status(status);
|
||||
|
||||
auto promise = aasdk::channel::SendPromise::defer(strand_);
|
||||
promise->then([]() {}, std::bind(&MediaStatusService::onChannelError, this->shared_from_this(), std::placeholders::_1));
|
||||
channel_->sendChannelOpenResponse(response, std::move(promise));
|
||||
|
||||
channel_->receive(this->shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
void MediaStatusService::onChannelError(const aasdk::error::Error& e)
|
||||
{
|
||||
OPENAUTO_LOG(error) << "[MediaStatusService] channel error: " << e.what();
|
||||
}
|
||||
|
||||
void MediaStatusService::onMetadataUpdate(const aasdk::proto::messages::MediaInfoChannelMetadataData& metadata)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[MediaStatusService] Metadata update"
|
||||
<< ", track: " << metadata.track_name()
|
||||
<< (metadata.has_artist_name()?", artist: ":"") << (metadata.has_artist_name()?metadata.artist_name():"")
|
||||
<< (metadata.has_album_name()?", album: ":"") << (metadata.has_album_name()?metadata.album_name():"")
|
||||
<< ", length: " << metadata.track_length();
|
||||
if(aa_interface_ != NULL)
|
||||
{
|
||||
aa_interface_->mediaMetadataUpdate(metadata);
|
||||
}
|
||||
channel_->receive(this->shared_from_this());
|
||||
}
|
||||
|
||||
void MediaStatusService::onPlaybackUpdate(const aasdk::proto::messages::MediaInfoChannelPlaybackData& playback)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[MediaStatusService] Playback update"
|
||||
<< ", source: " << playback.media_source()
|
||||
<< ", state: " << playback.playback_state()
|
||||
<< ", progress: " << playback.track_progress();
|
||||
if(aa_interface_ != NULL)
|
||||
{
|
||||
aa_interface_->mediaPlaybackUpdate(playback);
|
||||
}
|
||||
channel_->receive(this->shared_from_this());
|
||||
}
|
||||
|
||||
void MediaStatusService::setAndroidAutoInterface(IAndroidAutoInterface* aa_interface)
|
||||
{
|
||||
this->aa_interface_ = aa_interface;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
119
openauto/Service/NavigationStatusService.cpp
Normal file
119
openauto/Service/NavigationStatusService.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include "OpenautoLog.hpp"
|
||||
#include "openauto/Service/NavigationStatusService.hpp"
|
||||
#include "aasdk_proto/ManeuverTypeEnum.pb.h"
|
||||
#include "aasdk_proto/ManeuverDirectionEnum.pb.h"
|
||||
#include "aasdk_proto/DistanceUnitEnum.pb.h"
|
||||
#include "openauto/Service/IAndroidAutoInterface.hpp"
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
|
||||
NavigationStatusService::NavigationStatusService(boost::asio::io_service& ioService, aasdk::messenger::IMessenger::Pointer messenger, IAndroidAutoInterface* aa_interface)
|
||||
: strand_(ioService)
|
||||
, channel_(std::make_shared<aasdk::channel::navigation::NavigationStatusServiceChannel>(strand_, std::move(messenger)))
|
||||
{
|
||||
this->aa_interface_ = aa_interface;
|
||||
}
|
||||
|
||||
void NavigationStatusService::start()
|
||||
{
|
||||
strand_.dispatch([this, self = this->shared_from_this()]() {
|
||||
OPENAUTO_LOG(info) << "[NavigationStatusService] start.";
|
||||
channel_->receive(this->shared_from_this());
|
||||
});
|
||||
}
|
||||
|
||||
void NavigationStatusService::stop()
|
||||
{
|
||||
strand_.dispatch([this, self = this->shared_from_this()]() {
|
||||
OPENAUTO_LOG(info) << "[NavigationStatusService] stop.";
|
||||
});
|
||||
}
|
||||
|
||||
void NavigationStatusService::fillFeatures(aasdk::proto::messages::ServiceDiscoveryResponse& response)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[NavigationStatusService] fill features";
|
||||
|
||||
auto* channelDescriptor = response.add_channels();
|
||||
channelDescriptor->set_channel_id(static_cast<uint32_t>(channel_->getId()));
|
||||
auto navStatusChannel = channelDescriptor->mutable_navigation_channel();
|
||||
navStatusChannel->set_minimum_interval_ms(1000);
|
||||
navStatusChannel->set_type(aasdk::proto::enums::NavigationTurnType::IMAGE);
|
||||
auto* imageOptions = new aasdk::proto::data::NavigationImageOptions();
|
||||
imageOptions->set_colour_depth_bits(16);
|
||||
imageOptions->set_height(256);
|
||||
imageOptions->set_width(256);
|
||||
imageOptions->set_dunno(255);
|
||||
navStatusChannel->set_allocated_image_options(imageOptions);
|
||||
}
|
||||
|
||||
void NavigationStatusService::onChannelOpenRequest(const aasdk::proto::messages::ChannelOpenRequest& request)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[NavigationStatusService] open request, priority: " << request.priority();
|
||||
const aasdk::proto::enums::Status::Enum status = aasdk::proto::enums::Status::OK;
|
||||
OPENAUTO_LOG(info) << "[NavigationStatusService] open status: " << status;
|
||||
|
||||
aasdk::proto::messages::ChannelOpenResponse response;
|
||||
response.set_status(status);
|
||||
|
||||
auto promise = aasdk::channel::SendPromise::defer(strand_);
|
||||
promise->then([]() {}, std::bind(&NavigationStatusService::onChannelError, this->shared_from_this(), std::placeholders::_1));
|
||||
channel_->sendChannelOpenResponse(response, std::move(promise));
|
||||
|
||||
channel_->receive(this->shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
void NavigationStatusService::onChannelError(const aasdk::error::Error& e)
|
||||
{
|
||||
OPENAUTO_LOG(error) << "[NavigationStatusService] channel error: " << e.what();
|
||||
}
|
||||
|
||||
void NavigationStatusService::onStatusUpdate(const aasdk::proto::messages::NavigationStatus& navStatus)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[NavigationStatusService] Navigation Status Update"
|
||||
<< ", Status: " << aasdk::proto::messages::NavigationStatus_Enum_Name(navStatus.status());
|
||||
if(aa_interface_ != NULL)
|
||||
{
|
||||
aa_interface_->navigationStatusUpdate(navStatus);
|
||||
}
|
||||
channel_->receive(this->shared_from_this());
|
||||
}
|
||||
|
||||
void NavigationStatusService::onTurnEvent(const aasdk::proto::messages::NavigationTurnEvent& turnEvent)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[NavigationStatusService] Turn Event"
|
||||
<< ", Street: " << turnEvent.street_name()
|
||||
<< ", Maneuver: " << aasdk::proto::enums::ManeuverDirection_Enum_Name(turnEvent.maneuverdirection()) << " " << aasdk::proto::enums::ManeuverType_Enum_Name(turnEvent.maneuvertype());
|
||||
if(aa_interface_ != NULL)
|
||||
{
|
||||
aa_interface_->navigationTurnEvent(turnEvent);
|
||||
}
|
||||
channel_->receive(this->shared_from_this());
|
||||
}
|
||||
|
||||
void NavigationStatusService::onDistanceEvent(const aasdk::proto::messages::NavigationDistanceEvent& distanceEvent)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "[NavigationStatusService] Distance Event"
|
||||
<< ", Distance (meters): " << distanceEvent.meters()
|
||||
<< ", Time To Turn (seconds): " << distanceEvent.timetostepseconds()
|
||||
<< ", Distance: " << distanceEvent.distancetostepmillis()/1000.0
|
||||
<< " ("<<aasdk::proto::enums::DistanceUnit_Enum_Name(distanceEvent.distanceunit())<<")";
|
||||
if(aa_interface_ != NULL)
|
||||
{
|
||||
aa_interface_->navigationDistanceEvent(distanceEvent);
|
||||
}
|
||||
channel_->receive(this->shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
void NavigationStatusService::setAndroidAutoInterface(IAndroidAutoInterface* aa_interface)
|
||||
{
|
||||
this->aa_interface_ = aa_interface;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -54,7 +54,6 @@ void SensorService::fillFeatures(aasdk::proto::messages::ServiceDiscoveryRespons
|
||||
|
||||
auto* channelDescriptor = response.add_channels();
|
||||
channelDescriptor->set_channel_id(static_cast<uint32_t>(channel_->getId()));
|
||||
|
||||
auto* sensorChannel = channelDescriptor->mutable_sensor_channel();
|
||||
sensorChannel->add_sensors()->set_type(aasdk::proto::enums::SensorType::DRIVING_STATUS);
|
||||
//sensorChannel->add_sensors()->set_type(aasdk::proto::enums::SensorType::LOCATION);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "openauto/Service/SensorService.hpp"
|
||||
#include "openauto/Service/BluetoothService.hpp"
|
||||
#include "openauto/Service/InputService.hpp"
|
||||
#include "openauto/Service/NavigationStatusService.hpp"
|
||||
#include "openauto/Projection/QtVideoOutput.hpp"
|
||||
#include "openauto/Projection/GSTVideoOutput.hpp"
|
||||
#include "openauto/Projection/OMXVideoOutput.hpp"
|
||||
@ -40,12 +41,13 @@
|
||||
#include "openauto/Projection/LocalBluetoothDevice.hpp"
|
||||
#include "openauto/Projection/RemoteBluetoothDevice.hpp"
|
||||
#include "openauto/Projection/DummyBluetoothDevice.hpp"
|
||||
#include "openauto/Service/IAndroidAutoInterface.hpp"
|
||||
|
||||
|
||||
namespace openauto
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
|
||||
ServiceFactory::ServiceFactory(boost::asio::io_service& ioService, configuration::IConfiguration::Pointer configuration, QWidget *activeArea, std::function<void(bool)> activeCallback, bool nightMode)
|
||||
: ioService_(ioService)
|
||||
, configuration_(std::move(configuration))
|
||||
@ -62,6 +64,7 @@ ServiceFactory::ServiceFactory(boost::asio::io_service& ioService, configuration
|
||||
, btservice_(configuration_)
|
||||
, nightMode_(nightMode)
|
||||
{
|
||||
OPENAUTO_LOG(info) << "SERVICE FACTORY INITED";
|
||||
|
||||
}
|
||||
|
||||
@ -79,8 +82,16 @@ ServiceList ServiceFactory::create(aasdk::messenger::IMessenger::Pointer messeng
|
||||
|
||||
serviceList.emplace_back(this->createVideoService(messenger));
|
||||
serviceList.emplace_back(this->createBluetoothService(messenger));
|
||||
serviceList.emplace_back(this->createInputService(messenger));
|
||||
std::shared_ptr<NavigationStatusService> navStatusService = this->createNavigationStatusService(messenger);
|
||||
navStatusService_ = navStatusService;
|
||||
serviceList.emplace_back(navStatusService);
|
||||
std::shared_ptr<MediaStatusService> mediaStatusService = this->createMediaStatusService(messenger);
|
||||
mediaStatusService_ = mediaStatusService;
|
||||
serviceList.emplace_back(mediaStatusService);
|
||||
|
||||
std::shared_ptr<InputService> inputService = this->createInputService(messenger);
|
||||
inputService_ = inputService;
|
||||
serviceList.emplace_back(inputService);
|
||||
return serviceList;
|
||||
}
|
||||
|
||||
@ -127,7 +138,17 @@ IService::Pointer ServiceFactory::createBluetoothService(aasdk::messenger::IMess
|
||||
return std::make_shared<BluetoothService>(ioService_, messenger, std::move(bluetoothDevice));
|
||||
}
|
||||
|
||||
IService::Pointer ServiceFactory::createInputService(aasdk::messenger::IMessenger::Pointer messenger)
|
||||
std::shared_ptr<NavigationStatusService> ServiceFactory::createNavigationStatusService(aasdk::messenger::IMessenger::Pointer messenger)
|
||||
{
|
||||
return std::make_shared<NavigationStatusService>(ioService_, messenger, aa_interface_);
|
||||
}
|
||||
|
||||
std::shared_ptr<MediaStatusService> ServiceFactory::createMediaStatusService(aasdk::messenger::IMessenger::Pointer messenger)
|
||||
{
|
||||
return std::make_shared<MediaStatusService>(ioService_, messenger, aa_interface_);
|
||||
}
|
||||
|
||||
std::shared_ptr<InputService> ServiceFactory::createInputService(aasdk::messenger::IMessenger::Pointer messenger)
|
||||
{
|
||||
QRect videoGeometry;
|
||||
switch(configuration_->getVideoResolution())
|
||||
@ -145,6 +166,11 @@ IService::Pointer ServiceFactory::createInputService(aasdk::messenger::IMessenge
|
||||
break;
|
||||
}
|
||||
|
||||
//account for margins being applied to android auto
|
||||
videoGeometry.setWidth(videoGeometry.width()-configuration_->getVideoMargins().width());
|
||||
videoGeometry.setHeight(videoGeometry.height()-configuration_->getVideoMargins().height());
|
||||
|
||||
|
||||
QObject* inputObject = activeArea_ == nullptr ? qobject_cast<QObject*>(QApplication::instance()) : qobject_cast<QObject*>(activeArea_);
|
||||
inputDevice_ = std::make_shared<projection::InputDevice>(*inputObject, configuration_, std::move(screenGeometry_), std::move(videoGeometry));
|
||||
|
||||
@ -212,6 +238,20 @@ void ServiceFactory::resize()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void ServiceFactory::setAndroidAutoInterface(IAndroidAutoInterface* aa_interface){
|
||||
if(aa_interface==NULL) return;
|
||||
this->aa_interface_ = aa_interface;
|
||||
|
||||
if(std::shared_ptr<MediaStatusService> mediaStatusService = mediaStatusService_.lock())
|
||||
{
|
||||
mediaStatusService->setAndroidAutoInterface(aa_interface);
|
||||
}
|
||||
if(std::shared_ptr<NavigationStatusService> navStatusService = navStatusService_.lock())
|
||||
{
|
||||
navStatusService->setAndroidAutoInterface(aa_interface);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ServiceFactory::setNightMode(bool nightMode)
|
||||
{
|
||||
@ -222,6 +262,15 @@ void ServiceFactory::setNightMode(bool nightMode)
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceFactory::sendButtonPress(aasdk::proto::enums::ButtonCode::Enum buttonCode, projection::WheelDirection wheelDirection, projection::ButtonEventType buttonEventType)
|
||||
{
|
||||
if(std::shared_ptr<InputService> inputService = inputService_.lock())
|
||||
{
|
||||
|
||||
inputService->sendButtonPress(buttonCode, wheelDirection, buttonEventType);
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceFactory::sendKeyEvent(QKeyEvent* event)
|
||||
{
|
||||
if(inputDevice_ != nullptr)
|
||||
|
Loading…
x
Reference in New Issue
Block a user