diff --git a/CMakeLists.txt b/CMakeLists.txt index 57d4c0e..1814800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ option(CHIAKI_ENABLE_ANDROID "Enable Android (Use only as part of the Gradle Pro option(CHIAKI_ENABLE_SWITCH "Enable Nintendo Switch (Requires devKitPro libnx)" OFF) tri_option(CHIAKI_ENABLE_SETSU "Enable libsetsu for touchpad input from controller" AUTO) option(CHIAKI_LIB_ENABLE_OPUS "Use Opus as part of Chiaki Lib" ON) +tri_option(CHIAKI_ENABLE_PI_DECODER "Enable Raspberry Pi-specific video decoder (requires libraspberrypi0 and libraspberrypi-doc)" AUTO) option(CHIAKI_LIB_ENABLE_MBEDTLS "Use mbedtls instead of OpenSSL as part of Chiaki Lib" OFF) option(CHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT "Use OpenSSL as CMake external project" OFF) option(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER "Use SDL Gamecontroller for Input" ON) @@ -99,6 +100,27 @@ if(CHIAKI_LIB_ENABLE_MBEDTLS) add_definitions(-DCHIAKI_LIB_ENABLE_MBEDTLS) endif() +if(CHIAKI_ENABLE_PI_DECODER) + find_package(ILClient) + if(ILClient_FOUND) + set(CHIAKI_ENABLE_PI_DECODER ON) + else() + if(NOT CHIAKI_ENABLE_PI_DECODER STREQUAL AUTO) + message(FATAL_ERROR " +CHIAKI_ENABLE_PI_DECODER is set to ON, but its dependencies (ilclient source and libs) could not be resolved. +The Raspberry Pi Decoder is only supported on Raspberry Pi OS and requires libraspberrypi0 and libraspberrypi-doc.") + endif() + set(CHIAKI_ENABLE_PI_DECODER OFF) + endif() +endif() + +if(CHIAKI_ENABLE_PI_DECODER) + message(STATUS "Pi Decoder enabled") +else() + message(STATUS "Pi Decoder disabled") +endif() + + add_subdirectory(lib) if(CHIAKI_ENABLE_CLI) diff --git a/cmake/FindILClient.cmake b/cmake/FindILClient.cmake new file mode 100644 index 0000000..b24fccd --- /dev/null +++ b/cmake/FindILClient.cmake @@ -0,0 +1,57 @@ +# Provides ILClient::ILClient +# (Raspberry Pi-specific video decoding stuff, very specific for libraspberrypi0 and libraspberrypi-doc) + +set(_required_libs + /opt/vc/lib/libbcm_host.so + /opt/vc/lib/libvcilcs.a + /opt/vc/lib/libvchiq_arm.so + /opt/vc/lib/libvcos.so) + +unset(_libvars) +foreach(_lib ${_required_libs}) + get_filename_component(_libname "${_lib}" NAME_WE) + set(_libvar "ILClient_${_libname}_LIBRARY") + list(APPEND _libvars "${_libvar}") + if(EXISTS "${_lib}") + set("${_libvar}" "${_lib}") + else() + set("${_libvar}" "${_libvar}-NOTFOUND") + endif() +endforeach() + +find_path(ILClient_INCLUDE_DIR bcm_host.h + PATHS /opt/vc/include + NO_DEFAULT_PATH) + +find_path(ILClient_SOURCE_DIR ilclient.c + PATHS /opt/vc/src/hello_pi/libs/ilclient) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ILClient + FOUND_VAR ILClient_FOUND + REQUIRED_VARS + ${_libvars} + ILClient_INCLUDE_DIR + ILClient_SOURCE_DIR) + +if(ILClient_FOUND) + if(NOT TARGET ILClient::ILClient) + # see /opt/vc/src/hello_pi/libs/ilclient/Makefile + add_library(ilclient STATIC + "${ILClient_SOURCE_DIR}/ilclient.c" + "${ILClient_SOURCE_DIR}/ilcore.c") + target_include_directories(ilclient PUBLIC + "${ILClient_INCLUDE_DIR}" + "${ILClient_SOURCE_DIR}") + target_compile_definitions(ilclient PUBLIC + HAVE_LIBOPENMAX=2 + OMX + OMX_SKIP64BIT + USE_EXTERNAL_OMX + HAVE_LIBBCM_HOST + USE_EXTERNAL_LIBBCM_HOST + USE_VCHIQ_ARM) + target_link_libraries(ilclient PUBLIC ${_required_libs}) + add_library(ILClient::ILClient ALIAS ilclient) + endif() +endif() diff --git a/gui/include/settings.h b/gui/include/settings.h index d2cee79..0d5975a 100644 --- a/gui/include/settings.h +++ b/gui/include/settings.h @@ -30,6 +30,12 @@ enum class DisconnectAction Ask }; +enum class Decoder +{ + Ffmpeg, + Pi +}; + class Settings : public QObject { Q_OBJECT @@ -69,6 +75,9 @@ class Settings : public QObject unsigned int GetBitrate() const; void SetBitrate(unsigned int bitrate); + Decoder GetDecoder() const; + void SetDecoder(Decoder decoder); + HardwareDecodeEngine GetHardwareDecodeEngine() const; void SetHardwareDecodeEngine(HardwareDecodeEngine enabled); diff --git a/gui/include/settingsdialog.h b/gui/include/settingsdialog.h index a44e209..b0b529e 100644 --- a/gui/include/settingsdialog.h +++ b/gui/include/settingsdialog.h @@ -25,6 +25,7 @@ class SettingsDialog : public QDialog QComboBox *fps_combo_box; QLineEdit *bitrate_edit; QLineEdit *audio_buffer_size_edit; + QCheckBox *pi_decoder_check_box; QComboBox *hardware_decode_combo_box; QListWidget *registered_hosts_list_widget; @@ -41,6 +42,7 @@ class SettingsDialog : public QDialog void BitrateEdited(); void AudioBufferSizeEdited(); void HardwareDecodeEngineSelected(); + void UpdateHardwareDecodeEngineComboBox(); void UpdateRegisteredHosts(); void UpdateRegisteredHostsButtons(); diff --git a/gui/include/streamsession.h b/gui/include/streamsession.h index 82acf86..2d5cb32 100644 --- a/gui/include/streamsession.h +++ b/gui/include/streamsession.h @@ -6,6 +6,10 @@ #include #include +#if CHIAKI_LIB_ENABLE_PI_DECODER +#include +#endif + #if CHIAKI_GUI_ENABLE_SETSU #include #endif @@ -14,6 +18,7 @@ #include "exception.h" #include "sessionlog.h" #include "controllermanager.h" +#include "settings.h" #include #include @@ -35,6 +40,7 @@ struct StreamSessionConnectInfo { Settings *settings; QMap key_map; + Decoder decoder; HardwareDecodeEngine hw_decode_engine; uint32_t log_level_mask; QString log_file; @@ -70,7 +76,10 @@ class StreamSession : public QObject ChiakiControllerState keyboard_state; - VideoDecoder video_decoder; + VideoDecoder *video_decoder; +#if CHIAKI_LIB_ENABLE_PI_DECODER + ChiakiPiDecoder *pi_decoder; +#endif unsigned int audio_buffer_size; QAudioOutput *audio_output; @@ -101,7 +110,10 @@ class StreamSession : public QObject void SetLoginPIN(const QString &pin); Controller *GetController() { return controller; } - VideoDecoder *GetVideoDecoder() { return &video_decoder; } + VideoDecoder *GetVideoDecoder() { return video_decoder; } +#if CHIAKI_LIB_ENABLE_PI_DECODER + ChiakiPiDecoder *GetPiDecoder() { return pi_decoder; } +#endif void HandleKeyboardEvent(QKeyEvent *event); void HandleMouseEvent(QMouseEvent *event); diff --git a/gui/include/streamwindow.h b/gui/include/streamwindow.h index 63c7440..5476672 100644 --- a/gui/include/streamwindow.h +++ b/gui/include/streamwindow.h @@ -25,6 +25,7 @@ class StreamWindow: public QMainWindow AVOpenGLWidget *av_widget; void Init(); + void UpdateVideoTransform(); protected: void keyPressEvent(QKeyEvent *event) override; @@ -32,6 +33,9 @@ class StreamWindow: public QMainWindow void closeEvent(QCloseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + void moveEvent(QMoveEvent *event) override; + void changeEvent(QEvent *event) override; private slots: void SessionQuit(ChiakiQuitReason reason, const QString &reason_str); diff --git a/gui/src/settings.cpp b/gui/src/settings.cpp index 40bf298..bedce8d 100644 --- a/gui/src/settings.cpp +++ b/gui/src/settings.cpp @@ -3,6 +3,8 @@ #include #include +#include + #define SETTINGS_VERSION 1 Settings::Settings(QObject *parent) : QObject(parent) @@ -79,6 +81,28 @@ unsigned int Settings::GetAudioBufferSizeRaw() const return settings.value("settings/audio_buffer_size", 0).toUInt(); } +static const QMap decoder_values = { + { Decoder::Ffmpeg, "ffmpeg" }, + { Decoder::Pi, "pi" } +}; + +static const Decoder decoder_default = Decoder::Pi; + +Decoder Settings::GetDecoder() const +{ +#if CHIAKI_LIB_ENABLE_PI_DECODER + auto v = settings.value("settings/decoder", decoder_values[decoder_default]).toString(); + return decoder_values.key(v, decoder_default); +#else + return Decoder::Ffmpeg; +#endif +} + +void Settings::SetDecoder(Decoder decoder) +{ + settings.setValue("settings/decoder", decoder_values[decoder]); +} + static const QMap hw_decode_engine_values = { { HW_DECODE_NONE, "none" }, { HW_DECODE_VAAPI, "vaapi" }, diff --git a/gui/src/settingsdialog.cpp b/gui/src/settingsdialog.cpp index f16d110..2e96b83 100644 --- a/gui/src/settingsdialog.cpp +++ b/gui/src/settingsdialog.cpp @@ -20,6 +20,8 @@ #include #include +#include + const char * const about_string = "

Chiaki

by thestr4ng3r, version " CHIAKI_VERSION "" @@ -157,6 +159,18 @@ SettingsDialog::SettingsDialog(Settings *settings, QWidget *parent) : QDialog(pa auto decode_settings_layout = new QFormLayout(); decode_settings->setLayout(decode_settings_layout); +#if CHIAKI_LIB_ENABLE_PI_DECODER + pi_decoder_check_box = new QCheckBox(this); + pi_decoder_check_box->setChecked(settings->GetDecoder() == Decoder::Pi); + connect(pi_decoder_check_box, &QCheckBox::toggled, this, [this](bool checked) { + this->settings->SetDecoder(checked ? Decoder::Pi : Decoder::Ffmpeg); + UpdateHardwareDecodeEngineComboBox(); + }); + decode_settings_layout->addRow(tr("Use Raspberry Pi Decoder:"), pi_decoder_check_box); +#else + pi_decoder_check_box = nullptr; +#endif + hardware_decode_combo_box = new QComboBox(this); static const QList> hardware_decode_engines = { { HW_DECODE_NONE, "none"}, @@ -173,6 +187,7 @@ SettingsDialog::SettingsDialog(Settings *settings, QWidget *parent) : QDialog(pa } connect(hardware_decode_combo_box, SIGNAL(currentIndexChanged(int)), this, SLOT(HardwareDecodeEngineSelected())); decode_settings_layout->addRow(tr("Hardware decode method:"), hardware_decode_combo_box); + UpdateHardwareDecodeEngineComboBox(); // Registered Consoles @@ -281,6 +296,11 @@ void SettingsDialog::HardwareDecodeEngineSelected() settings->SetHardwareDecodeEngine((HardwareDecodeEngine)hardware_decode_combo_box->currentData().toInt()); } +void SettingsDialog::UpdateHardwareDecodeEngineComboBox() +{ + hardware_decode_combo_box->setEnabled(settings->GetDecoder() == Decoder::Ffmpeg); +} + void SettingsDialog::UpdateBitratePlaceholder() { bitrate_edit->setPlaceholderText(tr("Automatic (%1)").arg(settings->GetVideoProfile().bitrate)); diff --git a/gui/src/streamsession.cpp b/gui/src/streamsession.cpp index f26f9a2..bb67f91 100644 --- a/gui/src/streamsession.cpp +++ b/gui/src/streamsession.cpp @@ -18,6 +18,7 @@ StreamSessionConnectInfo::StreamSessionConnectInfo(Settings *settings, QString h : settings(settings) { key_map = settings->GetControllerMappingForDecoding(); + decoder = settings->GetDecoder(); hw_decode_engine = settings->GetHardwareDecodeEngine(); log_level_mask = settings->GetLogLevelMask(); log_file = CreateLogFilename(); @@ -42,11 +43,30 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje : QObject(parent), log(this, connect_info.log_level_mask, connect_info.log_file), controller(nullptr), - video_decoder(connect_info.hw_decode_engine, log.GetChiakiLog()), + video_decoder(nullptr), +#if CHIAKI_LIB_ENABLE_PI_DECODER + pi_decoder(nullptr), +#endif audio_output(nullptr), audio_io(nullptr) { connected = false; + +#if CHIAKI_LIB_ENABLE_PI_DECODER + if(connect_info.decoder == Decoder::Pi) + { + pi_decoder = CHIAKI_NEW(ChiakiPiDecoder); + if(chiaki_pi_decoder_init(pi_decoder, log.GetChiakiLog()) != CHIAKI_ERR_SUCCESS) + throw ChiakiException("Failed to initialize Raspberry Pi Decoder"); + } + else + { +#endif + video_decoder = new VideoDecoder(connect_info.hw_decode_engine, log.GetChiakiLog()); +#if CHIAKI_LIB_ENABLE_PI_DECODER + } +#endif + chiaki_opus_decoder_init(&opus_decoder, log.GetChiakiLog()); audio_buffer_size = connect_info.audio_buffer_size; @@ -75,7 +95,17 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje chiaki_opus_decoder_get_sink(&opus_decoder, &audio_sink); chiaki_session_set_audio_sink(&session, &audio_sink); - chiaki_session_set_video_sample_cb(&session, VideoSampleCb, this); +#if CHIAKI_LIB_ENABLE_PI_DECODER + if(pi_decoder) + chiaki_session_set_video_sample_cb(&session, chiaki_pi_decoder_video_sample_cb, pi_decoder); + else + { +#endif + chiaki_session_set_video_sample_cb(&session, VideoSampleCb, this); +#if CHIAKI_LIB_ENABLE_PI_DECODER + } +#endif + chiaki_session_set_event_cb(&session, EventCb, this); #if CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER @@ -107,6 +137,14 @@ StreamSession::~StreamSession() #if CHIAKI_GUI_ENABLE_SETSU setsu_free(setsu); #endif +#if CHIAKI_LIB_ENABLE_PI_DECODER + if(pi_decoder) + { + chiaki_pi_decoder_fini(pi_decoder); + free(pi_decoder); + } +#endif + delete video_decoder; } void StreamSession::Start() @@ -285,7 +323,7 @@ void StreamSession::PushAudioFrame(int16_t *buf, size_t samples_count) void StreamSession::PushVideoSample(uint8_t *buf, size_t buf_size) { - video_decoder.PushFrame(buf, buf_size); + video_decoder->PushFrame(buf, buf_size); } void StreamSession::Event(ChiakiEvent *event) diff --git a/gui/src/streamwindow.cpp b/gui/src/streamwindow.cpp index ea712cf..eae263d 100644 --- a/gui/src/streamwindow.cpp +++ b/gui/src/streamwindow.cpp @@ -47,8 +47,17 @@ void StreamWindow::Init() connect(session, &StreamSession::SessionQuit, this, &StreamWindow::SessionQuit); connect(session, &StreamSession::LoginPINRequested, this, &StreamWindow::LoginPINRequested); - av_widget = new AVOpenGLWidget(session->GetVideoDecoder(), this); - setCentralWidget(av_widget); + if(session->GetVideoDecoder()) + { + av_widget = new AVOpenGLWidget(session->GetVideoDecoder(), this); + setCentralWidget(av_widget); + } + else + { + QWidget *bg_widget = new QWidget(this); + bg_widget->setStyleSheet("background-color: black;"); + setCentralWidget(bg_widget); + } grabKeyboard(); @@ -167,3 +176,34 @@ void StreamWindow::ToggleFullscreen() av_widget->HideMouse(); } } + +void StreamWindow::resizeEvent(QResizeEvent *event) +{ + UpdateVideoTransform(); + QMainWindow::resizeEvent(event); +} + +void StreamWindow::moveEvent(QMoveEvent *event) +{ + UpdateVideoTransform(); + QMainWindow::moveEvent(event); +} + +void StreamWindow::changeEvent(QEvent *event) +{ + if(event->type() == QEvent::ActivationChange) + UpdateVideoTransform(); + QMainWindow::changeEvent(event); +} + +void StreamWindow::UpdateVideoTransform() +{ +#if CHIAKI_LIB_ENABLE_PI_DECODER + ChiakiPiDecoder *pi_decoder = session->GetPiDecoder(); + if(pi_decoder) + { + QRect r = geometry(); + chiaki_pi_decoder_set_params(pi_decoder, r.x(), r.y(), r.width(), r.height(), isActiveWindow()); + } +#endif +} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 4d9e98f..5af259f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -73,6 +73,12 @@ set(SOURCE_FILES src/regist.c src/opusdecoder.c) +if(CHIAKI_ENABLE_PI_DECODER) + list(APPEND HEADER_FILES include/chiaki/pidecoder.h) + list(APPEND SOURCE_FILES src/pidecoder.c) +endif() +set(CHIAKI_LIB_ENABLE_PI_DECODER "${CHIAKI_ENABLE_PI_DECODER}") + add_subdirectory(protobuf) set_source_files_properties(${CHIAKI_LIB_PROTO_SOURCE_FILES} ${CHIAKI_LIB_PROTO_HEADER_FILES} PROPERTIES GENERATED TRUE) include_directories("${CHIAKI_LIB_PROTO_INCLUDE_DIR}") @@ -119,6 +125,10 @@ endif() target_link_libraries(chiaki-lib Nanopb::nanopb) target_link_libraries(chiaki-lib Jerasure::Jerasure) +if(CHIAKI_ENABLE_PI_DECODER) + target_link_libraries(chiaki-lib ILClient::ILClient) +endif() + if(CHIAKI_LIB_ENABLE_OPUS) target_link_libraries(chiaki-lib ${Opus_LIBRARIES}) endif() diff --git a/lib/config.h.in b/lib/config.h.in index cd0a5eb..d06ab98 100644 --- a/lib/config.h.in +++ b/lib/config.h.in @@ -4,5 +4,6 @@ #define CHIAKI_CONFIG_H #cmakedefine01 CHIAKI_LIB_ENABLE_OPUS +#cmakedefine01 CHIAKI_LIB_ENABLE_PI_DECODER #endif // CHIAKI_CONFIG_H diff --git a/lib/include/chiaki/pidecoder.h b/lib/include/chiaki/pidecoder.h new file mode 100644 index 0000000..af2b18b --- /dev/null +++ b/lib/include/chiaki/pidecoder.h @@ -0,0 +1,36 @@ +#ifndef CHIAKI_PI_DECODER_H +#define CHIAKI_PI_DECODER_H + +#include +#include + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chiaki_pi_decoder_t +{ + ChiakiLog *log; + TUNNEL_T tunnel[2]; + COMPONENT_T *components[3]; + ILCLIENT_T *client; + COMPONENT_T *video_decode; + COMPONENT_T *video_render; + bool port_settings_changed; + bool first_packet; +} ChiakiPiDecoder; + +CHIAKI_EXPORT ChiakiErrorCode chiaki_pi_decoder_init(ChiakiPiDecoder *decoder, ChiakiLog *log); +CHIAKI_EXPORT void chiaki_pi_decoder_fini(ChiakiPiDecoder *decoder); +CHIAKI_EXPORT void chiaki_pi_decoder_set_params(ChiakiPiDecoder *decoder, int x, int y, int w, int h, bool visible); +CHIAKI_EXPORT bool chiaki_pi_decoder_video_sample_cb(uint8_t *buf, size_t buf_size, void *user); + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_PI_DECODER_H diff --git a/lib/src/pidecoder.c b/lib/src/pidecoder.c new file mode 100644 index 0000000..72a22f5 --- /dev/null +++ b/lib/src/pidecoder.c @@ -0,0 +1,261 @@ + +#include + +#include + +#include +#include +#include +#include + +#define MAX_DECODE_UNIT_SIZE 262144 + +CHIAKI_EXPORT ChiakiErrorCode chiaki_pi_decoder_init(ChiakiPiDecoder *decoder, ChiakiLog *log) +{ + memset(decoder, 0, sizeof(ChiakiPiDecoder)); + + bcm_host_init(); + + if(!(decoder->client = ilclient_init())) + { + CHIAKI_LOGE(decoder->log, "ilclient_init failed"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + + if(OMX_Init() != OMX_ErrorNone) + { + CHIAKI_LOGE(decoder->log, "OMX_Init failed"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + + if(ilclient_create_component(decoder->client, &decoder->video_decode, "video_decode", + ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) + { + CHIAKI_LOGE(decoder->log, "ilclient_create_component failed for video_decode"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + decoder->components[0] = decoder->video_decode; + + if(ilclient_create_component(decoder->client, &decoder->video_render, "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0) + { + CHIAKI_LOGE(decoder->log, "ilclient_create_component failed for video_render"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + decoder->components[1] = decoder->video_render; + + set_tunnel(decoder->tunnel, decoder->video_decode, 131, decoder->video_render, 90); + + ilclient_change_component_state(decoder->video_decode, OMX_StateIdle); + + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + OMX_PARAM_DATAUNITTYPE unit; + memset(&unit, 0, sizeof(OMX_PARAM_DATAUNITTYPE)); + unit.nSize = sizeof(OMX_PARAM_DATAUNITTYPE); + unit.nVersion.nVersion = OMX_VERSION; + unit.nPortIndex = 130; + unit.eUnitType = OMX_DataUnitCodedPicture; + unit.eEncapsulationType = OMX_DataEncapsulationElementaryStream; + + if(OMX_SetParameter(ILC_GET_HANDLE(decoder->video_decode), OMX_IndexParamVideoPortFormat, &format) != OMX_ErrorNone + || OMX_SetParameter(ILC_GET_HANDLE(decoder->video_decode), OMX_IndexParamBrcmDataUnit, &unit) != OMX_ErrorNone) + { + CHIAKI_LOGE(decoder->log, "OMX_SetParameter failed for video parameters"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + + OMX_CONFIG_LATENCYTARGETTYPE latencyTarget; + memset(&latencyTarget, 0, sizeof(OMX_CONFIG_LATENCYTARGETTYPE)); + latencyTarget.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + latencyTarget.nVersion.nVersion = OMX_VERSION; + latencyTarget.nPortIndex = 90; + latencyTarget.bEnabled = OMX_TRUE; + latencyTarget.nFilter = 2; + latencyTarget.nTarget = 4000; + latencyTarget.nShift = 3; + latencyTarget.nSpeedFactor = -135; + latencyTarget.nInterFactor = 500; + latencyTarget.nAdjCap = 20; + + if(OMX_SetParameter(ILC_GET_HANDLE(decoder->video_render), OMX_IndexConfigLatencyTarget, &latencyTarget) != OMX_ErrorNone) + { + CHIAKI_LOGE(decoder->log, "OMX_SetParameter failed for render parameters"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + + OMX_CONFIG_ROTATIONTYPE rotationType; + memset(&rotationType, 0, sizeof(OMX_CONFIG_ROTATIONTYPE)); + rotationType.nSize = sizeof(OMX_CONFIG_ROTATIONTYPE); + rotationType.nVersion.nVersion = OMX_VERSION; + rotationType.nPortIndex = 90; + //rotationType.nRotation = 90; // example + + if(OMX_SetParameter(ILC_GET_HANDLE(decoder->video_render), OMX_IndexConfigCommonRotate, &rotationType) != OMX_ErrorNone) + { + CHIAKI_LOGE(decoder->log, "OMX_SetParameter failed for rotation"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + + OMX_PARAM_PORTDEFINITIONTYPE port; + + memset(&port, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + port.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + port.nVersion.nVersion = OMX_VERSION; + port.nPortIndex = 130; + if(OMX_GetParameter(ILC_GET_HANDLE(decoder->video_decode), OMX_IndexParamPortDefinition, &port) != OMX_ErrorNone) + { + CHIAKI_LOGE(decoder->log, "Failed to get decoder port definition\n"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + + // Increase the buffer size to fit the largest possible frame + port.nBufferSize = MAX_DECODE_UNIT_SIZE; + + if(OMX_SetParameter(ILC_GET_HANDLE(decoder->video_decode), OMX_IndexParamPortDefinition, &port) != OMX_ErrorNone) + { + CHIAKI_LOGE(decoder->log, "OMX_SetParameter failed for port"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + + if(ilclient_enable_port_buffers(decoder->video_decode, 130, NULL, NULL, NULL) != 0) + { + CHIAKI_LOGE(decoder->log, "ilclient_enable_port_buffers failed"); + chiaki_pi_decoder_fini(decoder); + return CHIAKI_ERR_UNKNOWN; + } + + decoder->port_settings_changed = false; + decoder->first_packet = true; + + ilclient_change_component_state(decoder->video_decode, OMX_StateExecuting); + + CHIAKI_LOGI(decoder->log, "Raspberry Pi Decoder initialized"); + return CHIAKI_ERR_SUCCESS; +} + +CHIAKI_EXPORT void chiaki_pi_decoder_fini(ChiakiPiDecoder *decoder) +{ + if(decoder->video_decode) + { + OMX_BUFFERHEADERTYPE *buf; + if((buf = ilclient_get_input_buffer(decoder->video_decode, 130, 1))) + { + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + OMX_EmptyThisBuffer(ILC_GET_HANDLE(decoder->video_decode), buf); + } + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(decoder->tunnel, 0); + + ilclient_disable_port_buffers(decoder->video_decode, 130, NULL, NULL, NULL); + + ilclient_disable_tunnel(decoder->tunnel); + ilclient_teardown_tunnels(decoder->tunnel); + + ilclient_state_transition(decoder->components, OMX_StateIdle); + ilclient_state_transition(decoder->components, OMX_StateLoaded); + + ilclient_cleanup_components(decoder->components); + } + + OMX_Deinit(); + if(decoder->client) + ilclient_destroy(decoder->client); +} + +static bool push_buffer(ChiakiPiDecoder *decoder, uint8_t *buf, size_t buf_size) +{ + OMX_BUFFERHEADERTYPE *omx_buf = ilclient_get_input_buffer(decoder->video_decode, 130, 1); + if(!omx_buf) + { + CHIAKI_LOGE(decoder->log, "ilclient_get_input_buffer failed"); + return false; + } + + if(omx_buf->nAllocLen < buf_size) + { + CHIAKI_LOGE(decoder->log, "Buffer from omx is too small for frame"); + return false; + } + + omx_buf->nFilledLen = 0; + omx_buf->nOffset = 0; + omx_buf->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; + if(decoder->first_packet) + { + omx_buf->nFlags |= OMX_BUFFERFLAG_STARTTIME; + decoder->first_packet = false; + } + + memcpy(omx_buf->pBuffer + omx_buf->nFilledLen, buf, buf_size); + omx_buf->nFilledLen += buf_size; + + if(!decoder->port_settings_changed + && ((omx_buf->nFilledLen > 0 && ilclient_remove_event(decoder->video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) + || (omx_buf->nFilledLen == 0 && ilclient_wait_for_event(decoder->video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + decoder->port_settings_changed = true; + + if(ilclient_setup_tunnel(decoder->tunnel, 0, 0) != 0) + { + CHIAKI_LOGE(decoder->log, "ilclient_setup_tunnel failed"); + return false; + } + + ilclient_change_component_state(decoder->video_render, OMX_StateExecuting); + } + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(decoder->video_decode), omx_buf) != OMX_ErrorNone) + { + CHIAKI_LOGE(decoder->log, "OMX_EmptyThisBuffer failed"); + return false; + } + return true; +} + +#define OMX_INIT_STRUCTURE(a) \ + memset(&(a), 0, sizeof(a)); \ + (a).nSize = sizeof(a); \ + (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ + (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ + (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ + (a).nVersion.s.nStep = OMX_VERSION_STEP + +CHIAKI_EXPORT void chiaki_pi_decoder_set_params(ChiakiPiDecoder *decoder, int x, int y, int w, int h, bool visible) +{ + OMX_CONFIG_DISPLAYREGIONTYPE configDisplay; + OMX_INIT_STRUCTURE(configDisplay); + configDisplay.nPortIndex = 90; + configDisplay.set = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_NOASPECT | OMX_DISPLAY_SET_MODE | OMX_DISPLAY_SET_FULLSCREEN | OMX_DISPLAY_SET_PIXEL | OMX_DISPLAY_SET_DEST_RECT | OMX_DISPLAY_SET_ALPHA); + configDisplay.mode = OMX_DISPLAY_MODE_LETTERBOX; + configDisplay.fullscreen = OMX_FALSE; + configDisplay.noaspect = OMX_FALSE; + configDisplay.dest_rect.x_offset = x; + configDisplay.dest_rect.y_offset = y; + configDisplay.dest_rect.width = w; + configDisplay.dest_rect.height = h; + configDisplay.alpha = visible ? 255 : 0; + + if(OMX_SetParameter(ILC_GET_HANDLE(decoder->video_render), OMX_IndexConfigDisplayRegion, &configDisplay) != OMX_ErrorNone) + CHIAKI_LOGE(decoder->log, "OMX_SetParameter failed for display params"); +} + +CHIAKI_EXPORT bool chiaki_pi_decoder_video_sample_cb(uint8_t *buf, size_t buf_size, void *user) +{ + return push_buffer(user, buf, buf_size); +}