From 88801511a1276e4d284b0fe32be9ec8c5855f06a Mon Sep 17 00:00:00 2001 From: Gerasim Troeglazov <3dEyes@gmail.com> Date: Sun, 1 Sep 2019 22:52:49 +1000 Subject: Add haiku support diff --git a/CMakeLists.txt b/CMakeLists.txt index f6710ef..11d985a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") elseif(ANDROID) set(DEFAULT_ALSA OFF) set(DEFAULT_PORTAUDIO OFF) +elseif(HAIKU) + set(DEFAULT_ALSA OFF) + set(DEFAULT_PORTAUDIO OFF) else() set(DEFAULT_ALSA OFF) set(DEFAULT_PORTAUDIO ON) @@ -55,7 +58,7 @@ if(WIN32) add_feature_info(CMD USE_CMD "Show CMD when running QMPlay2") endif() -if(NOT WIN32 AND NOT APPLE AND NOT ANDROID) +if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) option(USE_FREEDESKTOP_NOTIFICATIONS "Use Freedesktop notifications" ON) add_feature_info("Freedesktop notifications" USE_FREEDESKTOP_NOTIFICATIONS "Use Freedesktop notifications") endif() @@ -84,7 +87,7 @@ add_feature_info(Modplug USE_MODPLUG "Build with Modplug module") option(USE_EXTENSIONS "Build with Extensions module" ON) add_feature_info(Extensions USE_EXTENSIONS "Build with Extensions module") -if(USE_EXTENSIONS AND NOT WIN32 AND NOT APPLE AND NOT ANDROID) +if(USE_EXTENSIONS AND NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) option(USE_MPRIS2 "Build Extensions with MPRIS2 support" ON) add_feature_info(MPRIS2 USE_MPRIS2 "Build Extensions with MPRIS2 support") endif() diff --git a/src/gui/Main.cpp b/src/gui/Main.cpp index 9c64652..cbe7316 100644 --- a/src/gui/Main.cpp +++ b/src/gui/Main.cpp @@ -599,6 +599,10 @@ int main(int argc, char *argv[]) checkForEGL(); #endif +#ifdef Q_OS_HAIKU + setenv("HOME", "/boot/home", 1); +#endif + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #ifndef Q_OS_WIN QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts); @@ -684,7 +688,7 @@ int main(int argc, char *argv[]) } if (!cmakeBuildFound) { -#if !defined Q_OS_WIN && !defined Q_OS_MACOS && !defined Q_OS_ANDROID +#if !defined Q_OS_WIN && !defined Q_OS_MACOS && !defined Q_OS_ANDROID && !defined Q_OS_HAIKU sharePath = QCoreApplication::applicationDirPath() + "/../share/qmplay2"; libPath = QMPlay2CoreClass::getLibDir(); if (libPath.isEmpty() || !QDir(libPath).exists("qmplay2")) @@ -902,5 +906,9 @@ int main(int argc, char *argv[]) if (canDeleteApp) #endif delete qApp; + +#ifdef Q_OS_HAIKU + kill(::getpid(), SIGKILL); +#endif return 0; } diff --git a/src/gui/MainWidget.cpp b/src/gui/MainWidget.cpp index 139dd2a..c9804c5 100644 --- a/src/gui/MainWidget.cpp +++ b/src/gui/MainWidget.cpp @@ -145,7 +145,7 @@ MainWidget::MainWidget(QList> &arguments) : setIconSize({22, 22}); SettingsWidget::InitSettings(); -#ifndef Q_OS_ANDROID +#if !defined Q_OS_ANDROID && !defined Q_OS_HAIKU settings.init("MainWidget/WidgetsLocked", false); #else settings.init("MainWidget/WidgetsLocked", true); diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 449882a..17b3da6 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -7,6 +7,9 @@ if(WIN32) elseif(APPLE) set(MODULES_INSTALL_PATH "${CMAKE_INSTALL_LIBDIR}/modules") set(QMPLAY2_MODULE SHARED) # otherwise CMake uses ".so" extension +elseif(HAIKU) + set(MODULES_INSTALL_PATH "modules") + set(QMPLAY2_MODULE MODULE) else() set(MODULES_INSTALL_PATH "${CMAKE_INSTALL_LIBDIR}/qmplay2/modules") set(QMPLAY2_MODULE MODULE) @@ -60,6 +63,10 @@ if(USE_PULSEAUDIO) add_subdirectory(PulseAudio) endif() +if(HAIKU) + add_subdirectory(MediaKit) +endif() + if(USE_XVIDEO) add_subdirectory(XVideo) endif() diff --git a/src/modules/MediaKit/CMakeLists.txt b/src/modules/MediaKit/CMakeLists.txt new file mode 100644 index 0000000..f94e365 --- /dev/null +++ b/src/modules/MediaKit/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.1) +project(MediaKit) + +set(MediaKit_HDR + MediaKit.hpp + MediaKitWriter.hpp + RingBuffer.hpp + SndPlayer.hpp +) + +set(MediaKit_SRC + MediaKit.cpp + MediaKitWriter.cpp + RingBuffer.cpp + SndPlayer.cpp +) + +set(MediaKit_RESOURCES + icon.qrc +) + +include_directories(../../qmplay2/headers) + +add_library(${PROJECT_NAME} ${QMPLAY2_MODULE} + ${MediaKit_HDR} + ${MediaKit_SRC} + ${MediaKit_RESOURCES} +) + +target_link_libraries(${PROJECT_NAME} + be + media + libqmplay2 +) + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) diff --git a/src/modules/MediaKit/MediaKit.cpp b/src/modules/MediaKit/MediaKit.cpp new file mode 100644 index 0000000..cc9998b --- /dev/null +++ b/src/modules/MediaKit/MediaKit.cpp @@ -0,0 +1,65 @@ +#include +#include + +MediaKit::MediaKit() : + Module( "MediaKit" ) +{ + m_icon = QIcon( ":/MediaKit" ); + + init( "WriterEnabled", true ); + init( "Delay", 0.2 ); +} + +QList< MediaKit::Info > MediaKit::getModulesInfo( const bool showDisabled ) const +{ + QList< Info > modulesInfo; + if ( showDisabled || getBool( "WriterEnabled" ) ) + modulesInfo += Info( MediaKitWriterName, WRITER, QStringList( "audio" ) ); + return modulesInfo; +} +void *MediaKit::createInstance( const QString &name ) +{ + if ( name == MediaKitWriterName && getBool( "WriterEnabled" ) ) + return new MediaKitWriter( *this ); + return NULL; +} + +MediaKit::SettingsWidget *MediaKit::getSettingsWidget() +{ + return new ModuleSettingsWidget( *this ); +} + +QMPLAY2_EXPORT_MODULE( MediaKit ) + +/**/ + +#include +#include +#include +#include + +ModuleSettingsWidget::ModuleSettingsWidget( Module &module ) : + Module::SettingsWidget( module ) +{ + enabledB = new QCheckBox( tr( "Enabled" ) ); + enabledB->setChecked( sets().getBool( "WriterEnabled" ) ); + + QLabel *delayL = new QLabel( tr( "Delay" ) + ": " ); + + delayB = new QDoubleSpinBox; + delayB->setRange( 0.01, 1.0 ); + delayB->setSingleStep( 0.01 ); + delayB->setSuffix( " " + tr( "sec" ) ); + delayB->setValue( sets().getDouble( "Delay" ) ); + + QGridLayout *layout = new QGridLayout( this ); + layout->addWidget( enabledB, 0, 0, 1, 2 ); + layout->addWidget( delayL, 1, 0, 1, 1 ); + layout->addWidget( delayB, 1, 1, 1, 1 ); +} + +void ModuleSettingsWidget::saveSettings() +{ + sets().set( "WriterEnabled", enabledB->isChecked() ); + sets().set( "Delay", delayB->value() ); +} diff --git a/src/modules/MediaKit/MediaKit.hpp b/src/modules/MediaKit/MediaKit.hpp new file mode 100644 index 0000000..ed89e32 --- /dev/null +++ b/src/modules/MediaKit/MediaKit.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +class MediaKit : public Module +{ +public: + MediaKit(); +private: + QList< Info > getModulesInfo( const bool ) const; + void *createInstance( const QString & ); + + SettingsWidget *getSettingsWidget(); +}; + +/**/ + +#include + +class QDoubleSpinBox; +class QCheckBox; + +class ModuleSettingsWidget : public Module::SettingsWidget +{ + Q_DECLARE_TR_FUNCTIONS( ModuleSettingsWidget ) +public: + ModuleSettingsWidget( Module & ); +private: + void saveSettings(); + + QCheckBox *enabledB; + QDoubleSpinBox *delayB; +}; diff --git a/src/modules/MediaKit/MediaKitWriter.cpp b/src/modules/MediaKit/MediaKitWriter.cpp new file mode 100644 index 0000000..a86e584 --- /dev/null +++ b/src/modules/MediaKit/MediaKitWriter.cpp @@ -0,0 +1,85 @@ +#include +#include + +MediaKitWriter::MediaKitWriter( Module &module ) : + err( false ) +{ + addParam( "delay" ); + addParam( "chn" ); + addParam( "rate" ); + + SetModule( module ); +} + +bool MediaKitWriter::set() +{ + if ( player.delay != sets().getDouble( "Delay" ) ) + { + player.delay = sets().getDouble( "Delay" ); + return false; + } + return sets().getBool( "WriterEnabled" ); +} + +bool MediaKitWriter::readyWrite() const +{ + return !err && player.isOpen(); +} + +bool MediaKitWriter::processParams( bool * ) +{ + bool resetAudio = false; + + uchar chn = getParam( "chn" ).toUInt(); + if ( player.channels != chn ) + { + resetAudio = true; + player.channels = chn; + } + uint rate = getParam( "rate" ).toUInt(); + if ( player.sample_rate != rate ) + { + resetAudio = true; + player.sample_rate = rate; + } + + if ( resetAudio || err ) + { + player.stop(); + err = !player.start(); + if ( !err ) + modParam( "delay", player.delay ); + else + QMPlay2Core.logError( "MediaKitWriter :: " + tr ( "Cannot open audio output stream" ) ); + } + + return readyWrite(); +} +qint64 MediaKitWriter::write( const QByteArray &arr ) +{ + if ( !arr.size() || !readyWrite() ) + return 0; + + err = !player.write( arr ); + if ( err ) + { + QMPlay2Core.logError( "MediaKitWriter :: " + tr ( "Playback error" ) ); + return 0; + } + + return arr.size(); +} + +qint64 MediaKitWriter::size() const +{ + return -1; +} +QString MediaKitWriter::name() const +{ + return MediaKitWriterName; +} + +bool MediaKitWriter::open() +{ + return player.isOK(); +} diff --git a/src/modules/MediaKit/MediaKitWriter.hpp b/src/modules/MediaKit/MediaKitWriter.hpp new file mode 100644 index 0000000..28fa249 --- /dev/null +++ b/src/modules/MediaKit/MediaKitWriter.hpp @@ -0,0 +1,30 @@ +#include +#include + +#include + +class MediaKitWriter : public Writer +{ + Q_DECLARE_TR_FUNCTIONS( MediaKitWriter ) +public: + MediaKitWriter( Module & ); +private: + bool set(); + + bool readyWrite() const; + + bool processParams( bool *paramsCorrected ); + qint64 write( const QByteArray & ); + + qint64 size() const; + QString name() const; + + bool open(); + + /**/ + + SndPlayer player; + bool err; +}; + +#define MediaKitWriterName "MediaKit Writer" diff --git a/src/modules/MediaKit/RingBuffer.cpp b/src/modules/MediaKit/RingBuffer.cpp new file mode 100644 index 0000000..915becc --- /dev/null +++ b/src/modules/MediaKit/RingBuffer.cpp @@ -0,0 +1,129 @@ +#include +#include +#include + +#include "RingBuffer.hpp" + +RingBuffer::RingBuffer( int size ) +{ + initialized = false; + Buffer = new unsigned char[size]; + if(Buffer!=NULL) { + memset( Buffer, 0, size ); + BufferSize = size; + } else { + BufferSize = 0; + } + reader = 0; + writer = 0; + writeBytesAvailable = size; + if((locker=create_sem(1,"locker")) >= B_OK) { + initialized = true; + } else { + if(Buffer!=NULL) { + delete[] Buffer; + } + } +} + +RingBuffer::~RingBuffer( ) +{ + if(initialized) { + delete[] Buffer; + delete_sem(locker); + } +} + +bool +RingBuffer::Empty( void ) +{ + memset( Buffer, 0, BufferSize ); + reader = 0; + writer = 0; + writeBytesAvailable = BufferSize; + return true; +} + +int +RingBuffer::Read( unsigned char *data, int size ) +{ + acquire_sem(locker); + + if( data == 0 || size <= 0 || writeBytesAvailable == BufferSize ) { + release_sem(locker); + return 0; + } + + int readBytesAvailable = BufferSize - writeBytesAvailable; + + if( size > readBytesAvailable ) { + size = readBytesAvailable; + } + + if(size > BufferSize - reader) { + int len = BufferSize - reader; + memcpy(data, Buffer + reader, len); + memcpy(data + len, Buffer, size-len); + } else { + memcpy(data, Buffer + reader, size); + } + + reader = (reader + size) % BufferSize; + writeBytesAvailable += size; + + release_sem(locker); + return size; +} + +int +RingBuffer::Write( unsigned char *data, int size ) +{ + acquire_sem(locker); + + if( data == 0 || size <= 0 || writeBytesAvailable == 0 ) { + release_sem(locker); + return 0; + } + + if( size > writeBytesAvailable ) { + size = writeBytesAvailable; + } + + if(size > BufferSize - writer) { + int len = BufferSize - writer; + memcpy(Buffer + writer, data, len); + memcpy(Buffer, data+len, size-len); + } else { + memcpy(Buffer + writer, data, size); + } + + writer = (writer + size) % BufferSize; + writeBytesAvailable -= size; + + release_sem(locker); + return size; +} + +int +RingBuffer::GetSize( void ) +{ + return BufferSize; +} + +int +RingBuffer::GetWriteAvailable( void ) +{ + return writeBytesAvailable; +} + +int +RingBuffer::GetReadAvailable( void ) +{ + return BufferSize - writeBytesAvailable; +} + +status_t +RingBuffer::InitCheck( void ) +{ + return initialized?B_OK:B_ERROR; +} diff --git a/src/modules/MediaKit/RingBuffer.hpp b/src/modules/MediaKit/RingBuffer.hpp new file mode 100644 index 0000000..4715632 --- /dev/null +++ b/src/modules/MediaKit/RingBuffer.hpp @@ -0,0 +1,31 @@ +#ifndef __RING_BUFFER_H__ +#define __RING_BUFFER_H__ + +#include + +class RingBuffer { + +public: + RingBuffer(int size); + ~RingBuffer(); + int Read( unsigned char* dataPtr, int numBytes ); + int Write( unsigned char *dataPtr, int numBytes ); + + bool Empty( void ); + int GetSize( ); + int GetWriteAvailable( ); + int GetReadAvailable( ); + status_t InitCheck( ); +private: + unsigned char *Buffer; + int BufferSize; + int reader; + int writer; + int writeBytesAvailable; + + sem_id locker; + + bool initialized; +}; + +#endif diff --git a/src/modules/MediaKit/SndPlayer.cpp b/src/modules/MediaKit/SndPlayer.cpp new file mode 100644 index 0000000..ca7ad89 --- /dev/null +++ b/src/modules/MediaKit/SndPlayer.cpp @@ -0,0 +1,103 @@ +#include + +#include +#include +#include +#include + + +static void proc(void *cookie, void *buffer, size_t len, const media_raw_audio_format &format) +{ + RingBuffer *ring = (RingBuffer*)cookie; + unsigned char* ptr = (unsigned char*)buffer; + + int readed = ring->Read(ptr,len); + + if(readed InitCheck() != B_OK) { + delete ring; ring = 0; + return false; + } + + player = new BSoundPlayer(&form, "QMPlay2_BSoundPlayer", proc, NULL, (void*)ring); + + if(player->InitCheck() != B_OK) { + delete player; + player = NULL; + return false; + } + + player->Start(); + player->SetHasData(true); + + _isOK = true; + + return player; +} +void SndPlayer::stop() +{ + if ( player ) + { + if(player) { + player->Stop(); + delete player; + delete ring; + } + + player = NULL; + ring = NULL; + } +} + +double SndPlayer::getLatency() +{ + double lat = player->Latency() / (ring->GetSize()*4.0); + + return lat; +} + +bool SndPlayer::write( const QByteArray &arr ) +{ + int s = arr.size(); + while ( s > 0 && s % 4 ) + s--; + if ( s <= 0 ) + return false; + + int len=s; + + unsigned char *src_ptr = (unsigned char *)arr.data(); + + for(;;) { + int len2 = ring->Write(src_ptr,len); + if(len2 == len)break; + len -= len2; + src_ptr += len2; + snooze(100); + } + + return true; +} diff --git a/src/modules/MediaKit/SndPlayer.hpp b/src/modules/MediaKit/SndPlayer.hpp new file mode 100644 index 0000000..b0ca8c2 --- /dev/null +++ b/src/modules/MediaKit/SndPlayer.hpp @@ -0,0 +1,49 @@ +#ifndef PULSE_HPP +#define PULSE_HPP + +#include + +#include + +#include +#include +#include + +#include "RingBuffer.hpp" + +class SndPlayer +{ +public: + SndPlayer(); + inline ~SndPlayer() + { + stop(); + } + + inline bool isOK() const + { + return _isOK; + } + inline bool isOpen() const + { + return player; + } + + bool start(); + void stop(); + + double getLatency(); + + bool write( const QByteArray & ); + + double delay; + uchar channels; + uint sample_rate; + +private: + bool _isOK; + BSoundPlayer *player; + RingBuffer *ring; +}; + +#endif diff --git a/src/modules/MediaKit/icon.qrc b/src/modules/MediaKit/icon.qrc new file mode 100644 index 0000000..24b4ebd --- /dev/null +++ b/src/modules/MediaKit/icon.qrc @@ -0,0 +1,3 @@ + + MediaKit.png + diff --git a/src/qmplay2/QMPlay2Core.cpp b/src/qmplay2/QMPlay2Core.cpp index 55f775d..044ea1f 100644 --- a/src/qmplay2/QMPlay2Core.cpp +++ b/src/qmplay2/QMPlay2Core.cpp @@ -201,7 +201,7 @@ void QMPlay2CoreClass::init(bool loadModules, bool modulesInSubdirs, const QStri settingsDir = QCoreApplication::applicationDirPath() + "/settings/"; else { -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) || defined(Q_OS_HAIKU) settingsDir = QFileInfo(QSettings(QSettings::IniFormat, QSettings::UserScope, QString()).fileName()).absolutePath() + "/QMPlay2/"; #elif defined(Q_OS_MACOS) settingsDir = Functions::cleanPath(QStandardPaths::standardLocations(QStandardPaths::DataLocation).value(0, settingsDir)); @@ -392,6 +392,11 @@ QStringList QMPlay2CoreClass::getModules(const QString &type, int typeLen) const #elif defined Q_OS_WIN if (type == "videoWriters") defaultModules << "OpenGL 2" << "DirectDraw"; +#elif defined Q_OS_HAIKU + if ( type == "videoWriters" ) + defaultModules << "QPainter"; + else if ( type == "audioWriters" ) + defaultModules << "MediaKit"; #endif if (type == "decoders") defaultModules << "FFmpeg Decoder"; -- 2.23.0