From c089c1340ea666a01b88b1def6b51f59f84108f0 Mon Sep 17 00:00:00 2001 From: Vladimir Sumarov Date: Thu, 26 May 2022 16:20:13 -0700 Subject: [PATCH 1/3] Add new api to put filter in exact position --- js/module.d.ts | 2 +- js/module.ts | 6 +-- obs-studio-client/source/input.cpp | 21 ++++++++++ obs-studio-client/source/input.hpp | 1 + obs-studio-server/source/osn-input.cpp | 27 +++++++++++++ obs-studio-server/source/osn-input.hpp | 5 +++ tests/osn-tests/src/test_osn_input.ts | 55 +++++++++++++++++++++++++- tests/osn-tests/util/error_messages.ts | 2 + 8 files changed, 114 insertions(+), 5 deletions(-) diff --git a/js/module.d.ts b/js/module.d.ts index 5b27b3613..c883fa437 100644 --- a/js/module.d.ts +++ b/js/module.d.ts @@ -458,7 +458,7 @@ export interface IInput extends ISource { sendFocus(focus: boolean): void; sendKeyClick(eventData: IKeyEvent, keyUp: boolean): void; setFilterOrder(filter: IFilter, movement: EOrderMovement): void; - setFilterOrder(filter: IFilter, movement: EOrderMovement): void; + setFilterPosition(filter: IFilter, position: number): void; readonly filters: IFilter[]; readonly width: number; readonly height: number; diff --git a/js/module.ts b/js/module.ts index 2609b442f..55782f635 100644 --- a/js/module.ts +++ b/js/module.ts @@ -819,11 +819,11 @@ export interface IInput extends ISource { setFilterOrder(filter: IFilter, movement: EOrderMovement): void; /** - * Move a filter up, down, top, or bottom in the filter list. + * Set a filter position in the filter list. * @param filter - The filter to move within the input source. - * @param movement - The movement to make within the list. + * @param position - The position to make within the list. */ - setFilterOrder(filter: IFilter, movement: EOrderMovement): void; + setFilterPosition(filter: IFilter, position: number): void; /** diff --git a/obs-studio-client/source/input.cpp b/obs-studio-client/source/input.cpp index 0096ca0f5..3bf09daed 100644 --- a/obs-studio-client/source/input.cpp +++ b/obs-studio-client/source/input.cpp @@ -47,6 +47,7 @@ Napi::Object osn::Input::Init(Napi::Env env, Napi::Object exports) { InstanceMethod("addFilter", &osn::Input::AddFilter), InstanceMethod("removeFilter", &osn::Input::RemoveFilter), InstanceMethod("setFilterOrder", &osn::Input::SetFilterOrder), + InstanceMethod("setFilterPosition", &osn::Input::SetFilterPosition), InstanceMethod("findFilter", &osn::Input::FindFilter), InstanceMethod("copyFilters", &osn::Input::CopyFilters), @@ -675,6 +676,26 @@ Napi::Value osn::Input::SetFilterOrder(const Napi::CallbackInfo& info) return info.Env().Undefined(); } +Napi::Value osn::Input::SetFilterPosition(const Napi::CallbackInfo& info) +{ + osn::Filter* objfilter = Napi::ObjectWrap::Unwrap(info[0].ToObject()); + uint32_t position = info[1].ToNumber().Uint32Value(); + + auto conn = GetConnection(info); + if (!conn) + return info.Env().Undefined(); + + conn->call( + "Input", "PositionFilter", {ipc::value(this->sourceId), ipc::value(objfilter->sourceId), ipc::value(position)}); + + SourceDataInfo* sdi = CacheManager::getInstance().Retrieve(this->sourceId); + if (sdi) { + sdi->filtersOrderChanged = true; + } + + return info.Env().Undefined(); +} + Napi::Value osn::Input::FindFilter(const Napi::CallbackInfo& info) { std::string name = info[0].ToString().Utf8Value(); diff --git a/obs-studio-client/source/input.hpp b/obs-studio-client/source/input.hpp index fde7aee98..42727a9a3 100644 --- a/obs-studio-client/source/input.hpp +++ b/obs-studio-client/source/input.hpp @@ -43,6 +43,7 @@ namespace osn Napi::Value AddFilter(const Napi::CallbackInfo& info); Napi::Value RemoveFilter(const Napi::CallbackInfo& info); Napi::Value SetFilterOrder(const Napi::CallbackInfo& info); + Napi::Value SetFilterPosition(const Napi::CallbackInfo& info); Napi::Value FindFilter(const Napi::CallbackInfo& info); Napi::Value CopyFilters(const Napi::CallbackInfo& info); diff --git a/obs-studio-server/source/osn-input.cpp b/obs-studio-server/source/osn-input.cpp index 7390e1069..7dfbdbb73 100644 --- a/obs-studio-server/source/osn-input.cpp +++ b/obs-studio-server/source/osn-input.cpp @@ -97,6 +97,8 @@ void osn::Input::Register(ipc::server& srv) "RemoveFilter", std::vector{ipc::type::UInt64, ipc::type::UInt64}, RemoveFilter)); cls->register_function(std::make_shared( "MoveFilter", std::vector{ipc::type::UInt64, ipc::type::UInt64, ipc::type::UInt32}, MoveFilter)); + cls->register_function(std::make_shared( + "PositionFilter", std::vector{ipc::type::UInt64, ipc::type::UInt64, ipc::type::UInt32}, PositionFilter)); cls->register_function(std::make_shared( "FindFilter", std::vector{ipc::type::UInt64, ipc::type::String}, FindFilter)); cls->register_function(std::make_shared( @@ -632,6 +634,30 @@ void osn::Input::MoveFilter( AUTO_DEBUG; } +void osn::Input::PositionFilter( + void* data, + const int64_t id, + const std::vector& args, + std::vector& rval) +{ + obs_source_t* input = osn::Source::Manager::GetInstance().find(args[0].value_union.ui64); + if (!input) { + PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Input reference is not valid."); + } + + obs_source_t* filter = osn::Source::Manager::GetInstance().find(args[1].value_union.ui64); + if (!filter) { + PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Filter reference is not valid."); + } + + size_t position = (size_t)args[2].value_union.ui32; + + obs_source_filter_set_position(input, filter, position); + + rval.push_back(ipc::value((uint64_t)ErrorCode::Ok)); + AUTO_DEBUG; +} + void osn::Input::FindFilter( void* data, const int64_t id, @@ -676,6 +702,7 @@ void osn::Input::GetFilters( std::vector* rval = reinterpret_cast*>(data); uint64_t id = osn::Source::Manager::GetInstance().find(filter); + blog(LOG_DEBUG, "osn::Input::GetFilters: %s", obs_source_get_name(filter)); if (id != UINT64_MAX) { rval->push_back(id); } diff --git a/obs-studio-server/source/osn-input.hpp b/obs-studio-server/source/osn-input.hpp index ef83787e7..482f5df6a 100644 --- a/obs-studio-server/source/osn-input.hpp +++ b/obs-studio-server/source/osn-input.hpp @@ -128,6 +128,11 @@ namespace osn const int64_t id, const std::vector& args, std::vector& rval); + static void PositionFilter( + void* data, + const int64_t id, + const std::vector& args, + std::vector& rval); static void FindFilter( void* data, const int64_t id, diff --git a/tests/osn-tests/src/test_osn_input.ts b/tests/osn-tests/src/test_osn_input.ts index f76f474eb..e77f35e6f 100644 --- a/tests/osn-tests/src/test_osn_input.ts +++ b/tests/osn-tests/src/test_osn_input.ts @@ -563,7 +563,7 @@ describe(testName, () => { }); }); - it('Change the order of filters in the list', () => { + it('Change the order of filters in the list by moving', () => { // Creating source const input = osn.InputFactory.create(EOBSInputTypes.ImageSource, 'test_source'); @@ -620,6 +620,59 @@ describe(testName, () => { input.release(); }); + it('Change the order of filters in the list by positioning', () => { + // Creating source + const input = osn.InputFactory.create(EOBSInputTypes.FFMPEGSource, 'ffmpeg_source'); + + // Checking if source was created correctly + expect(input).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateInput, EOBSInputTypes.FFMPEGSource)); + expect(input.id).to.equal(EOBSInputTypes.FFMPEGSource, GetErrorMessage(ETestErrorMsg.InputId, EOBSInputTypes.FFMPEGSource)); + expect(input.name).to.equal('ffmpeg_source', GetErrorMessage(ETestErrorMsg.InputName, EOBSInputTypes.FFMPEGSource)); + + // Creating filters + const filter1 = osn.FilterFactory.create(EOBSFilterTypes.Color, 'filter1'); + const filter2 = osn.FilterFactory.create(EOBSFilterTypes.Crop, 'filter2'); + const filter3 = osn.FilterFactory.create(EOBSFilterTypes.Gain, 'filter3'); + const filter4 = osn.FilterFactory.create(EOBSFilterTypes.GPUDelay, 'filter4'); + + // Checking if filters were created correctly + expect(filter1).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.Color)); + expect(filter2).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.Crop)); + expect(filter3).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.Gain)); + expect(filter4).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.GPUDelay)); + + // Adding filters to source + input.addFilter(filter1); + input.addFilter(filter2); + input.addFilter(filter3); + input.addFilter(filter4); + + // Checking if filters are in the right position + expect(input.filters[0].name).to.equal('filter1', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.Color)); + expect(input.filters[1].name).to.equal('filter2', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.Crop)); + expect(input.filters[2].name).to.equal('filter3', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.Gain)); + expect(input.filters[3].name).to.equal('filter4', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.GPUDelay)); + + // Changing filter order down + input.setFilterOrder(filter1, osn.EOrderMovement.Down); + // Checking if filter is in the right position + expect(input.filters[1].name).to.equal('filter1', GetErrorMessage(ETestErrorMsg.MoveFilterDown, EOBSFilterTypes.Color)); + + // Change filter position + input.setFilterPosition(filter1, 2); + + // Checking if filter is in the right position + expect(input.filters[2].name).to.equal('filter1', GetErrorMessage(ETestErrorMsg.PositionFilter, EOBSFilterTypes.Color)); + + // Removing all filters + input.filters.forEach(function(filter) { + input.removeFilter(filter); + filter.release(); + }); + + input.release(); + }); + it('Fail test - Try to find an input that does not exist', () => { let inputFromName: IInput; diff --git a/tests/osn-tests/util/error_messages.ts b/tests/osn-tests/util/error_messages.ts index 7f426fb77..c4773dd4d 100644 --- a/tests/osn-tests/util/error_messages.ts +++ b/tests/osn-tests/util/error_messages.ts @@ -86,6 +86,8 @@ export const enum ETestErrorMsg { MonitoringType = 'Failed to update monitoring type of input %VALUE1%', FindFilter = 'Did not found filter %VALUE1% in input %VALUE2%', RemoveFilter = 'Not all filters were removed', + PositionFilter = 'Failed to move filter %VALUE1% into position', + FilterInsert = 'Failed to insert a filter %VALUE1% into source', MoveFilterDown = 'Failed to move filter %VALUE1% down', MoveFilterUp = 'Failed to move filter %VALUE1% up', MoveFilterBottom = 'Failed to move filter %VALUE1% to bottom', From e93f9e51e59481e8b6e6f869525065b97a1ca0e5 Mon Sep 17 00:00:00 2001 From: Vladimir Sumarov Date: Mon, 30 May 2022 09:33:12 -0700 Subject: [PATCH 2/3] remove debug logs --- obs-studio-server/source/osn-input.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obs-studio-server/source/osn-input.cpp b/obs-studio-server/source/osn-input.cpp index 7dfbdbb73..9d2c7c3fe 100644 --- a/obs-studio-server/source/osn-input.cpp +++ b/obs-studio-server/source/osn-input.cpp @@ -702,7 +702,7 @@ void osn::Input::GetFilters( std::vector* rval = reinterpret_cast*>(data); uint64_t id = osn::Source::Manager::GetInstance().find(filter); - blog(LOG_DEBUG, "osn::Input::GetFilters: %s", obs_source_get_name(filter)); + if (id != UINT64_MAX) { rval->push_back(id); } From b171904108de170f6fb3f608b67ac78923657fd2 Mon Sep 17 00:00:00 2001 From: Vladimir Sumarov Date: Tue, 31 May 2022 16:35:43 -0700 Subject: [PATCH 3/3] separate lists for video and audio filters --- js/module.d.ts | 4 + js/module.ts | 12 +- obs-studio-client/source/cache-manager.hpp | 2 +- obs-studio-client/source/input.cpp | 127 ++++++++++++++++----- obs-studio-client/source/input.hpp | 12 ++ obs-studio-server/source/osn-input.cpp | 43 ++++++- tests/osn-tests/src/test_osn_input.ts | 48 ++++++++ 7 files changed, 217 insertions(+), 31 deletions(-) diff --git a/js/module.d.ts b/js/module.d.ts index c883fa437..32fe3b64d 100644 --- a/js/module.d.ts +++ b/js/module.d.ts @@ -459,7 +459,11 @@ export interface IInput extends ISource { sendKeyClick(eventData: IKeyEvent, keyUp: boolean): void; setFilterOrder(filter: IFilter, movement: EOrderMovement): void; setFilterPosition(filter: IFilter, position: number): void; + setVideoFilterPosition(filter: IFilter, position: number): void; + setAudioFilterPosition(filter: IFilter, position: number): void; readonly filters: IFilter[]; + readonly videoFilters: IFilter[]; + readonly audioFilters: IFilter[]; readonly width: number; readonly height: number; getDuration(): number; diff --git a/js/module.ts b/js/module.ts index 55782f635..7d70b8115 100644 --- a/js/module.ts +++ b/js/module.ts @@ -824,12 +824,22 @@ export interface IInput extends ISource { * @param position - The position to make within the list. */ setFilterPosition(filter: IFilter, position: number): void; + setVideoFilterPosition(filter: IFilter, position: number): void; + setAudioFilterPosition(filter: IFilter, position: number): void; /** * Obtain a list of all filters associated with the input source */ - readonly filters: IFilter[]; + readonly filters: IFilter[]; + /** + * Obtain a list of video filters associated with the input source + */ + readonly videoFilters: IFilter[]; + /** + * Obtain a list of audio filters associated with the input source + */ + readonly audioFilters: IFilter[]; /** * Width of the underlying source diff --git a/obs-studio-client/source/cache-manager.hpp b/obs-studio-client/source/cache-manager.hpp index 5d39965b2..f37a84f3d 100644 --- a/obs-studio-client/source/cache-manager.hpp +++ b/obs-studio-client/source/cache-manager.hpp @@ -45,7 +45,7 @@ struct SourceDataInfo uint32_t audioMixers = UINT32_MAX; bool audioMixersChanged = true; - std::vector* filters = new std::vector(); + std::vector>* filters = new std::vector>(); bool filtersOrderChanged = true; }; diff --git a/obs-studio-client/source/input.cpp b/obs-studio-client/source/input.cpp index 3bf09daed..ba57e64aa 100644 --- a/obs-studio-client/source/input.cpp +++ b/obs-studio-client/source/input.cpp @@ -48,6 +48,8 @@ Napi::Object osn::Input::Init(Napi::Env env, Napi::Object exports) { InstanceMethod("removeFilter", &osn::Input::RemoveFilter), InstanceMethod("setFilterOrder", &osn::Input::SetFilterOrder), InstanceMethod("setFilterPosition", &osn::Input::SetFilterPosition), + InstanceMethod("setVideoFilterPosition", &osn::Input::SetVideoFilterPosition), + InstanceMethod("setAudioFilterPosition", &osn::Input::SetAudioFilterPosition), InstanceMethod("findFilter", &osn::Input::FindFilter), InstanceMethod("copyFilters", &osn::Input::CopyFilters), @@ -62,6 +64,8 @@ Napi::Object osn::Input::Init(Napi::Env env, Napi::Object exports) { InstanceAccessor("deinterlaceFieldOrder", &osn::Input::GetDeinterlaceFieldOrder, &osn::Input::SetDeinterlaceFieldOrder), InstanceAccessor("deinterlaceMode", &osn::Input::GetDeinterlaceMode, &osn::Input::SetDeinterlaceMode), InstanceAccessor("filters", &osn::Input::Filters, nullptr), + InstanceAccessor("videoFilters", &osn::Input::VideoFilters, nullptr), + InstanceAccessor("audioFilters", &osn::Input::AudioFilters, nullptr), InstanceAccessor("seek", &osn::Input::GetTime, &osn::Input::SetTime), InstanceAccessor("configurable", &osn::Input::CallIsConfigurable, nullptr), @@ -573,20 +577,44 @@ void osn::Input::SetDeinterlaceMode(const Napi::CallbackInfo& info, const Napi:: } Napi::Value osn::Input::Filters(const Napi::CallbackInfo& info) +{ + return GetFilters(info, osn::FilterSubset::All); +} + +Napi::Value osn::Input::AudioFilters(const Napi::CallbackInfo& info) +{ + return GetFilters(info, osn::FilterSubset::Audio); +} + +Napi::Value osn::Input::VideoFilters(const Napi::CallbackInfo& info) +{ + return GetFilters(info, osn::FilterSubset::Video); +} + +Napi::Value osn::Input::FiltersFromCache(const Napi::CallbackInfo& info, osn::FilterSubset subset, std::vector> * filters) +{ + Napi::Array array = Napi::Array::New(info.Env()); + int index = 0; + for (uint32_t i = 0; i < filters->size(); i++) { + if ((filters->at(i).second == static_cast(osn::FilterSubset::Video) && subset == osn::FilterSubset::Audio) + || (filters->at(i).second == static_cast(osn::FilterSubset::Audio) && subset == osn::FilterSubset::Video)) + continue; + auto instance = + osn::Filter::constructor.New({ + Napi::Number::New(info.Env(), filters->at(i).first) + }); + array.Set(index, instance); + index++; + } + return array; +} + +Napi::Value osn::Input::GetFilters(const Napi::CallbackInfo& info, osn::FilterSubset subset) { SourceDataInfo* sdi = CacheManager::getInstance().Retrieve(this->sourceId); if (sdi && !sdi->filtersOrderChanged) { - std::vector* filters = sdi->filters; - Napi::Array array = Napi::Array::New(info.Env(), int(filters->size())); - for (uint32_t i = 0; i < filters->size(); i++) { - auto instance = - osn::Filter::constructor.New({ - Napi::Number::New(info.Env(), filters->at(i)) - }); - array.Set(i, instance); - } - return array; + return FiltersFromCache(info, subset, sdi->filters); } auto conn = GetConnection(info); @@ -599,28 +627,35 @@ Napi::Value osn::Input::Filters(const Napi::CallbackInfo& info) if (!ValidateResponse(info, response)) return info.Env().Undefined(); - std::vector* filters; + Napi::Array array = Napi::Array::New(info.Env()); + std::vector>* filters; if (sdi) { filters = sdi->filters; filters->clear(); - } - Napi::Array array = Napi::Array::New(info.Env(), response.size() - 1); - for (size_t idx = 1; idx < response.size(); idx++) { - auto instance = - osn::Filter::constructor.New({ - Napi::Number::New(info.Env(), response[idx].value_union.ui64) - }); - array.Set(uint32_t(idx) - 1, instance); - - if (sdi) - filters->push_back(response[idx].value_union.ui64); - } + for (size_t idx = 1; idx < response.size(); idx += 2) { + filters->push_back(std::make_pair(response[idx].value_union.ui64, response[idx+1].value_union.i32)); + } - if (sdi) sdi->filtersOrderChanged = false; - return array; + return FiltersFromCache(info, subset, filters); + } else { + int index = 0; + for (size_t idx = 1; idx < response.size(); idx += 2) { + if ((response[idx+1].value_union.i32 == static_cast(osn::FilterSubset::Video) && subset == osn::FilterSubset::Audio) + || (response[idx+1].value_union.i32 == static_cast(osn::FilterSubset::Audio) && subset == osn::FilterSubset::Video)) + continue; + + auto instance = + osn::Filter::constructor.New({ + Napi::Number::New(info.Env(), response[idx].value_union.ui64) + }); + array.Set(index, instance); + index++; + } + return array; + } } Napi::Value osn::Input::AddFilter(const Napi::CallbackInfo& info) @@ -686,7 +721,47 @@ Napi::Value osn::Input::SetFilterPosition(const Napi::CallbackInfo& info) return info.Env().Undefined(); conn->call( - "Input", "PositionFilter", {ipc::value(this->sourceId), ipc::value(objfilter->sourceId), ipc::value(position)}); + "Input", "PositionFilter", {ipc::value(this->sourceId), ipc::value(objfilter->sourceId), ipc::value(position), ipc::value(0)}); + + SourceDataInfo* sdi = CacheManager::getInstance().Retrieve(this->sourceId); + if (sdi) { + sdi->filtersOrderChanged = true; + } + + return info.Env().Undefined(); +} + +Napi::Value osn::Input::SetVideoFilterPosition(const Napi::CallbackInfo& info) +{ + osn::Filter* objfilter = Napi::ObjectWrap::Unwrap(info[0].ToObject()); + uint32_t position = info[1].ToNumber().Uint32Value(); + + auto conn = GetConnection(info); + if (!conn) + return info.Env().Undefined(); + + conn->call( + "Input", "PositionFilter", {ipc::value(this->sourceId), ipc::value(objfilter->sourceId), ipc::value(position), ipc::value(static_cast(osn::FilterSubset::Video))}); + + SourceDataInfo* sdi = CacheManager::getInstance().Retrieve(this->sourceId); + if (sdi) { + sdi->filtersOrderChanged = true; + } + + return info.Env().Undefined(); +} + +Napi::Value osn::Input::SetAudioFilterPosition(const Napi::CallbackInfo& info) +{ + osn::Filter* objfilter = Napi::ObjectWrap::Unwrap(info[0].ToObject()); + uint32_t position = info[1].ToNumber().Uint32Value(); + + auto conn = GetConnection(info); + if (!conn) + return info.Env().Undefined(); + + conn->call( + "Input", "PositionFilter", {ipc::value(this->sourceId), ipc::value(objfilter->sourceId), ipc::value(position), ipc::value(static_cast(osn::FilterSubset::Video))}); SourceDataInfo* sdi = CacheManager::getInstance().Retrieve(this->sourceId); if (sdi) { diff --git a/obs-studio-client/source/input.hpp b/obs-studio-client/source/input.hpp index 42727a9a3..b8ab00114 100644 --- a/obs-studio-client/source/input.hpp +++ b/obs-studio-client/source/input.hpp @@ -23,6 +23,12 @@ namespace osn { + enum class FilterSubset : std::int16_t + { + All =0, + Audio = 2, + Video = 1 + }; class Input : public Napi::ObjectWrap { public: @@ -44,6 +50,8 @@ namespace osn Napi::Value RemoveFilter(const Napi::CallbackInfo& info); Napi::Value SetFilterOrder(const Napi::CallbackInfo& info); Napi::Value SetFilterPosition(const Napi::CallbackInfo& info); + Napi::Value SetVideoFilterPosition(const Napi::CallbackInfo& info); + Napi::Value SetAudioFilterPosition(const Napi::CallbackInfo& info); Napi::Value FindFilter(const Napi::CallbackInfo& info); Napi::Value CopyFilters(const Napi::CallbackInfo& info); @@ -64,6 +72,10 @@ namespace osn Napi::Value GetDeinterlaceMode(const Napi::CallbackInfo& info); void SetDeinterlaceMode(const Napi::CallbackInfo& info, const Napi::Value &value); Napi::Value Filters(const Napi::CallbackInfo& info); + Napi::Value VideoFilters(const Napi::CallbackInfo& info); + Napi::Value AudioFilters(const Napi::CallbackInfo& info); + Napi::Value GetFilters(const Napi::CallbackInfo& info, osn::FilterSubset subset); + Napi::Value FiltersFromCache(const Napi::CallbackInfo& info, osn::FilterSubset subset, std::vector> * filters); Napi::Value CallIsConfigurable(const Napi::CallbackInfo& info); Napi::Value CallGetProperties(const Napi::CallbackInfo& info); diff --git a/obs-studio-server/source/osn-input.cpp b/obs-studio-server/source/osn-input.cpp index 9d2c7c3fe..c7bdd47ca 100644 --- a/obs-studio-server/source/osn-input.cpp +++ b/obs-studio-server/source/osn-input.cpp @@ -90,7 +90,7 @@ void osn::Input::Register(ipc::server& srv) "SetDeInterlaceMode", std::vector{ipc::type::UInt64, ipc::type::Int32}, GetDeInterlaceMode)); cls->register_function( - std::make_shared("GetFilters", std::vector{ipc::type::UInt64}, GetFilters)); + std::make_shared("GetFilters", std::vector{ipc::type::UInt64, ipc::type::Int32}, GetFilters)); cls->register_function(std::make_shared( "AddFilter", std::vector{ipc::type::UInt64, ipc::type::UInt64}, AddFilter)); cls->register_function(std::make_shared( @@ -98,7 +98,7 @@ void osn::Input::Register(ipc::server& srv) cls->register_function(std::make_shared( "MoveFilter", std::vector{ipc::type::UInt64, ipc::type::UInt64, ipc::type::UInt32}, MoveFilter)); cls->register_function(std::make_shared( - "PositionFilter", std::vector{ipc::type::UInt64, ipc::type::UInt64, ipc::type::UInt32}, PositionFilter)); + "PositionFilter", std::vector{ipc::type::UInt64, ipc::type::UInt64, ipc::type::UInt32, ipc::type::UInt32}, PositionFilter)); cls->register_function(std::make_shared( "FindFilter", std::vector{ipc::type::UInt64, ipc::type::String}, FindFilter)); cls->register_function(std::make_shared( @@ -634,6 +634,38 @@ void osn::Input::MoveFilter( AUTO_DEBUG; } +size_t FilterPositionInFullList(obs_source_t* input, size_t position, int subset) +{ + struct filter_position_info { + size_t count; + size_t position; + size_t subset_count; + size_t full_position; + int subset; + } info = {0, position, 0, position, subset}; + + auto enum_cb = [](obs_source_t* parent, obs_source_t* filter, void* data) { + filter_position_info* info = reinterpret_cast(data); + uint32_t output_flags = obs_source_get_output_flags(filter); + info->count++; + if(info->subset == OBS_SOURCE_VIDEO && (output_flags & OBS_SOURCE_VIDEO)) { + info->subset_count++; + if( info->subset_count == info->position) { + info->full_position = info->count; + } + } + if(info->subset == OBS_SOURCE_AUDIO && (output_flags & OBS_SOURCE_AUDIO)) { + info->subset_count++; + if( info->subset_count == info->position) { + info->full_position = info->count; + } + } + }; + + obs_source_enum_filters(input, enum_cb, &info); + return info.full_position; +} + void osn::Input::PositionFilter( void* data, const int64_t id, @@ -651,6 +683,10 @@ void osn::Input::PositionFilter( } size_t position = (size_t)args[2].value_union.ui32; + int subset = args[3].value_union.ui32; + + if (subset != 0) + position = FilterPositionInFullList(input, position, subset); obs_source_filter_set_position(input, filter, position); @@ -700,11 +736,12 @@ void osn::Input::GetFilters( auto enum_cb = [](obs_source_t* parent, obs_source_t* filter, void* data) { std::vector* rval = reinterpret_cast*>(data); - + uint32_t output_flags = obs_source_get_output_flags(filter); uint64_t id = osn::Source::Manager::GetInstance().find(filter); if (id != UINT64_MAX) { rval->push_back(id); + rval->push_back(output_flags & (OBS_SOURCE_AUDIO|OBS_SOURCE_VIDEO)); } }; diff --git a/tests/osn-tests/src/test_osn_input.ts b/tests/osn-tests/src/test_osn_input.ts index e77f35e6f..e076e8348 100644 --- a/tests/osn-tests/src/test_osn_input.ts +++ b/tests/osn-tests/src/test_osn_input.ts @@ -673,6 +673,54 @@ describe(testName, () => { input.release(); }); + it('Use separate lists for audio and video filters', () => { + // Creating source + const input = osn.InputFactory.create(EOBSInputTypes.FFMPEGSource, 'ffmpeg_source'); + + // Checking if source was created correctly + expect(input).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateInput, EOBSInputTypes.FFMPEGSource)); + expect(input.id).to.equal(EOBSInputTypes.FFMPEGSource, GetErrorMessage(ETestErrorMsg.InputId, EOBSInputTypes.FFMPEGSource)); + expect(input.name).to.equal('ffmpeg_source', GetErrorMessage(ETestErrorMsg.InputName, EOBSInputTypes.FFMPEGSource)); + + // Creating filters + const filter1 = osn.FilterFactory.create(EOBSFilterTypes.Color, 'filter1'); + const filter2 = osn.FilterFactory.create(EOBSFilterTypes.Crop, 'filter2'); + const filter3 = osn.FilterFactory.create(EOBSFilterTypes.Gain, 'filter3'); + const filter4 = osn.FilterFactory.create(EOBSFilterTypes.GPUDelay, 'filter4'); + const filter5 = osn.FilterFactory.create(EOBSFilterTypes.Compressor, 'filter5'); + + // Checking if filters were created correctly + expect(filter1).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.Color)); + expect(filter2).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.Crop)); + expect(filter3).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.Gain)); + expect(filter4).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.GPUDelay)); + expect(filter5).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateFilter, EOBSFilterTypes.Compressor)); + + // Adding filters to source + input.addFilter(filter1); + input.addFilter(filter2); + input.addFilter(filter3); + input.addFilter(filter4); + + // Checking if filters are in the right position + expect(input.videoFilters[0].name).to.equal('filter1', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.Color)); + expect(input.videoFilters[1].name).to.equal('filter2', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.Crop)); + expect(input.videoFilters[2].name).to.equal('filter4', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.GPUDelay)); + expect(input.audioFilters[0].name).to.equal('filter3', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.Gain)); + + // Adding more filters + input.addFilter(filter5); + expect(input.audioFilters[1].name).to.equal('filter5', GetErrorMessage(ETestErrorMsg.FilterInsert, EOBSFilterTypes.Compressor)); + + // Removing all filters + input.filters.forEach(function(filter) { + input.removeFilter(filter); + filter.release(); + }); + + input.release(); + }); + it('Fail test - Try to find an input that does not exist', () => { let inputFromName: IInput;