Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default image feature #25

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 65 additions & 11 deletions src/softcamcore/DShowSoftcam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <chrono>
#include <ctime>

#include "Misc.h"


namespace {

Expand Down Expand Up @@ -147,11 +149,33 @@ AM_MEDIA_TYPE* makeMediaType(int width, int height, float framerate)
return amt;
}

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

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,
Expand All @@ -166,15 +190,24 @@ 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_default_image(
m_frame_buffer ? DefaultImage{} :
UseDefaultBlankImage ? DefaultImage::makeBlankImage(DefaultImageWidth, DefaultImageHeight) :
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)
{
CAutoLock lock(&m_cStateLock);

m_paStreams = new CSourceStream*[1];
m_paStreams[0] = new SoftcamStream(phr, this, L"DirectShow Softcam Stream");
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());

// 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");
}


Expand Down Expand Up @@ -335,7 +368,7 @@ FrameBuffer* Softcam::getFrameBuffer()
return nullptr;
}

CAutoLock lock(&m_cStateLock);
CAutoLock lock(&m_critsec);
if (!m_frame_buffer)
{
auto fb = FrameBuffer::open();
Expand All @@ -360,10 +393,24 @@ FrameBuffer* Softcam::getFrameBuffer()
void
Softcam::releaseFrameBuffer()
{
CAutoLock lock(&m_cStateLock);
CAutoLock lock(&m_critsec);
m_frame_buffer.release();
}

const DefaultImage*
Softcam::getDefaultImage()
{
CAutoLock lock(&m_critsec);
if (m_default_image)
{
return &m_default_image;
}
else
{
return nullptr;
}
}

SoftcamStream::SoftcamStream(HRESULT *phr,
Softcam *pParent,
LPCWSTR pPinName) :
Expand Down Expand Up @@ -446,7 +493,14 @@ 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 if (auto def = getParent()->getDefaultImage())
{
std::memcpy(pData, def->imageBits(), size);
}
}

CAutoLock lock(&m_critsec);
Expand Down
9 changes: 9 additions & 0 deletions src/softcamcore/DShowSoftcam.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <memory>
#include <baseclasses/streams.h>
#include "FrameBuffer.h"
#include "DefaultImage.h"


namespace softcam {
Expand Down Expand Up @@ -33,8 +34,16 @@ 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();

private:
CCritSec m_critsec;
FrameBuffer m_frame_buffer;
DefaultImage m_default_image;
const bool m_valid;
const int m_width;
const int m_height;
Expand Down
115 changes: 115 additions & 0 deletions src/softcamcore/DefaultImage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "DefaultImage.h"

#include <utility>
#include <locale.h>
#include <shlwapi.h>
#include <wincodec.h>


#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 {


DefaultImage
DefaultImage::tryLoad(const std::string& file_path)
{
if (!PathFileExistsA(file_path.c_str()))
{
return {};
}
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
{
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<std::uint8_t[]>(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
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<std::uint8_t[]>(buffer_size);
std::memset(img.m_bits.get(), 0x00, buffer_size);

return std::move(img);
}


} //namespace softcam
32 changes: 32 additions & 0 deletions src/softcamcore/DefaultImage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <string>
#include <memory>
#include <cstdint>


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<std::uint8_t[]> m_bits;
};


} //namespace softcam
30 changes: 30 additions & 0 deletions src/softcamcore/Misc.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#include "Misc.h"

#include <windows.h>
#include <shlwapi.h>
#include <cmath>
#include <cassert>


#pragma comment(lib, "shlwapi.lib") // PathRemoveFileSpecA


namespace softcam {


Expand Down Expand Up @@ -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
5 changes: 5 additions & 0 deletions src/softcamcore/Misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <memory>
#include <cstdint>
#include <string>


namespace softcam {
Expand Down Expand Up @@ -69,4 +70,8 @@ class SharedMemory
};


/// Retrieve the directory path of this module (DLL/EXE)
std::string GetModuleDirectoryPath();


} //namespace softcam
2 changes: 2 additions & 0 deletions src/softcamcore/softcamcore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,15 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="DefaultImage.h" />
<ClInclude Include="DShowSoftcam.h" />
<ClInclude Include="FrameBuffer.h" />
<ClInclude Include="Misc.h" />
<ClInclude Include="SenderAPI.h" />
<ClInclude Include="Watchdog.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="DefaultImage.cpp" />
<ClCompile Include="DShowSoftcam.cpp" />
<ClCompile Include="FrameBuffer.cpp" />
<ClCompile Include="Misc.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions src/softcamcore/softcamcore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
<ClInclude Include="Watchdog.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DefaultImage.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="FrameBuffer.cpp">
Expand All @@ -43,5 +46,8 @@
<ClCompile Include="Watchdog.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DefaultImage.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
Loading