Skip to content

Commit

Permalink
Added support for stream sources for recording and overlays
Browse files Browse the repository at this point in the history
  • Loading branch information
sskodje committed Apr 19, 2024
1 parent dc268e4 commit e750c2e
Show file tree
Hide file tree
Showing 21 changed files with 477 additions and 103 deletions.
88 changes: 51 additions & 37 deletions ScreenRecorderLib/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,9 +452,7 @@ OutputDimensions^ Recorder::GetOutputDimensionsForRecordingSources(IEnumerable<R
for each (RecordingSourceBase ^ recordingSource in recordingSources)
{
if (isinst<WindowRecordingSource^>(recordingSource)) {
WindowRecordingSource^ windowRecordingSource = (WindowRecordingSource^)recordingSource;
HWND hwnd = (HWND)windowRecordingSource->Handle.ToPointer();
if (hwnd == nativeSource->SourceWindow) {
if ((gcnew String(nativeSource->ID.c_str()))->Equals(recordingSource->ID)) {
outputDimensions->OutputCoordinates->Add(gcnew SourceCoordinates(recordingSource, gcnew ScreenRect(nativeSourceRect.left, nativeSourceRect.top, RectWidth(nativeSourceRect), RectHeight(nativeSourceRect))));
break;
}
Expand All @@ -466,8 +464,7 @@ OutputDimensions^ Recorder::GetOutputDimensionsForRecordingSources(IEnumerable<R
for each (RecordingSourceBase ^ recordingSource in recordingSources)
{
if (isinst<DisplayRecordingSource^>(recordingSource)) {
DisplayRecordingSource^ displayRecordingSource = (DisplayRecordingSource^)recordingSource;
if ((gcnew String(nativeSource->SourcePath.c_str()))->Equals(displayRecordingSource->DeviceName)) {
if ((gcnew String(nativeSource->ID.c_str()))->Equals(recordingSource->ID)) {
outputDimensions->OutputCoordinates->Add(gcnew SourceCoordinates(recordingSource, gcnew ScreenRect(nativeSourceRect.left, nativeSourceRect.top, RectWidth(nativeSourceRect), RectHeight(nativeSourceRect))));
break;
}
Expand All @@ -479,8 +476,7 @@ OutputDimensions^ Recorder::GetOutputDimensionsForRecordingSources(IEnumerable<R
for each (RecordingSourceBase ^ recordingSource in recordingSources)
{
if (isinst<VideoRecordingSource^>(recordingSource)) {
VideoRecordingSource^ videoRecordingSource = (VideoRecordingSource^)recordingSource;
if ((gcnew String(nativeSource->SourcePath.c_str()))->Equals(videoRecordingSource->SourcePath)) {
if ((gcnew String(nativeSource->ID.c_str()))->Equals(recordingSource->ID)) {
outputDimensions->OutputCoordinates->Add(gcnew SourceCoordinates(recordingSource, gcnew ScreenRect(nativeSourceRect.left, nativeSourceRect.top, RectWidth(nativeSourceRect), RectHeight(nativeSourceRect))));
break;
}
Expand All @@ -492,8 +488,7 @@ OutputDimensions^ Recorder::GetOutputDimensionsForRecordingSources(IEnumerable<R
for each (RecordingSourceBase ^ recordingSource in recordingSources)
{
if (isinst<VideoCaptureRecordingSource^>(recordingSource)) {
VideoCaptureRecordingSource^ cameraRecordingSource = (VideoCaptureRecordingSource^)recordingSource;
if ((gcnew String(nativeSource->ID.c_str()))->Equals(cameraRecordingSource->ID)) {
if ((gcnew String(nativeSource->ID.c_str()))->Equals(recordingSource->ID)) {
outputDimensions->OutputCoordinates->Add(gcnew SourceCoordinates(recordingSource, gcnew ScreenRect(nativeSourceRect.left, nativeSourceRect.top, RectWidth(nativeSourceRect), RectHeight(nativeSourceRect))));
break;
}
Expand All @@ -505,8 +500,7 @@ OutputDimensions^ Recorder::GetOutputDimensionsForRecordingSources(IEnumerable<R
for each (RecordingSourceBase ^ recordingSource in recordingSources)
{
if (isinst<ImageRecordingSource^>(recordingSource)) {
ImageRecordingSource^ videoRecordingSource = (ImageRecordingSource^)recordingSource;
if ((gcnew String(nativeSource->SourcePath.c_str()))->Equals(videoRecordingSource->SourcePath)) {
if ((gcnew String(nativeSource->ID.c_str()))->Equals(recordingSource->ID)) {
outputDimensions->OutputCoordinates->Add(gcnew SourceCoordinates(recordingSource, gcnew ScreenRect(nativeSourceRect.left, nativeSourceRect.top, RectWidth(nativeSourceRect), RectHeight(nativeSourceRect))));
break;
}
Expand Down Expand Up @@ -563,11 +557,7 @@ Recorder::!Recorder() {
delete m_Rec;
m_Rec = nullptr;
}
if (m_ManagedStream) {
delete m_ManagedStream;
m_ManagedStream = nullptr;
}
ClearCallbacks();
ReleaseResources();
}

Recorder^ Recorder::CreateRecorder() {
Expand Down Expand Up @@ -629,7 +619,7 @@ void Recorder::SetupCallbacks() {
CreateFrameNumberCallback();
}

void Recorder::ClearCallbacks() {
void Recorder::ReleaseCallbacks() {
if (_statusChangedDelegateGcHandler.IsAllocated)
_statusChangedDelegateGcHandler.Free();
if (_errorDelegateGcHandler.IsAllocated)
Expand All @@ -642,6 +632,24 @@ void Recorder::ClearCallbacks() {
_frameNumberDelegateGcHandler.Free();
}

void Recorder::ReleaseResources() {
ReleaseCallbacks();
if (m_ManagedStream) {
delete m_ManagedStream;
m_ManagedStream = nullptr;
}
if (m_Rec) {
for each (auto var in m_Rec->GetRecordingOverlays())
{
SafeRelease(&var->SourceStream);
}
for each (auto var in m_Rec->GetRecordingSources())
{
SafeRelease(&var->SourceStream);
}
}
}

HRESULT Recorder::CreateNativeRecordingSource(_In_ RecordingSourceBase^ managedSource, _Out_ RECORDING_SOURCE* pNativeSource)
{
HRESULT hr = E_FAIL;
Expand Down Expand Up @@ -724,19 +732,25 @@ HRESULT Recorder::CreateNativeRecordingSource(_In_ RecordingSourceBase^ managedS
}
else if (isinst<VideoRecordingSource^>(managedSource)) {
VideoRecordingSource^ videoSource = (VideoRecordingSource^)managedSource;
if (!String::IsNullOrEmpty(videoSource->SourcePath)) {
std::wstring sourcePath = msclr::interop::marshal_as<std::wstring>(videoSource->SourcePath);
nativeSource.Type = RecordingSourceType::Video;
nativeSource.SourcePath = sourcePath;
nativeSource.Type = RecordingSourceType::Video;
if (videoSource->SourceStream) {
nativeSource.SourceStream = new ManagedIStream(videoSource->SourceStream);
hr = S_OK;
}
else if (!String::IsNullOrEmpty(videoSource->SourcePath)) {
nativeSource.SourcePath = msclr::interop::marshal_as<std::wstring>(videoSource->SourcePath);
hr = S_OK;
}
}
else if (isinst<ImageRecordingSource^>(managedSource)) {
ImageRecordingSource^ imageSource = (ImageRecordingSource^)managedSource;
if (!String::IsNullOrEmpty(imageSource->SourcePath)) {
std::wstring sourcePath = msclr::interop::marshal_as<std::wstring>(imageSource->SourcePath);
nativeSource.Type = RecordingSourceType::Picture;
nativeSource.SourcePath = sourcePath;
nativeSource.Type = RecordingSourceType::Picture;
if (imageSource->SourceStream) {
nativeSource.SourceStream = new ManagedIStream(imageSource->SourceStream);
hr = S_OK;
}
else if (!String::IsNullOrEmpty(imageSource->SourcePath)) {
nativeSource.SourcePath = msclr::interop::marshal_as<std::wstring>(imageSource->SourcePath);
hr = S_OK;
}
}
Expand Down Expand Up @@ -767,15 +781,23 @@ HRESULT Recorder::CreateNativeRecordingOverlay(_In_ RecordingOverlayBase^ manage
else if (isinst<ImageOverlay^>(managedOverlay)) {
ImageOverlay^ pictureOverlay = (ImageOverlay^)managedOverlay;
nativeOverlay.Type = RecordingSourceType::Picture;
if (!String::IsNullOrEmpty(pictureOverlay->SourcePath)) {
if (pictureOverlay->SourceStream) {
nativeOverlay.SourceStream = new ManagedIStream(pictureOverlay->SourceStream);
hr = S_OK;
}
else if (!String::IsNullOrEmpty(pictureOverlay->SourcePath)) {
nativeOverlay.SourcePath = msclr::interop::marshal_as<std::wstring>(pictureOverlay->SourcePath);
hr = S_OK;
}
}
else if (isinst<VideoOverlay^>(managedOverlay)) {
VideoOverlay^ videoOverlay = (VideoOverlay^)managedOverlay;
nativeOverlay.Type = RecordingSourceType::Video;
if (!String::IsNullOrEmpty(videoOverlay->SourcePath)) {
if (videoOverlay->SourceStream) {
nativeOverlay.SourceStream = new ManagedIStream(videoOverlay->SourceStream);
hr = S_OK;
}
else if (!String::IsNullOrEmpty(videoOverlay->SourcePath)) {
nativeOverlay.SourcePath = msclr::interop::marshal_as<std::wstring>(videoOverlay->SourcePath);
hr = S_OK;
}
Expand Down Expand Up @@ -953,27 +975,19 @@ void Recorder::CreateFrameNumberCallback() {
}
void Recorder::EventComplete(std::wstring path, fifo_map<std::wstring, int> delays)
{
ClearCallbacks();
ReleaseResources();

List<FrameData^>^ frameInfos = gcnew List<FrameData^>();

for (auto x : delays) {
frameInfos->Add(gcnew FrameData(gcnew String(x.first.c_str()), x.second));
}
if (m_ManagedStream) {
delete m_ManagedStream;
m_ManagedStream = nullptr;
}
RecordingCompleteEventArgs^ args = gcnew RecordingCompleteEventArgs(gcnew String(path.c_str()), frameInfos);
OnRecordingComplete(this, args);
}
void Recorder::EventFailed(std::wstring error, std::wstring path)
{
ClearCallbacks();
if (m_ManagedStream) {
delete m_ManagedStream;
m_ManagedStream = nullptr;
}
ReleaseResources();
OnRecordingFailed(this, gcnew RecordingFailedEventArgs(gcnew String(error.c_str()), gcnew String(path.c_str())));
}
void Recorder::EventStatusChanged(int status)
Expand Down
3 changes: 2 additions & 1 deletion ScreenRecorderLib/Recorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ namespace ScreenRecorderLib {
void EventSnapshotCreated(std::wstring str);
void FrameNumberChanged(int newFrameNumber, INT64 timestamp);
void SetupCallbacks();
void ClearCallbacks();
void ReleaseCallbacks();
void ReleaseResources();
static HRESULT CreateNativeRecordingSource(_In_ RecordingSourceBase^ managedSource, _Out_ RECORDING_SOURCE* pNativeSource);
static HRESULT CreateNativeRecordingOverlay(_In_ RecordingOverlayBase^ managedOverlay, _Out_ RECORDING_OVERLAY* pNativeOverlay);
static List<VideoCaptureFormat^>^ CreateVideoCaptureFormatList(_In_ std::vector< IMFMediaType*> mediaTypes);
Expand Down
8 changes: 8 additions & 0 deletions ScreenRecorderLib/RecordingOverlays.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ namespace ScreenRecorderLib {
VideoOverlay(String^ path) {
SourcePath = path;
}
VideoOverlay(System::IO::Stream^ stream) {
SourceStream = stream;
}
property String^ SourcePath;
property System::IO::Stream^ SourceStream;
};
public ref class ImageOverlay :RecordingOverlayBase {
public:
Expand All @@ -125,7 +129,11 @@ namespace ScreenRecorderLib {
ImageOverlay(String^ path) {
SourcePath = path;
}
ImageOverlay(System::IO::Stream^ stream) {
SourceStream = stream;
}
property String^ SourcePath;
property System::IO::Stream^ SourceStream;
};
public ref class DisplayOverlay :RecordingOverlayBase {
private:
Expand Down
10 changes: 10 additions & 0 deletions ScreenRecorderLib/RecordingSources.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ namespace ScreenRecorderLib {
/// The file path to the video
/// </summary>
property String^ SourcePath;
property System::IO::Stream^ SourceStream;

VideoRecordingSource()
{
Expand All @@ -296,6 +297,10 @@ namespace ScreenRecorderLib {
}
VideoRecordingSource(VideoRecordingSource^ source) :RecordingSourceBase(source) {
SourcePath = source->SourcePath;
SourceStream = source->SourceStream;
}
VideoRecordingSource(System::IO::Stream^ stream) {
SourceStream = stream;
}
};

Expand All @@ -305,6 +310,7 @@ namespace ScreenRecorderLib {
/// The file path to the video
/// </summary>
property String^ SourcePath;
property System::IO::Stream^ SourceStream;

ImageRecordingSource()
{
Expand All @@ -315,6 +321,10 @@ namespace ScreenRecorderLib {
}
ImageRecordingSource(ImageRecordingSource^ source) :RecordingSourceBase(source) {
SourcePath = source->SourcePath;
SourceStream = source->SourceStream;
}
ImageRecordingSource(System::IO::Stream^ stream) {
SourceStream = stream;
}
};

Expand Down
14 changes: 13 additions & 1 deletion ScreenRecorderLibNative/CameraCapture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ HRESULT CameraCapture::InitializeSourceReader(
*ppMediaTransform = nullptr;
}
*pStreamIndex = 0;
HRESULT hr = S_OK;
HRESULT hr = E_FAIL;
CComPtr<IMFAttributes> pAttributes = nullptr;
CComPtr<IMFSourceReader> pSourceReader = nullptr;
EnterCriticalSection(&m_CriticalSection);
Expand Down Expand Up @@ -100,6 +100,18 @@ HRESULT CameraCapture::InitializeSourceReader(
return hr;
}

HRESULT CameraCapture::InitializeSourceReader(
_In_ IStream *pSourceStream,
_In_ std::optional<long> sourceFormatIndex,
_Out_ long *pStreamIndex,
_Outptr_ IMFSourceReader **ppSourceReader,
_Outptr_ IMFMediaType **ppInputMediaType,
_Outptr_opt_ IMFMediaType **ppOutputMediaType,
_Outptr_opt_result_maybenull_ IMFTransform **ppMediaTransform)
{
return E_NOTIMPL;
}

HRESULT CameraCapture::InitializeMediaSource(
_In_ CComPtr<IMFMediaSource> pSource,
_In_ std::optional<long> sourceFormatIndex,
Expand Down
9 changes: 9 additions & 0 deletions ScreenRecorderLibNative/CameraCapture.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ class CameraCapture : public SourceReaderBase
_Outptr_ IMFMediaType **ppInputMediaType,
_Outptr_opt_ IMFMediaType **ppOutputMediaType,
_Outptr_opt_result_maybenull_ IMFTransform **ppMediaTransform) override;

virtual HRESULT InitializeSourceReader(
_In_ IStream *pSourceStream,
_In_ std::optional<long> sourceFormatIndex,
_Out_ long *pStreamIndex,
_Outptr_ IMFSourceReader **ppSourceReader,
_Outptr_ IMFMediaType **ppInputMediaType,
_Outptr_opt_ IMFMediaType **ppOutputMediaType,
_Outptr_opt_result_maybenull_ IMFTransform **ppMediaTransform) override;
private:
HRESULT InitializeMediaSource(
_In_ CComPtr<IMFMediaSource> pDevice,
Expand Down
2 changes: 2 additions & 0 deletions ScreenRecorderLibNative/CommonTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ enum class RecordingSourceApi {

struct RECORDING_SOURCE_BASE abstract {
std::wstring SourcePath;
IStream *SourceStream;
HWND SourceWindow;
RecordingSourceType Type;
std::wstring ID;
Expand Down Expand Up @@ -227,6 +228,7 @@ struct RECORDING_SOURCE_BASE abstract {
Type(RecordingSourceType::Display),
SourceWindow(nullptr),
SourcePath(L""),
SourceStream(nullptr),
OutputSize{ std::nullopt },
ID(L""),
Stretch(TextureStretchMode::Uniform),
Expand Down
25 changes: 16 additions & 9 deletions ScreenRecorderLibNative/DX.util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,14 @@ HRESULT GetOutputRectsForRecordingSources(_In_ const std::vector<RECORDING_SOURC
}
case RecordingSourceType::Picture: {
SIZE size{};
std::string signature = ReadFileSignature(source->SourcePath.c_str());
std::string signature = "";

if (source->SourceStream) {
signature = ReadFileSignature(source->SourceStream);
}
else {
signature = ReadFileSignature(source->SourcePath.c_str());
}
ImageFileType imageType = getImageTypeByMagic(signature.c_str());
std::unique_ptr<CaptureBase> reader = nullptr;
if (imageType == ImageFileType::IMAGE_FILE_GIF) {
Expand All @@ -240,11 +247,11 @@ HRESULT GetOutputRectsForRecordingSources(_In_ const std::vector<RECORDING_SOURC
}
}
auto sortRect = [](const std::pair<RECORDING_SOURCE *, RECT> &p1, const std::pair<RECORDING_SOURCE *, RECT> &p2)
{
RECT r1 = p1.second;
RECT r2 = p2.second;
return std::tie(r1.left, r1.top) < std::tie(r2.left, r2.top);
};
{
RECT r1 = p1.second;
RECT r2 = p2.second;
return std::tie(r1.left, r1.top) < std::tie(r2.left, r2.top);
};
//Sort all sources according to leftmost and then topmost edge
std::sort(validOutputs.begin(), validOutputs.end(), sortRect);

Expand Down Expand Up @@ -389,9 +396,9 @@ void GetCombinedRects(_In_ std::vector<RECT> inputs, _Out_ RECT *pOutRect, _Out_
{
*pOutRect = RECT{};
auto sortRects = [](const RECT &r1, const RECT &r2)
{
return std::tie(r1.left, r1.top) < std::tie(r2.left, r2.top);
};
{
return std::tie(r1.left, r1.top) < std::tie(r2.left, r2.top);
};
std::sort(inputs.begin(), inputs.end(), sortRects);
if (pOffsets) {
*pOffsets = std::vector<SIZE>();
Expand Down
Loading

0 comments on commit e750c2e

Please sign in to comment.