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
This commit is contained in:
parent
e2b6985d87
commit
8fa72b4f43
@ -47,6 +47,7 @@ find_package(Protobuf REQUIRED)
|
|||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
find_package(rtaudio REQUIRED)
|
find_package(rtaudio REQUIRED)
|
||||||
find_package(aasdk REQUIRED)
|
find_package(aasdk REQUIRED)
|
||||||
|
find_package(h264 REQUIRED)
|
||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
|
|
||||||
include(${base_directory}/cmake_modules/gitversion.cmake)
|
include(${base_directory}/cmake_modules/gitversion.cmake)
|
||||||
|
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)
|
@ -50,6 +50,8 @@ public:
|
|||||||
int32_t getOMXLayerIndex() const override;
|
int32_t getOMXLayerIndex() const override;
|
||||||
void setVideoMargins(QRect value) override;
|
void setVideoMargins(QRect value) override;
|
||||||
QRect getVideoMargins() const override;
|
QRect getVideoMargins() const override;
|
||||||
|
void setWhitescreenWorkaround(bool value) override;
|
||||||
|
bool getWhitescreenWorkaround() const override;
|
||||||
|
|
||||||
bool getTouchscreenEnabled() const override;
|
bool getTouchscreenEnabled() const override;
|
||||||
void setTouchscreenEnabled(bool value) override;
|
void setTouchscreenEnabled(bool value) override;
|
||||||
@ -91,6 +93,7 @@ private:
|
|||||||
size_t screenDPI_;
|
size_t screenDPI_;
|
||||||
int32_t omxLayerIndex_;
|
int32_t omxLayerIndex_;
|
||||||
QRect videoMargins_;
|
QRect videoMargins_;
|
||||||
|
bool whitescreenWorkaround_;
|
||||||
bool enableTouchscreen_;
|
bool enableTouchscreen_;
|
||||||
ButtonCodes buttonCodes_;
|
ButtonCodes buttonCodes_;
|
||||||
BluetoothAdapterType bluetoothAdapterType_;
|
BluetoothAdapterType bluetoothAdapterType_;
|
||||||
@ -115,6 +118,7 @@ private:
|
|||||||
static const std::string cVideoOMXLayerIndexKey;
|
static const std::string cVideoOMXLayerIndexKey;
|
||||||
static const std::string cVideoMarginWidth;
|
static const std::string cVideoMarginWidth;
|
||||||
static const std::string cVideoMarginHeight;
|
static const std::string cVideoMarginHeight;
|
||||||
|
static const std::string cVideoWhitescreenWorkaround;
|
||||||
|
|
||||||
static const std::string cAudioMusicAudioChannelEnabled;
|
static const std::string cAudioMusicAudioChannelEnabled;
|
||||||
static const std::string cAudioSpeechAudioChannelEnabled;
|
static const std::string cAudioSpeechAudioChannelEnabled;
|
||||||
|
@ -59,6 +59,8 @@ public:
|
|||||||
virtual int32_t getOMXLayerIndex() const = 0;
|
virtual int32_t getOMXLayerIndex() const = 0;
|
||||||
virtual void setVideoMargins(QRect value) = 0;
|
virtual void setVideoMargins(QRect value) = 0;
|
||||||
virtual QRect getVideoMargins() const = 0;
|
virtual QRect getVideoMargins() const = 0;
|
||||||
|
virtual void setWhitescreenWorkaround(bool value) = 0;
|
||||||
|
virtual bool getWhitescreenWorkaround() const = 0;
|
||||||
|
|
||||||
virtual bool getTouchscreenEnabled() const = 0;
|
virtual bool getTouchscreenEnabled() const = 0;
|
||||||
virtual void setTouchscreenEnabled(bool value) = 0;
|
virtual void setTouchscreenEnabled(bool value) = 0;
|
||||||
|
@ -78,6 +78,8 @@ private:
|
|||||||
static GstPadProbeReturn convertProbe(GstPad* pad, GstPadProbeInfo* info, void*);
|
static GstPadProbeReturn convertProbe(GstPad* pad, GstPadProbeInfo* info, void*);
|
||||||
static gboolean busCallback(GstBus*, GstMessage* message, gpointer*);
|
static gboolean busCallback(GstBus*, GstMessage* message, gpointer*);
|
||||||
|
|
||||||
|
bool firstHeaderParsed = false;
|
||||||
|
|
||||||
QGst::ElementPtr videoSink_;
|
QGst::ElementPtr videoSink_;
|
||||||
QQuickWidget* videoWidget_;
|
QQuickWidget* videoWidget_;
|
||||||
GstElement* vidPipeline_;
|
GstElement* vidPipeline_;
|
||||||
|
@ -115,6 +115,7 @@ target_link_libraries(openauto PUBLIC
|
|||||||
Threads::Threads
|
Threads::Threads
|
||||||
${Boost_LIBRARIES}
|
${Boost_LIBRARIES}
|
||||||
aasdk
|
aasdk
|
||||||
|
h264
|
||||||
rtaudio
|
rtaudio
|
||||||
Qt5::Bluetooth
|
Qt5::Bluetooth
|
||||||
Qt5::MultimediaWidgets
|
Qt5::MultimediaWidgets
|
||||||
|
@ -35,6 +35,8 @@ const std::string Configuration::cVideoScreenDPIKey = "Video.ScreenDPI";
|
|||||||
const std::string Configuration::cVideoOMXLayerIndexKey = "Video.OMXLayerIndex";
|
const std::string Configuration::cVideoOMXLayerIndexKey = "Video.OMXLayerIndex";
|
||||||
const std::string Configuration::cVideoMarginWidth = "Video.MarginWidth";
|
const std::string Configuration::cVideoMarginWidth = "Video.MarginWidth";
|
||||||
const std::string Configuration::cVideoMarginHeight = "Video.MarginHeight";
|
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::cAudioMusicAudioChannelEnabled = "Audio.MusicAudioChannelEnabled";
|
||||||
const std::string Configuration::cAudioSpeechAudioChannelEnabled = "Audio.SpeechAudioChannelEnabled";
|
const std::string Configuration::cAudioSpeechAudioChannelEnabled = "Audio.SpeechAudioChannelEnabled";
|
||||||
@ -93,6 +95,7 @@ void Configuration::load()
|
|||||||
|
|
||||||
omxLayerIndex_ = iniConfig.get<int32_t>(cVideoOMXLayerIndexKey, 1);
|
omxLayerIndex_ = iniConfig.get<int32_t>(cVideoOMXLayerIndexKey, 1);
|
||||||
videoMargins_ = QRect(0, 0, iniConfig.get<int32_t>(cVideoMarginWidth, 0), iniConfig.get<int32_t>(cVideoMarginHeight, 0));
|
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);
|
enableTouchscreen_ = iniConfig.get<bool>(cInputEnableTouchscreenKey, true);
|
||||||
this->readButtonCodes(iniConfig);
|
this->readButtonCodes(iniConfig);
|
||||||
@ -130,6 +133,7 @@ void Configuration::reset()
|
|||||||
screenDPI_ = 140;
|
screenDPI_ = 140;
|
||||||
omxLayerIndex_ = 1;
|
omxLayerIndex_ = 1;
|
||||||
videoMargins_ = QRect(0, 0, 0, 0);
|
videoMargins_ = QRect(0, 0, 0, 0);
|
||||||
|
whitescreenWorkaround_ = true;
|
||||||
enableTouchscreen_ = true;
|
enableTouchscreen_ = true;
|
||||||
buttonCodes_.clear();
|
buttonCodes_.clear();
|
||||||
bluetoothAdapterType_ = BluetoothAdapterType::NONE;
|
bluetoothAdapterType_ = BluetoothAdapterType::NONE;
|
||||||
@ -151,6 +155,7 @@ void Configuration::save()
|
|||||||
iniConfig.put<int32_t>(cVideoOMXLayerIndexKey, omxLayerIndex_);
|
iniConfig.put<int32_t>(cVideoOMXLayerIndexKey, omxLayerIndex_);
|
||||||
iniConfig.put<uint32_t>(cVideoMarginWidth, videoMargins_.width());
|
iniConfig.put<uint32_t>(cVideoMarginWidth, videoMargins_.width());
|
||||||
iniConfig.put<uint32_t>(cVideoMarginHeight, videoMargins_.height());
|
iniConfig.put<uint32_t>(cVideoMarginHeight, videoMargins_.height());
|
||||||
|
iniConfig.put<bool>(cVideoWhitescreenWorkaround, whitescreenWorkaround_);
|
||||||
|
|
||||||
iniConfig.put<bool>(cInputEnableTouchscreenKey, enableTouchscreen_);
|
iniConfig.put<bool>(cInputEnableTouchscreenKey, enableTouchscreen_);
|
||||||
this->writeButtonCodes(iniConfig);
|
this->writeButtonCodes(iniConfig);
|
||||||
@ -240,6 +245,16 @@ QRect Configuration::getVideoMargins() const
|
|||||||
return videoMargins_;
|
return videoMargins_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Configuration::setWhitescreenWorkaround(bool value)
|
||||||
|
{
|
||||||
|
whitescreenWorkaround_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Configuration::getWhitescreenWorkaround() const
|
||||||
|
{
|
||||||
|
return whitescreenWorkaround_;
|
||||||
|
}
|
||||||
|
|
||||||
bool Configuration::getTouchscreenEnabled() const
|
bool Configuration::getTouchscreenEnabled() const
|
||||||
{
|
{
|
||||||
return enableTouchscreen_;
|
return enableTouchscreen_;
|
||||||
|
@ -20,7 +20,13 @@
|
|||||||
#include "aasdk/Common/Data.hpp"
|
#include "aasdk/Common/Data.hpp"
|
||||||
#include "openauto/Projection/GSTVideoOutput.hpp"
|
#include "openauto/Projection/GSTVideoOutput.hpp"
|
||||||
#include "OpenautoLog.hpp"
|
#include "OpenautoLog.hpp"
|
||||||
|
#include "h264_stream.h"
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
// these are needed only for pretty printing of data, to be removed
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
namespace openauto
|
namespace openauto
|
||||||
{
|
{
|
||||||
@ -176,17 +182,103 @@ bool GSTVideoOutput::init()
|
|||||||
|
|
||||||
void GSTVideoOutput::write(uint64_t timestamp, const aasdk::common::DataConstBuffer& buffer)
|
void GSTVideoOutput::write(uint64_t timestamp, const aasdk::common::DataConstBuffer& buffer)
|
||||||
{
|
{
|
||||||
GstBuffer* buffer_ = gst_buffer_new_and_alloc(buffer.size);
|
if(!firstHeaderParsed && this->configuration_->getWhitescreenWorkaround())
|
||||||
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";
|
// 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()
|
void GSTVideoOutput::onStartPlayback()
|
||||||
{
|
{
|
||||||
|
firstHeaderParsed = false;
|
||||||
if(activeCallback_ != nullptr)
|
if(activeCallback_ != nullptr)
|
||||||
{
|
{
|
||||||
activeCallback_(true);
|
activeCallback_(true);
|
||||||
@ -215,6 +307,8 @@ void GSTVideoOutput::stop()
|
|||||||
|
|
||||||
void GSTVideoOutput::onStopPlayback()
|
void GSTVideoOutput::onStopPlayback()
|
||||||
{
|
{
|
||||||
|
firstHeaderParsed = false;
|
||||||
|
|
||||||
if(activeCallback_ != nullptr)
|
if(activeCallback_ != nullptr)
|
||||||
{
|
{
|
||||||
activeCallback_(false);
|
activeCallback_(false);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user