Skip to content

Commit a82e2a0

Browse files
committed
- Added timeout option to control the time until sending is considered stopped
- Support capturing of mip-mapped textures
1 parent 6a0a80c commit a82e2a0

File tree

9 files changed

+27
-16
lines changed

9 files changed

+27
-16
lines changed
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ can refer to the 'UnityCaptureTextureExample' scene and the 'CaptureTexture' scr
6060
There are a few settings for the 'Unity Capture' behavior.
6161

6262
- 'Capture Device': Set the capture device filter number (only relevant when multiple capture devices were [installed](#installation))
63+
- 'Timeout': Sets how many milliseconds to wait for a new frame until sending is considered to be stopped
64+
If rendering every frame this can be very low. Default is 1000 to allow stalls due to loading, etc.
65+
When set to 0 the image will stay up even when Unity is ended (until the receiving application also ends).
6366
- 'Resize Mode': It is suggested to leave this disabled and just let your capture target application handle the display
6467
sizing/resizing because this setting can introduce frame skipping. So far only a very basic linear resize is supported.
6568
- 'Mirror Mode': This setting should also be handled by your target application if possible and needed, but it is available.

Source/UnityCaptureFilter.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ class CCaptureStream : CSourceStream, IKsPropertySet, IAMStreamConfig, IAMStream
153153
break;
154154

155155
case SharedImageMemory::RECEIVERES_OLDFRAME:{
156-
if (m_llFrameMissCount++ < 5) break;
157-
//Show color pattern when having 5 frames without new image (probably Unity stopped sending data)
156+
if (++m_llFrameMissCount < m_llFrameMissMax) break;
157+
//Show color pattern when received more than X frames without new image (probably Unity stopped sending data)
158158
char DisplayString[] = "Unity has stopped sending image data", *DisplayStrings[] = { DisplayString };
159159
int DisplayStringLens[] = { sizeof(DisplayString) - 1 };
160160
FillErrorPattern(ErrorDrawModes[EDC_UnitySendingStopped], &State, 1, DisplayStrings, DisplayStringLens, m_llFrame);
@@ -454,8 +454,11 @@ class CCaptureStream : CSourceStream, IKsPropertySet, IAMStreamConfig, IAMStream
454454
CCaptureStream* Owner;
455455
};
456456

457-
static void ProcessImage(int InWidth, int InHeight, int InStride, SharedImageMemory::EFormat Format, SharedImageMemory::EResizeMode ResizeMode, SharedImageMemory::EMirrorMode MirrorMode, uint8_t* InBuf, ProcessState* State)
457+
static void ProcessImage(int InWidth, int InHeight, int InStride, SharedImageMemory::EFormat Format, SharedImageMemory::EResizeMode ResizeMode, SharedImageMemory::EMirrorMode MirrorMode, int Timeout, uint8_t* InBuf, ProcessState* State)
458458
{
459+
//Set maximum number of missed frames allowed until we show sending as having stopped
460+
State->Owner->m_llFrameMissMax = (Timeout + SharedImageMemory::RECEIVE_MAX_WAIT - 1) / SharedImageMemory::RECEIVE_MAX_WAIT;
461+
459462
const bool NeedResize = (InWidth != State->BufWidth || InHeight != State->BufHeight);
460463
if (NeedResize && ResizeMode == SharedImageMemory::RESIZEMODE_DISABLED)
461464
{
@@ -805,11 +808,12 @@ class CCaptureStream : CSourceStream, IKsPropertySet, IAMStreamConfig, IAMStream
805808
{
806809
DebugLog("[OnThreadStartPlay] OnThreadStartPlay\n");
807810
m_llFrame = m_llFrameMissCount = 0;
811+
m_llFrameMissMax = 5;
808812
return CSourceStream::OnThreadStartPlay();
809813
}
810814

811815
CMediaType m_mt;
812-
LONGLONG m_llFrame, m_llFrameMissCount;
816+
LONGLONG m_llFrame, m_llFrameMissCount, m_llFrameMissMax;
813817
REFERENCE_TIME m_prevStartTime;
814818
REFERENCE_TIME m_avgTimePerFrame;
815819
SharedImageMemory* m_pReceiver;

Source/UnityCapturePlugin.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ extern "C" __declspec(dllexport) void CaptureDeleteInstance(UnityCaptureInstance
7171
delete c;
7272
}
7373

74-
extern "C" __declspec(dllexport) int CaptureSendTexture(UnityCaptureInstance* c, void* TextureNativePtr, bool UseDoubleBuffering, SharedImageMemory::EResizeMode ResizeMode, SharedImageMemory::EMirrorMode MirrorMode, bool IsLinearColorSpace)
74+
extern "C" __declspec(dllexport) int CaptureSendTexture(UnityCaptureInstance* c, void* TextureNativePtr, int Timeout, bool UseDoubleBuffering, SharedImageMemory::EResizeMode ResizeMode, SharedImageMemory::EMirrorMode MirrorMode, bool IsLinearColorSpace)
7575
{
7676
if (!c || !TextureNativePtr) return RET_ERROR_PARAMETER;
7777
if (g_GraphicsDeviceType != kUnityGfxRendererD3D11) return RET_ERROR_UNSUPPORTEDGRAPHICSDEVICE;
@@ -95,7 +95,7 @@ extern "C" __declspec(dllexport) int CaptureSendTexture(UnityCaptureInstance* c,
9595
ZeroMemory(&textureDesc, sizeof(textureDesc));
9696
textureDesc.Width = desc.Width;
9797
textureDesc.Height = desc.Height;
98-
textureDesc.MipLevels = 1;
98+
textureDesc.MipLevels = desc.MipLevels;
9999
textureDesc.ArraySize = 1;
100100
textureDesc.Format = desc.Format;
101101
textureDesc.SampleDesc.Count = 1;
@@ -128,10 +128,10 @@ extern "C" __declspec(dllexport) int CaptureSendTexture(UnityCaptureInstance* c,
128128
//Copy render texture to texture with CPU access and map the image data to RAM
129129
ctx->CopyResource(WriteTexture, d3dtex);
130130
D3D11_MAPPED_SUBRESOURCE mapResource;
131-
if (FAILED(ctx->Map(ReadTexture, 0, D3D11_MAP_READ, NULL, &mapResource))) return RET_ERROR_READTEXTURE;
131+
if (FAILED(ctx->Map(ReadTexture, 0, D3D11_MAP_READ, 0, &mapResource))) return RET_ERROR_READTEXTURE;
132132

133133
//Push the captured data to the direct show filter
134-
SharedImageMemory::ESendResult res = c->Sender->Send(desc.Width, desc.Height, mapResource.RowPitch / (Format == SharedImageMemory::FORMAT_UINT8 ? 4 : 8), mapResource.RowPitch * desc.Height, Format, ResizeMode, MirrorMode, (const unsigned char*)mapResource.pData);
134+
SharedImageMemory::ESendResult res = c->Sender->Send(desc.Width, desc.Height, mapResource.RowPitch / (Format == SharedImageMemory::FORMAT_UINT8 ? 4 : 8), mapResource.RowPitch * desc.Height, Format, ResizeMode, MirrorMode, Timeout, (const unsigned char*)mapResource.pData);
135135

136136
ctx->Unmap(ReadTexture, 0);
137137

Source/shared.inl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,23 @@ struct SharedImageMemory
3939

4040
int32_t GetCapNum() { return m_CapNum; }
4141
enum { MAX_CAPNUM = ('z' - '0') }; //see Open() for why this number
42+
enum { RECEIVE_MAX_WAIT = 200 }; //How many milliseconds to wait for new frame
4243
enum EFormat { FORMAT_UINT8, FORMAT_FP16_GAMMA, FORMAT_FP16_LINEAR };
4344
enum EResizeMode { RESIZEMODE_DISABLED = 0, RESIZEMODE_LINEAR = 1 };
4445
enum EMirrorMode { MIRRORMODE_DISABLED = 0, MIRRORMODE_HORIZONTALLY = 1 };
4546
enum EReceiveResult { RECEIVERES_CAPTUREINACTIVE, RECEIVERES_NEWFRAME, RECEIVERES_OLDFRAME };
4647

47-
typedef void (*ReceiveCallbackFunc)(int width, int height, int stride, EFormat format, EResizeMode resizemode, EMirrorMode mirrormode, uint8_t* buffer, void* callback_data);
48+
typedef void (*ReceiveCallbackFunc)(int width, int height, int stride, EFormat format, EResizeMode resizemode, EMirrorMode mirrormode, int timeout, uint8_t* buffer, void* callback_data);
4849

4950
EReceiveResult Receive(ReceiveCallbackFunc callback, void* callback_data)
5051
{
5152
if (!Open(true) || !m_pSharedBuf->width) return RECEIVERES_CAPTUREINACTIVE;
5253

5354
SetEvent(m_hWantFrameEvent);
54-
bool IsNewFrame = (WaitForSingleObject(m_hSentFrameEvent, 200) == WAIT_OBJECT_0);
55+
bool IsNewFrame = (WaitForSingleObject(m_hSentFrameEvent, RECEIVE_MAX_WAIT) == WAIT_OBJECT_0);
5556

5657
WaitForSingleObject(m_hMutex, INFINITE); //lock mutex
57-
callback(m_pSharedBuf->width, m_pSharedBuf->height, m_pSharedBuf->stride, (EFormat)m_pSharedBuf->format, (EResizeMode)m_pSharedBuf->resizemode, (EMirrorMode)m_pSharedBuf->mirrormode, m_pSharedBuf->data, callback_data);
58+
callback(m_pSharedBuf->width, m_pSharedBuf->height, m_pSharedBuf->stride, (EFormat)m_pSharedBuf->format, (EResizeMode)m_pSharedBuf->resizemode, (EMirrorMode)m_pSharedBuf->mirrormode, m_pSharedBuf->timeout, m_pSharedBuf->data, callback_data);
5859
ReleaseMutex(m_hMutex); //unlock mutex
5960

6061
return (IsNewFrame ? RECEIVERES_NEWFRAME : RECEIVERES_OLDFRAME);
@@ -66,7 +67,7 @@ struct SharedImageMemory
6667
}
6768

6869
enum ESendResult { SENDRES_TOOLARGE, SENDRES_WARN_FRAMESKIP, SENDRES_OK };
69-
ESendResult Send(int width, int height, int stride, DWORD DataSize, EFormat format, EResizeMode resizemode, EMirrorMode mirrormode, const uint8_t* buffer)
70+
ESendResult Send(int width, int height, int stride, DWORD DataSize, EFormat format, EResizeMode resizemode, EMirrorMode mirrormode, int timeout, const uint8_t* buffer)
7071
{
7172
UCASSERT(buffer);
7273
UCASSERT(m_pSharedBuf);
@@ -79,6 +80,7 @@ struct SharedImageMemory
7980
m_pSharedBuf->format = format;
8081
m_pSharedBuf->resizemode = resizemode;
8182
m_pSharedBuf->mirrormode = mirrormode;
83+
m_pSharedBuf->timeout = timeout;
8284
memcpy(m_pSharedBuf->data, buffer, DataSize);
8385
ReleaseMutex(m_hMutex); //unlock mutex
8486

@@ -150,6 +152,7 @@ private:
150152
int format;
151153
int resizemode;
152154
int mirrormode;
155+
int timeout;
153156
uint8_t data[1];
154157
};
155158

Binary file not shown.
Binary file not shown.

UnityCaptureSample/Assets/UnityCapture/UnityCapture.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public enum ECaptureSendResult { SUCCESS = 0, WARNING_FRAMESKIP = 1, WARNING_CAP
3838

3939
[SerializeField] [Tooltip("Capture device index")] public ECaptureDevice CaptureDevice = ECaptureDevice.CaptureDevice1;
4040
[SerializeField] [Tooltip("Scale image if Unity and capture resolution don't match (can introduce frame dropping, not recommended)")] public EResizeMode ResizeMode = EResizeMode.Disabled;
41+
[SerializeField] [Tooltip("How many milliseconds to wait for a new frame until sending is considered to be stopped")] public int Timeout = 1000;
4142
[SerializeField] [Tooltip("Mirror captured output image")] public EMirrorMode MirrorMode = EMirrorMode.Disabled;
4243
[SerializeField] [Tooltip("Introduce a frame of latency in favor of frame rate")] public bool DoubleBuffering = false;
4344
[SerializeField] [Tooltip("Check to enable VSync during capturing")] public bool EnableVSync = false;
@@ -71,7 +72,7 @@ void OnDestroy()
7172
void OnRenderImage(RenderTexture source, RenderTexture destination)
7273
{
7374
Graphics.Blit(source, destination);
74-
switch (CaptureInterface.SendTexture(source, DoubleBuffering, ResizeMode, MirrorMode))
75+
switch (CaptureInterface.SendTexture(source, Timeout, DoubleBuffering, ResizeMode, MirrorMode))
7576
{
7677
case ECaptureSendResult.SUCCESS: break;
7778
case ECaptureSendResult.WARNING_FRAMESKIP: if (!HideWarnings) Debug.LogWarning("[UnityCapture] Capture device did skip a frame read, capture frame rate will not match render frame rate."); break;
@@ -89,7 +90,7 @@ public class Interface
8990
{
9091
[System.Runtime.InteropServices.DllImport("UnityCapturePlugin")] extern static System.IntPtr CaptureCreateInstance(int CapNum);
9192
[System.Runtime.InteropServices.DllImport("UnityCapturePlugin")] extern static void CaptureDeleteInstance(System.IntPtr instance);
92-
[System.Runtime.InteropServices.DllImport("UnityCapturePlugin")] extern static ECaptureSendResult CaptureSendTexture(System.IntPtr instance, System.IntPtr nativetexture, bool UseDoubleBuffering, EResizeMode ResizeMode, EMirrorMode MirrorMode, bool IsLinearColorSpace);
93+
[System.Runtime.InteropServices.DllImport("UnityCapturePlugin")] extern static ECaptureSendResult CaptureSendTexture(System.IntPtr instance, System.IntPtr nativetexture, int Timeout, bool UseDoubleBuffering, EResizeMode ResizeMode, EMirrorMode MirrorMode, bool IsLinearColorSpace);
9394
System.IntPtr CaptureInstance;
9495

9596
public Interface(ECaptureDevice CaptureDevice)
@@ -108,10 +109,10 @@ public void Close()
108109
CaptureInstance = System.IntPtr.Zero;
109110
}
110111

111-
public ECaptureSendResult SendTexture(Texture Source, bool DoubleBuffering = false, EResizeMode ResizeMode = EResizeMode.Disabled, EMirrorMode MirrorMode = EMirrorMode.Disabled)
112+
public ECaptureSendResult SendTexture(Texture Source, int Timeout = 1000, bool DoubleBuffering = false, EResizeMode ResizeMode = EResizeMode.Disabled, EMirrorMode MirrorMode = EMirrorMode.Disabled)
112113
{
113114
if (CaptureInstance == System.IntPtr.Zero) return ECaptureSendResult.ERROR_INVALIDCAPTUREINSTANCEPTR;
114-
return CaptureSendTexture(CaptureInstance, Source.GetNativeTexturePtr(), DoubleBuffering, ResizeMode, MirrorMode, QualitySettings.activeColorSpace == ColorSpace.Linear);
115+
return CaptureSendTexture(CaptureInstance, Source.GetNativeTexturePtr(), Timeout, DoubleBuffering, ResizeMode, MirrorMode, QualitySettings.activeColorSpace == ColorSpace.Linear);
115116
}
116117
}
117118
}

0 commit comments

Comments
 (0)