Compare commits

...

10 Commits

Author SHA1 Message Date
4ab5a162c4 Switch to opendsh openauto 2025-04-21 19:04:52 +03:00
deadmasterog
e7caeb4d49
use static mutex member in RtAudioOutput class to serialize RtAudio calls across instances (#32) 2024-10-22 21:32:09 -05:00
Cole Brinsfield
6496f8e360
gstreamer pipeline updates, pi bt updates. (#29)
* new services being added to openauto

* cmake for new services

* MediaStatusService, transitioning away from qt gstreamer

* Reverting change to qmlglsink as it's not functional on raspbian as of this moment

* Multitouch support within android auto

* add night mode toggle to aainterface

* Dot dump debug functionality

* NavigationStatusService, MediaStatusService, AAInterface, Hotkey migration

* Add a delay to dot dumping because caps aren't selected until data is streaming, so we can't rely on just a PLAYING status

* Update btservice connection method for pi4

* Update for pi4 compiling

* missing brace

* Yet another new bt connect process

* oops bad merge

* bad merge

* Remove pi specific bt connection

* Try to play nice with bullseye

* Select decoder automatically based on priority list

* Quick cleanup
2022-07-13 00:46:00 -05:00
Cole Brinsfield
8fa72b4f43
whitescreen workaround (#27)
* Terrible, Horrible, No Good, Very Bad Hack for h264

* Add setting to turn on this terrible hack

* Handle multiple video resolutions

* Use h264bitstream to do this better

* Remove all parameters we don't want

* Cleaning things up a little

* Renaming whitescreen workaround, defaulting to on
2022-02-12 14:33:02 -06:00
Cole Brinsfield
e2b6985d87
Navigation Status Service, Media Status Service, Touchscreen Multitouch Support, AAInterface (#26)
* new services being added to openauto

* cmake for new services

* MediaStatusService, transitioning away from qt gstreamer

* Reverting change to qmlglsink as it's not functional on raspbian as of this moment

* Multitouch support within android auto

* add night mode toggle to aainterface

* Dot dump debug functionality

* NavigationStatusService, MediaStatusService, AAInterface, Hotkey migration

* Add a delay to dot dumping because caps aren't selected until data is streaming, so we can't rely on just a PLAYING status

* PR cleanup
2022-01-20 11:47:15 -06:00
Matthew Hilton
cf77993f3c
switch patch version to use git hash (#25)
* switched patch version to use git hash

* added message to output version
2021-12-25 11:53:05 -06:00
Matthew Hilton
618a014100
add cpack support (#24)
Added CPack support
Fixed error with RPI_REVISION/RPI_MODEL IF not matching
2021-08-01 21:17:03 -05:00
Cole Brinsfield
efb9d8bc6f
Allow for scroll wheel injection (#23)
* Working on proper resizing of AA

* allow for custom aspect ratios

* A mess of code to support sending input events from dash

* Restructuring input injection to be much nicer

* Code Cleanup

* Allow for scroll wheel injection
2021-07-25 18:03:08 -07:00
Cole Brinsfield
658005da61
input service binding (#22)
* Working on proper resizing of AA

* allow for custom aspect ratios

* A mess of code to support sending input events from dash

* Restructuring input injection to be much nicer

* Code Cleanup
2021-07-15 18:11:59 -05:00
Cole Brinsfield
e940b8036e
custom aspect ratios (#21)
* Working on proper resizing of AA

* allow for custom aspect ratios
2021-05-21 21:59:57 -05:00
35 changed files with 1669 additions and 323 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
lib/
bin/
.idea/
build/
CMakeLists.txt.user
cmake-build-*/

View File

@ -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)

View File

@ -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;
}

View File

@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:22pt; font-weight:600; font-style:italic; color:#3465a4;&quot;&gt;Waiting for device...&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/ico_androidauto.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic; color:#eeeeec;&quot;&gt;Plug in your device to start AndroidAuto (tm).&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/f1xpl/openauto&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;https://github.com/f1xpl/openauto&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/ico_warning.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; color:#ef2929;&quot;&gt;WARNING!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; color:#ef2929;&quot;&gt;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.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ef2929;&quot;&gt;This software is not certified by Google Inc. It is created for R&amp;amp;D purposes and may not work as expected by the original authors. Do not use while driving.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; color:#ef2929;&quot;&gt;You use this software at your own risk.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/ico_warning.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/ico_info.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;AndroidAuto is registered trademark of Google Inc.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;If you are looking for a Raspberry PI compatible 12V power supply equipped with an ignition switch, visit &lt;/span&gt;&lt;a href=&quot;https://bluewavestudio.io/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;https://bluewavestudio.io&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

BIN
autoapp/assets/artist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

BIN
autoapp/assets/radio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

View File

@ -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
}
}
}
}

View 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)

View 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

View File

@ -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;
};
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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_;

View File

@ -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;
};
}

View File

@ -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;
};
}

View File

@ -50,7 +50,7 @@ private:
uint32_t sampleRate_;
SequentialBuffer audioBuffer_;
std::unique_ptr<RtAudio> dac_;
std::mutex mutex_;
static std::mutex mutex_;
};
}

View 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;
};
}
}

View File

@ -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;
};
}

View 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;
};
}
}

View 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;
};
}
}

View File

@ -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;
};
}

View File

@ -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

View File

@ -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_;

View File

@ -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)));
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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());

View 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;
}
}
}

View 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;
}
}
}

View File

@ -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);

View File

@ -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)