From f67d8488a565e045025095f0002e377775defa43 Mon Sep 17 00:00:00 2001 From: tshino Date: Tue, 7 Mar 2023 22:19:09 +0900 Subject: [PATCH 01/14] Test default image feature with minimum impl --- src/softcamcore/DShowSoftcam.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/softcamcore/DShowSoftcam.cpp b/src/softcamcore/DShowSoftcam.cpp index 30315d3..eaf68f7 100644 --- a/src/softcamcore/DShowSoftcam.cpp +++ b/src/softcamcore/DShowSoftcam.cpp @@ -147,6 +147,11 @@ AM_MEDIA_TYPE* makeMediaType(int width, int height, float framerate) return amt; } +const bool HasDefaultImage = true; // TODO: Check if default image is available or not +const int DefaultImageWidth = 320; // TODO: Read the default image when it's necessary +const int DefaultImageHeight = 240; // TODO: Read the default image when it's necessary +const float DefaultFramerate = 60.0f; // TODO: Read the default image when it's necessary + } //namespace namespace softcam { @@ -166,10 +171,10 @@ CUnknown * Softcam::CreateInstance( Softcam::Softcam(LPUNKNOWN lpunk, const GUID& clsid, HRESULT *phr) : CSource(NAME("DirectShow Softcam"), lpunk, clsid), m_frame_buffer(FrameBuffer::open()), - m_valid(m_frame_buffer ? true : false), - m_width(m_frame_buffer.width()), - m_height(m_frame_buffer.height()), - m_framerate(m_frame_buffer.framerate()) + m_valid(m_frame_buffer || HasDefaultImage), + m_width(m_frame_buffer ? m_frame_buffer.width() : DefaultImageWidth), + m_height(m_frame_buffer ? m_frame_buffer.height() : DefaultImageHeight), + m_framerate(m_frame_buffer ? m_frame_buffer.framerate() : DefaultFramerate) { CAutoLock lock(&m_cStateLock); @@ -446,7 +451,15 @@ HRESULT SoftcamStream::FillBuffer(IMediaSample *pms) Timer::sleep(0.100f); const std::size_t size = calcDIBSize(m_width, m_height); - std::memcpy(pData, m_screenshot.get(), size); + if (m_screenshot) + { + std::memcpy(pData, m_screenshot.get(), size); + } + else + { + // TODO: Write the default image + std::memset(pData, 77, size); + } } CAutoLock lock(&m_critsec); From 3b23ebed0a8cd640317b933df6947b650e78be38 Mon Sep 17 00:00:00 2001 From: tshino Date: Tue, 7 Mar 2023 22:50:23 +0900 Subject: [PATCH 02/14] Fix existing tests --- src/softcamcore/DShowSoftcam.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/softcamcore/DShowSoftcam.cpp b/src/softcamcore/DShowSoftcam.cpp index eaf68f7..7ea5011 100644 --- a/src/softcamcore/DShowSoftcam.cpp +++ b/src/softcamcore/DShowSoftcam.cpp @@ -147,7 +147,7 @@ AM_MEDIA_TYPE* makeMediaType(int width, int height, float framerate) return amt; } -const bool HasDefaultImage = true; // TODO: Check if default image is available or not +const bool HasDefaultImage = false; //true; // TODO: Check if default image is available or not const int DefaultImageWidth = 320; // TODO: Read the default image when it's necessary const int DefaultImageHeight = 240; // TODO: Read the default image when it's necessary const float DefaultFramerate = 60.0f; // TODO: Read the default image when it's necessary @@ -172,9 +172,9 @@ Softcam::Softcam(LPUNKNOWN lpunk, const GUID& clsid, HRESULT *phr) : CSource(NAME("DirectShow Softcam"), lpunk, clsid), m_frame_buffer(FrameBuffer::open()), m_valid(m_frame_buffer || HasDefaultImage), - m_width(m_frame_buffer ? m_frame_buffer.width() : DefaultImageWidth), - m_height(m_frame_buffer ? m_frame_buffer.height() : DefaultImageHeight), - m_framerate(m_frame_buffer ? m_frame_buffer.framerate() : DefaultFramerate) + m_width(m_frame_buffer ? m_frame_buffer.width() : HasDefaultImage ? DefaultImageWidth : 0), + m_height(m_frame_buffer ? m_frame_buffer.height() : HasDefaultImage ? DefaultImageHeight : 0), + m_framerate(m_frame_buffer ? m_frame_buffer.framerate() : HasDefaultImage ? DefaultFramerate : 0.0f) { CAutoLock lock(&m_cStateLock); From c21388da6a9b38330a76ce2ae491336000a14846 Mon Sep 17 00:00:00 2001 From: tshino Date: Sun, 21 May 2023 22:45:57 +0900 Subject: [PATCH 03/14] New DefaultImage class --- src/softcamcore/DefaultImage.cpp | 32 ++++++++++++++++++++ src/softcamcore/DefaultImage.h | 32 ++++++++++++++++++++ src/softcamcore/softcamcore.vcxproj | 2 ++ src/softcamcore/softcamcore.vcxproj.filters | 6 ++++ tests/core_tests/DefaultImageTest.cpp | 33 +++++++++++++++++++++ tests/core_tests/core_tests.vcxproj | 1 + 6 files changed, 106 insertions(+) create mode 100644 src/softcamcore/DefaultImage.cpp create mode 100644 src/softcamcore/DefaultImage.h create mode 100644 tests/core_tests/DefaultImageTest.cpp diff --git a/src/softcamcore/DefaultImage.cpp b/src/softcamcore/DefaultImage.cpp new file mode 100644 index 0000000..138e006 --- /dev/null +++ b/src/softcamcore/DefaultImage.cpp @@ -0,0 +1,32 @@ +#include "DefaultImage.h" + +#include + + +namespace softcam { + + +DefaultImage +DefaultImage::tryLoad(const std::string& file_path) +{ + // TODO: check the file and try load image from the file + return {}; +} + +DefaultImage +DefaultImage::makeBlankImage(int width, int height) +{ + DefaultImage img; + img.m_valid = true; + img.m_width = width; + img.m_height = height; + + auto buffer_size = height * width * 3; + img.m_bits = std::make_unique(buffer_size); + std::memset(img.m_bits.get(), 0x00, buffer_size); + + return std::move(img); +} + + +} //namespace softcam diff --git a/src/softcamcore/DefaultImage.h b/src/softcamcore/DefaultImage.h new file mode 100644 index 0000000..c2e4e78 --- /dev/null +++ b/src/softcamcore/DefaultImage.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + + +namespace softcam { + + +/// Default image +class DefaultImage +{ + public: + static DefaultImage tryLoad(const std::string& file_path); + static DefaultImage makeBlankImage(int width, int height); + + explicit operator bool() const { return m_valid; } + + int width() const { return m_width; } + int height() const { return m_height; } + const void* imageBits() const { return m_bits.get(); } + + private: + bool m_valid = false; + int m_width = 0; + int m_height = 0; + std::unique_ptr m_bits; +}; + + +} //namespace softcam diff --git a/src/softcamcore/softcamcore.vcxproj b/src/softcamcore/softcamcore.vcxproj index 8dac4af..f84a05a 100644 --- a/src/softcamcore/softcamcore.vcxproj +++ b/src/softcamcore/softcamcore.vcxproj @@ -149,6 +149,7 @@ + @@ -156,6 +157,7 @@ + diff --git a/src/softcamcore/softcamcore.vcxproj.filters b/src/softcamcore/softcamcore.vcxproj.filters index c861811..05c3617 100644 --- a/src/softcamcore/softcamcore.vcxproj.filters +++ b/src/softcamcore/softcamcore.vcxproj.filters @@ -26,6 +26,9 @@ Header Files + + Header Files + @@ -43,5 +46,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/tests/core_tests/DefaultImageTest.cpp b/tests/core_tests/DefaultImageTest.cpp new file mode 100644 index 0000000..c2f80f7 --- /dev/null +++ b/tests/core_tests/DefaultImageTest.cpp @@ -0,0 +1,33 @@ +#include +#include + + +namespace { +namespace sc = softcam; + + +TEST(DefaultImage, FileNotFound) { + auto img = sc::DefaultImage::tryLoad("not_a_valid_file_path"); + + EXPECT_FALSE( img ); + EXPECT_EQ( img.width(), 0 ); + EXPECT_EQ( img.height(), 0 ); + EXPECT_EQ( img.imageBits(), nullptr ); +} + +TEST(DefaultImage, BlankImage) { + auto img = sc::DefaultImage::makeBlankImage(320, 200); + + EXPECT_TRUE( img ); + EXPECT_EQ( img.width(), 320 ); + EXPECT_EQ( img.height(), 200 ); + + ASSERT_NE( img.imageBits(), nullptr ); + EXPECT_EQ( ((const std::uint8_t*)img.imageBits())[0], 0x00 ); + EXPECT_EQ( ((const std::uint8_t*)img.imageBits())[1], 0x00 ); + EXPECT_EQ( ((const std::uint8_t*)img.imageBits())[2], 0x00 ); + EXPECT_EQ( ((const std::uint8_t*)img.imageBits())[200*320*3-1], 0x00 ); +} + + +} //namespace diff --git a/tests/core_tests/core_tests.vcxproj b/tests/core_tests/core_tests.vcxproj index c807857..fae4c5f 100644 --- a/tests/core_tests/core_tests.vcxproj +++ b/tests/core_tests/core_tests.vcxproj @@ -45,6 +45,7 @@ $(Platform)\$(Configuration)\ + From 568a92cab83f45965e0d313562a670ede1e601e0 Mon Sep 17 00:00:00 2001 From: tshino Date: Mon, 22 May 2023 05:07:21 +0900 Subject: [PATCH 04/14] Add GetModuleDirectoryPath() --- src/softcamcore/Misc.cpp | 30 ++++++++++++++++++++++++++++++ src/softcamcore/Misc.h | 5 +++++ tests/core_tests/MiscTest.cpp | 7 +++++++ 3 files changed, 42 insertions(+) diff --git a/src/softcamcore/Misc.cpp b/src/softcamcore/Misc.cpp index a4af02d..c9b038b 100644 --- a/src/softcamcore/Misc.cpp +++ b/src/softcamcore/Misc.cpp @@ -1,10 +1,14 @@ #include "Misc.h" #include +#include #include #include +#pragma comment(lib, "shlwapi.lib") // PathRemoveFileSpecA + + namespace softcam { @@ -188,4 +192,30 @@ SharedMemory::unmap(void* ptr) } } +std::string +GetModuleDirectoryPath() +{ + // get module handle + MEMORY_BASIC_INFORMATION info; + if (0 < VirtualQueryEx( + GetCurrentProcess(), + (void*)&GetModuleDirectoryPath, + &info, + sizeof(info))) + { + HMODULE hmodule = (HMODULE)info.AllocationBase; + // get the file path + char buf[1024]; + if (0 < GetModuleFileNameA(hmodule, buf, sizeof(buf))) + { + // get the directory path + if (PathRemoveFileSpecA(buf)) + { + return buf; + } + } + } + return {}; +} + } //namespace softcam diff --git a/src/softcamcore/Misc.h b/src/softcamcore/Misc.h index 49d32c4..15457d4 100644 --- a/src/softcamcore/Misc.h +++ b/src/softcamcore/Misc.h @@ -2,6 +2,7 @@ #include #include +#include namespace softcam { @@ -69,4 +70,8 @@ class SharedMemory }; +/// Retrieve the directory path of this module (DLL/EXE) +std::string GetModuleDirectoryPath(); + + } //namespace softcam diff --git a/tests/core_tests/MiscTest.cpp b/tests/core_tests/MiscTest.cpp index 8e28385..d50dcf0 100644 --- a/tests/core_tests/MiscTest.cpp +++ b/tests/core_tests/MiscTest.cpp @@ -218,4 +218,11 @@ TEST(SharedMemory, MultipleOpenSucceeds) { EXPECT_GE( view3.size(), SHMEM_SIZE ); } +TEST(GetModuleDirectoryPath, NotEmpty) { + auto path = sc::GetModuleDirectoryPath(); + + EXPECT_FALSE( path.empty() ); +} + + } //namespace From 10a31a41127abe3a0a9eb3635902bc126cd4affa Mon Sep 17 00:00:00 2001 From: tshino Date: Tue, 30 May 2023 01:14:21 +0900 Subject: [PATCH 05/14] Add test for GetModuleDirectoryPath() --- tests/core_tests/MiscTest.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/core_tests/MiscTest.cpp b/tests/core_tests/MiscTest.cpp index d50dcf0..c8013a6 100644 --- a/tests/core_tests/MiscTest.cpp +++ b/tests/core_tests/MiscTest.cpp @@ -224,5 +224,12 @@ TEST(GetModuleDirectoryPath, NotEmpty) { EXPECT_FALSE( path.empty() ); } +TEST(GetModuleDirectoryPath, Consistent) { + auto path1 = sc::GetModuleDirectoryPath(); + auto path2 = sc::GetModuleDirectoryPath(); + + EXPECT_TRUE( path1 == path2 ); +} + } //namespace From 9884c7c9ec5da570b0926be80707d1f084baa00b Mon Sep 17 00:00:00 2001 From: tshino Date: Tue, 30 May 2023 20:13:19 +0900 Subject: [PATCH 06/14] Add test harness for default image --- src/softcamcore/DShowSoftcam.h | 4 ++++ tests/core_tests/DShowSoftcamTest.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/src/softcamcore/DShowSoftcam.h b/src/softcamcore/DShowSoftcam.h index 4e18d82..f0ec530 100644 --- a/src/softcamcore/DShowSoftcam.h +++ b/src/softcamcore/DShowSoftcam.h @@ -33,6 +33,10 @@ class Softcam : public CSource, public IAMStreamConfig float framerate() const { return m_framerate; } void releaseFrameBuffer(); + // Testing purpose only + static void enableDefaultBlankImage(int width, int height) {}; + static void disableDefaultBlankImage() {}; + private: FrameBuffer m_frame_buffer; const bool m_valid; diff --git a/tests/core_tests/DShowSoftcamTest.cpp b/tests/core_tests/DShowSoftcamTest.cpp index 87b926e..1b796dd 100644 --- a/tests/core_tests/DShowSoftcamTest.cpp +++ b/tests/core_tests/DShowSoftcamTest.cpp @@ -57,6 +57,7 @@ class Softcam : public ::testing::Test { m_softcam->Release(); } + sc::Softcam::disableDefaultBlankImage(); } }; From ba04239dd98a75736324dcafe6ae83d6ac7807ce Mon Sep 17 00:00:00 2001 From: tshino Date: Wed, 31 May 2023 00:39:20 +0900 Subject: [PATCH 07/14] Add tests for default image handling --- src/softcamcore/DShowSoftcam.cpp | 35 +++++++++++++++++++++------ src/softcamcore/DShowSoftcam.h | 6 +++-- tests/core_tests/DShowSoftcamTest.cpp | 33 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/softcamcore/DShowSoftcam.cpp b/src/softcamcore/DShowSoftcam.cpp index 7ea5011..c45eb9e 100644 --- a/src/softcamcore/DShowSoftcam.cpp +++ b/src/softcamcore/DShowSoftcam.cpp @@ -147,16 +147,31 @@ AM_MEDIA_TYPE* makeMediaType(int width, int height, float framerate) return amt; } -const bool HasDefaultImage = false; //true; // TODO: Check if default image is available or not -const int DefaultImageWidth = 320; // TODO: Read the default image when it's necessary -const int DefaultImageHeight = 240; // TODO: Read the default image when it's necessary -const float DefaultFramerate = 60.0f; // TODO: Read the default image when it's necessary +bool UseDefaultBlankImage = false; // Testing purpose only +int DefaultImageWidth = 0; // Testing purpose only +int DefaultImageHeight = 0; // Testing purpose only +const float DefaultFramerate = 60.0f; } //namespace namespace softcam { +void Softcam::enableDefaultBlankImage(int width, int height) +{ + UseDefaultBlankImage = true; + DefaultImageWidth = width; + DefaultImageHeight = height; +} + +void Softcam::disableDefaultBlankImage() +{ + UseDefaultBlankImage = false; + DefaultImageWidth = 0; + DefaultImageHeight = 0; +} + + CUnknown * Softcam::CreateInstance( LPUNKNOWN lpunk, const GUID& clsid, @@ -171,10 +186,14 @@ CUnknown * Softcam::CreateInstance( Softcam::Softcam(LPUNKNOWN lpunk, const GUID& clsid, HRESULT *phr) : CSource(NAME("DirectShow Softcam"), lpunk, clsid), m_frame_buffer(FrameBuffer::open()), - m_valid(m_frame_buffer || HasDefaultImage), - m_width(m_frame_buffer ? m_frame_buffer.width() : HasDefaultImage ? DefaultImageWidth : 0), - m_height(m_frame_buffer ? m_frame_buffer.height() : HasDefaultImage ? DefaultImageHeight : 0), - m_framerate(m_frame_buffer ? m_frame_buffer.framerate() : HasDefaultImage ? DefaultFramerate : 0.0f) + m_default_image( + m_frame_buffer ? DefaultImage{} : + UseDefaultBlankImage ? DefaultImage::makeBlankImage(DefaultImageWidth, DefaultImageHeight) : + DefaultImage{} /* TODO: Read the default image when it's necessary */), + m_valid(m_frame_buffer || m_default_image), + m_width(m_frame_buffer ? m_frame_buffer.width() : m_default_image ? m_default_image.width() : 0), + m_height(m_frame_buffer ? m_frame_buffer.height() : m_default_image ? m_default_image.height() : 0), + m_framerate(m_frame_buffer ? m_frame_buffer.framerate() : m_default_image ? DefaultFramerate : 0.0f) { CAutoLock lock(&m_cStateLock); diff --git a/src/softcamcore/DShowSoftcam.h b/src/softcamcore/DShowSoftcam.h index f0ec530..8477e35 100644 --- a/src/softcamcore/DShowSoftcam.h +++ b/src/softcamcore/DShowSoftcam.h @@ -3,6 +3,7 @@ #include #include #include "FrameBuffer.h" +#include "DefaultImage.h" namespace softcam { @@ -34,11 +35,12 @@ class Softcam : public CSource, public IAMStreamConfig void releaseFrameBuffer(); // Testing purpose only - static void enableDefaultBlankImage(int width, int height) {}; - static void disableDefaultBlankImage() {}; + static void enableDefaultBlankImage(int width, int height); + static void disableDefaultBlankImage(); private: FrameBuffer m_frame_buffer; + DefaultImage m_default_image; const bool m_valid; const int m_width; const int m_height; diff --git a/tests/core_tests/DShowSoftcamTest.cpp b/tests/core_tests/DShowSoftcamTest.cpp index 1b796dd..a9d49b9 100644 --- a/tests/core_tests/DShowSoftcamTest.cpp +++ b/tests/core_tests/DShowSoftcamTest.cpp @@ -130,6 +130,39 @@ TEST_F(Softcam, AttributesNormal) EXPECT_EQ( m_softcam->framerate(), 60.0f ); } +TEST_F(Softcam, AttributesNoServerButWithDefaultImage) +{ + sc::Softcam::enableDefaultBlankImage(320, 240); + + HRESULT hr = 555; + m_softcam = (sc::Softcam*)sc::Softcam::CreateInstance(nullptr, SOME_GUID, &hr); + ASSERT_NE( m_softcam, nullptr ); + m_softcam->AddRef(); + + EXPECT_EQ( m_softcam->getFrameBuffer(), nullptr ); + EXPECT_EQ( m_softcam->valid(), true ); + EXPECT_EQ( m_softcam->width(), 320 ); + EXPECT_EQ( m_softcam->height(), 240 ); + EXPECT_EQ( m_softcam->framerate(), 60.0f ); +} + +TEST_F(Softcam, AttributesWithBothValidServerAndDefaultImage) +{ + sc::Softcam::enableDefaultBlankImage(160, 120); + auto fb = createFrameBufer(320, 240, 30); + + HRESULT hr = 555; + m_softcam = (sc::Softcam*)sc::Softcam::CreateInstance(nullptr, SOME_GUID, &hr); + ASSERT_NE( m_softcam, nullptr ); + m_softcam->AddRef(); + + EXPECT_NE( m_softcam->getFrameBuffer(), nullptr ); + EXPECT_EQ( m_softcam->valid(), true ); + EXPECT_EQ( m_softcam->width(), 320 ); + EXPECT_EQ( m_softcam->height(), 240 ); + EXPECT_EQ( m_softcam->framerate(), 30.0f ); +} + TEST_F(Softcam, AttributesDeactivatedServer) { auto fb = createFrameBufer(320, 240, 60); From 25b22441dabdba0fb9a184ebf8bc707fd6f7cc76 Mon Sep 17 00:00:00 2001 From: tshino Date: Wed, 31 May 2023 00:48:41 +0900 Subject: [PATCH 08/14] Add tests for default image handling --- tests/core_tests/DShowSoftcamTest.cpp | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/core_tests/DShowSoftcamTest.cpp b/tests/core_tests/DShowSoftcamTest.cpp index a9d49b9..90897ca 100644 --- a/tests/core_tests/DShowSoftcamTest.cpp +++ b/tests/core_tests/DShowSoftcamTest.cpp @@ -163,6 +163,54 @@ TEST_F(Softcam, AttributesWithBothValidServerAndDefaultImage) EXPECT_EQ( m_softcam->framerate(), 30.0f ); } +TEST_F(Softcam, AttributesSwitchingFromDefaultImageToActualServer) +{ + sc::Softcam::enableDefaultBlankImage(320, 240); + + HRESULT hr = 555; + m_softcam = (sc::Softcam*)sc::Softcam::CreateInstance(nullptr, SOME_GUID, &hr); + ASSERT_NE( m_softcam, nullptr ); + m_softcam->AddRef(); + + EXPECT_EQ( m_softcam->getFrameBuffer(), nullptr ); + EXPECT_EQ( m_softcam->valid(), true ); + EXPECT_EQ( m_softcam->width(), 320 ); + EXPECT_EQ( m_softcam->height(), 240 ); + EXPECT_EQ( m_softcam->framerate(), 60.0f ); + + auto fb = createFrameBufer(320, 240, 30); + + EXPECT_NE( m_softcam->getFrameBuffer(), nullptr ); + EXPECT_EQ( m_softcam->valid(), true ); + EXPECT_EQ( m_softcam->width(), 320 ); + EXPECT_EQ( m_softcam->height(), 240 ); + EXPECT_EQ( m_softcam->framerate(), 60.0f ); +} + +TEST_F(Softcam, AttributesKeepingDefaultImageIfIncompatibleServer) +{ + sc::Softcam::enableDefaultBlankImage(320, 240); + + HRESULT hr = 555; + m_softcam = (sc::Softcam*)sc::Softcam::CreateInstance(nullptr, SOME_GUID, &hr); + ASSERT_NE( m_softcam, nullptr ); + m_softcam->AddRef(); + + EXPECT_EQ( m_softcam->getFrameBuffer(), nullptr ); + EXPECT_EQ( m_softcam->valid(), true ); + EXPECT_EQ( m_softcam->width(), 320 ); + EXPECT_EQ( m_softcam->height(), 240 ); + EXPECT_EQ( m_softcam->framerate(), 60.0f ); + + auto fb = createFrameBufer(300, 200, 30); // <-- + + EXPECT_EQ( m_softcam->getFrameBuffer(), nullptr ); // <-- + EXPECT_EQ( m_softcam->valid(), true ); + EXPECT_EQ( m_softcam->width(), 320 ); // <-- + EXPECT_EQ( m_softcam->height(), 240 ); // <-- + EXPECT_EQ( m_softcam->framerate(), 60.0f ); +} + TEST_F(Softcam, AttributesDeactivatedServer) { auto fb = createFrameBufer(320, 240, 60); From 5980816f383778cf21d3557a1c07c85db44e3b51 Mon Sep 17 00:00:00 2001 From: tshino Date: Thu, 1 Jun 2023 02:49:53 +0900 Subject: [PATCH 09/14] Implement DefaultImage::tryLoad() --- src/softcamcore/DefaultImage.cpp | 87 +++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/src/softcamcore/DefaultImage.cpp b/src/softcamcore/DefaultImage.cpp index 138e006..a2933a8 100644 --- a/src/softcamcore/DefaultImage.cpp +++ b/src/softcamcore/DefaultImage.cpp @@ -1,6 +1,28 @@ #include "DefaultImage.h" #include +#include +#include +#include + + +#pragma comment(lib, "Windowscodecs.lib") + + +namespace { + +std::wstring ToWString(const std::string& str) +{ + size_t len = 0, converted = 0; + _locale_t loc = _create_locale(LC_ALL, ""); + _mbstowcs_s_l(&len, nullptr, 0, str.data(), str.size() + 1, loc); + std::wstring wstr(len, L'\0'); + _mbstowcs_s_l(&converted, (wchar_t*)wstr.data(), len, str.data(), _TRUNCATE, loc); + _free_locale(loc); + return std::wstring(wstr.c_str()); +} + +} namespace softcam { @@ -9,8 +31,69 @@ namespace softcam { DefaultImage DefaultImage::tryLoad(const std::string& file_path) { - // TODO: check the file and try load image from the file - return {}; + if (!PathFileExistsA(file_path.c_str())) + { + return {}; + } + if (FAILED(CoInitialize(nullptr))) + { + return {}; + } + DefaultImage img; + IWICImagingFactory* factory = nullptr; + if (!FAILED(CoCreateInstance( + CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, + IID_IWICImagingFactory, (void**)&factory))) + { + IWICBitmapDecoder* decoder = nullptr; + if (!FAILED(factory->CreateDecoderFromFilename( + ToWString(file_path).c_str(), nullptr, GENERIC_READ, + WICDecodeMetadataCacheOnDemand, &decoder))) + { + IWICBitmapFrameDecode* decode = nullptr; + if (!FAILED(decoder->GetFrame(0, &decode))) + { + IWICFormatConverter* converter = nullptr; + if (!FAILED(factory->CreateFormatConverter(&converter))) + { + do + { + if (FAILED(converter->Initialize( + decode, GUID_WICPixelFormat24bppBGR, WICBitmapDitherTypeNone, + nullptr, 0.0, WICBitmapPaletteTypeCustom))) + { + break; + } + UINT w, h; + if (FAILED(converter->GetSize(&w, &h))) + { + break; + } + if (w < 1 || w > 16384 || w % 4 != 0 || + h < 1 || h > 16384 || h % 4 != 0) + { + break; + } + auto bits = std::make_unique(h*w*3); + if (FAILED(converter->CopyPixels(nullptr, w*3, h*w*3, bits.get()))) + { + break; + } + img.m_valid = true; + img.m_width = (int)w; + img.m_height = (int)h; + img.m_bits = std::move(bits); + } while (false); + converter->Release(); + } + decode->Release(); + } + decoder->Release(); + } + factory->Release(); + } + CoUninitialize(); + return img; } DefaultImage From 2bb3c8ab682be6832b26ad966be349f74fc20158 Mon Sep 17 00:00:00 2001 From: tshino Date: Sun, 4 Jun 2023 18:34:10 +0900 Subject: [PATCH 10/14] Use COINIT_MULTITHREADED --- src/softcamcore/DefaultImage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/softcamcore/DefaultImage.cpp b/src/softcamcore/DefaultImage.cpp index a2933a8..c8d9a80 100644 --- a/src/softcamcore/DefaultImage.cpp +++ b/src/softcamcore/DefaultImage.cpp @@ -35,7 +35,7 @@ DefaultImage::tryLoad(const std::string& file_path) { return {}; } - if (FAILED(CoInitialize(nullptr))) + if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) { return {}; } From 45dda22d3d47950bd9304c292d84567135a0013e Mon Sep 17 00:00:00 2001 From: tshino Date: Sun, 4 Jun 2023 18:51:10 +0900 Subject: [PATCH 11/14] Detect and use default image if available --- src/softcamcore/DShowSoftcam.cpp | 28 ++++++++++++++++++++++++---- src/softcamcore/DShowSoftcam.h | 2 ++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/softcamcore/DShowSoftcam.cpp b/src/softcamcore/DShowSoftcam.cpp index c45eb9e..f226229 100644 --- a/src/softcamcore/DShowSoftcam.cpp +++ b/src/softcamcore/DShowSoftcam.cpp @@ -8,6 +8,8 @@ #include #include +#include "Misc.h" + namespace { @@ -150,6 +152,8 @@ AM_MEDIA_TYPE* makeMediaType(int width, int height, float framerate) bool UseDefaultBlankImage = false; // Testing purpose only int DefaultImageWidth = 0; // Testing purpose only int DefaultImageHeight = 0; // Testing purpose only + +const char DefaultImageSuffix[] = "\\placeholder.png"; const float DefaultFramerate = 60.0f; } //namespace @@ -189,12 +193,15 @@ Softcam::Softcam(LPUNKNOWN lpunk, const GUID& clsid, HRESULT *phr) : m_default_image( m_frame_buffer ? DefaultImage{} : UseDefaultBlankImage ? DefaultImage::makeBlankImage(DefaultImageWidth, DefaultImageHeight) : - DefaultImage{} /* TODO: Read the default image when it's necessary */), + DefaultImage::tryLoad(GetModuleDirectoryPath() + DefaultImageSuffix)), m_valid(m_frame_buffer || m_default_image), m_width(m_frame_buffer ? m_frame_buffer.width() : m_default_image ? m_default_image.width() : 0), m_height(m_frame_buffer ? m_frame_buffer.height() : m_default_image ? m_default_image.height() : 0), m_framerate(m_frame_buffer ? m_frame_buffer.framerate() : m_default_image ? DefaultFramerate : 0.0f) { + LOG("ctor -> frame_buffer:%s(%dx%d) default_image:%s(%dx%d)\n", + m_frame_buffer ? "valid" : "none", m_frame_buffer.width(), m_frame_buffer.height(), + m_default_image ? "valid" : "none", m_default_image.width(), m_default_image.height()); CAutoLock lock(&m_cStateLock); m_paStreams = new CSourceStream*[1]; @@ -388,6 +395,20 @@ Softcam::releaseFrameBuffer() m_frame_buffer.release(); } +const DefaultImage* +Softcam::getDefaultImage() +{ + CAutoLock lock(&m_cStateLock); + if (m_default_image) + { + return &m_default_image; + } + else + { + return nullptr; + } +} + SoftcamStream::SoftcamStream(HRESULT *phr, Softcam *pParent, LPCWSTR pPinName) : @@ -474,10 +495,9 @@ HRESULT SoftcamStream::FillBuffer(IMediaSample *pms) { std::memcpy(pData, m_screenshot.get(), size); } - else + else if (auto def = getParent()->getDefaultImage()) { - // TODO: Write the default image - std::memset(pData, 77, size); + std::memcpy(pData, def->imageBits(), size); } } diff --git a/src/softcamcore/DShowSoftcam.h b/src/softcamcore/DShowSoftcam.h index 8477e35..a6b27fd 100644 --- a/src/softcamcore/DShowSoftcam.h +++ b/src/softcamcore/DShowSoftcam.h @@ -34,6 +34,8 @@ class Softcam : public CSource, public IAMStreamConfig float framerate() const { return m_framerate; } void releaseFrameBuffer(); + const DefaultImage* getDefaultImage(); + // Testing purpose only static void enableDefaultBlankImage(int width, int height); static void disableDefaultBlankImage(); From 83de1629e190451b9e61a9d3e175faffa350915e Mon Sep 17 00:00:00 2001 From: tshino Date: Sun, 10 Sep 2023 15:13:12 +0900 Subject: [PATCH 12/14] Remove redundant lock --- src/softcamcore/DShowSoftcam.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/softcamcore/DShowSoftcam.cpp b/src/softcamcore/DShowSoftcam.cpp index f226229..75bce12 100644 --- a/src/softcamcore/DShowSoftcam.cpp +++ b/src/softcamcore/DShowSoftcam.cpp @@ -202,10 +202,12 @@ Softcam::Softcam(LPUNKNOWN lpunk, const GUID& clsid, HRESULT *phr) : LOG("ctor -> frame_buffer:%s(%dx%d) default_image:%s(%dx%d)\n", m_frame_buffer ? "valid" : "none", m_frame_buffer.width(), m_frame_buffer.height(), m_default_image ? "valid" : "none", m_default_image.width(), m_default_image.height()); - CAutoLock lock(&m_cStateLock); - m_paStreams = new CSourceStream*[1]; - m_paStreams[0] = new SoftcamStream(phr, this, L"DirectShow Softcam Stream"); + // This code is okay though it may look strange as the return value is ignored. + // Calling the SoftcamStream constructor results in calling the CBaseOutputPin + // constructor which registers the instance to this Softcam instance by calling + // CSource::AddPin(). + (void)new SoftcamStream(phr, this, L"DirectShow Softcam Stream"); } From a3b1f354700b501883f9c39858572f9cbf8ae2e3 Mon Sep 17 00:00:00 2001 From: tshino Date: Sun, 10 Sep 2023 15:23:01 +0900 Subject: [PATCH 13/14] Stop using base-class's lock --- src/softcamcore/DShowSoftcam.cpp | 4 +-- src/softcamcore/DShowSoftcam.h | 1 + tests/core_tests/DShowSoftcamTest.cpp | 38 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/softcamcore/DShowSoftcam.cpp b/src/softcamcore/DShowSoftcam.cpp index 75bce12..2c013e8 100644 --- a/src/softcamcore/DShowSoftcam.cpp +++ b/src/softcamcore/DShowSoftcam.cpp @@ -368,7 +368,7 @@ FrameBuffer* Softcam::getFrameBuffer() return nullptr; } - CAutoLock lock(&m_cStateLock); + CAutoLock lock(&m_critsec); if (!m_frame_buffer) { auto fb = FrameBuffer::open(); @@ -393,7 +393,7 @@ FrameBuffer* Softcam::getFrameBuffer() void Softcam::releaseFrameBuffer() { - CAutoLock lock(&m_cStateLock); + CAutoLock lock(&m_critsec); m_frame_buffer.release(); } diff --git a/src/softcamcore/DShowSoftcam.h b/src/softcamcore/DShowSoftcam.h index a6b27fd..8ff8b03 100644 --- a/src/softcamcore/DShowSoftcam.h +++ b/src/softcamcore/DShowSoftcam.h @@ -41,6 +41,7 @@ class Softcam : public CSource, public IAMStreamConfig static void disableDefaultBlankImage(); private: + CCritSec m_critsec; FrameBuffer m_frame_buffer; DefaultImage m_default_image; const bool m_valid; diff --git a/tests/core_tests/DShowSoftcamTest.cpp b/tests/core_tests/DShowSoftcamTest.cpp index 90897ca..5aebc73 100644 --- a/tests/core_tests/DShowSoftcamTest.cpp +++ b/tests/core_tests/DShowSoftcamTest.cpp @@ -1003,5 +1003,43 @@ TEST_F(SoftcamStream, CSourceStreamGetMediaTypeNormal) checkMediaType320x240(&mt); } +TEST_F(SoftcamStream, getFrameBuffer_must_not_lock_the_filter_state) +{ + auto fb = createFrameBufer(320, 240, 60); + SetUpSoftcamStream(); + + CAutoLock lock(m_softcam->pStateLock()); // 1st lock + std::atomic done{false}; + std::thread th([&] + { + // A deadlock occurs if this code attempts to get lock. + m_softcam->getFrameBuffer(); + done = true; + }); + + for (int i = 0; !done && i < 20; i++) { sc::Timer::sleep(0.050f); } + ASSERT_TRUE( done ); + th.join(); +} + +TEST_F(SoftcamStream, releaseFrameBuffer_must_not_lock_the_filter_state) +{ + auto fb = createFrameBufer(320, 240, 60); + SetUpSoftcamStream(); + + CAutoLock lock(m_softcam->pStateLock()); // 1st lock + std::atomic done{false}; + std::thread th([&] + { + // A deadlock occurs if this code attempts to get lock. + m_softcam->releaseFrameBuffer(); + done = true; + }); + + for (int i = 0; !done && i < 20; i++) { sc::Timer::sleep(0.050f); } + ASSERT_TRUE( done ); + th.join(); +} + } //namespace From 24d85cd208afc83fcce88d1db64b08587c1766e4 Mon Sep 17 00:00:00 2001 From: tshino Date: Wed, 27 Dec 2023 00:28:31 +0900 Subject: [PATCH 14/14] Use correct lock --- src/softcamcore/DShowSoftcam.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/softcamcore/DShowSoftcam.cpp b/src/softcamcore/DShowSoftcam.cpp index 2c013e8..859eca4 100644 --- a/src/softcamcore/DShowSoftcam.cpp +++ b/src/softcamcore/DShowSoftcam.cpp @@ -400,7 +400,7 @@ Softcam::releaseFrameBuffer() const DefaultImage* Softcam::getDefaultImage() { - CAutoLock lock(&m_cStateLock); + CAutoLock lock(&m_critsec); if (m_default_image) { return &m_default_image;