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

Unity Video Capture backend #56

Merged
merged 19 commits into from
Apr 19, 2021
Merged

Unity Video Capture backend #56

merged 19 commits into from
Apr 19, 2021

Conversation

graphemecluster
Copy link
Contributor

@graphemecluster graphemecluster commented Apr 18, 2021

(Discussion: #51)

Many of the files are rewrote for this pull request. Just as you said, I'm not including the install scripts though the shared.inl is necessary. And I'm finally not going to touch the UnityCaptureFilter.cpp file.

Looking at the source code of UnityCaptureFilter.cpp:

  • Yes, the conversion of RGBA to BGRA inside the file is unavoidable without modify it.
  • A maximum number of 74 devices can be installed.
  • The devices are named name #index except for index 0, which is named name. Yes, the default value of name is Unity Video Capture, but it can be changed by the user by the install script argument UnityCaptureName. The number of devices installed is specified by the argument UnityCaptureDevices with the default value 1. All devices have different CLSID in the format 5C2CD55C-92AD-4999-8666-912BD3E700XX.

With the above information:

  • We parse the device parameter by the following condition:
  • If device is missing, we iterate through 0 to 74 to check for the first device registered;
  • If device is a number-like string from 0 to 74, the index is used;
  • Otherwise the device is assumed to be the device name; we iterate through 0 to 74 to check if device matches the name given by the register key; or an error is fired if there are no matches.
  • The device() function returns the device name given by the register key, not necessary Unity Video Capture.

A few more things to notice:

  • I think it won't works on a notable number of Windows devices due to the RegGetValueW method.
  • The red-blue channels are not switched at least for my device in the final display.
  • I am not changing argb to bgra in image_formats.h due to consistency, but of course you can if you want.
  • All uint32_t are changed to int32_t, though only the argb_to_argb method has actual use to negative numbers.
  • I used std::wstring instead of std::string.
  • The fps parameter is not used currently.
  • I don't know if we also need to include the shared.inl file in setup.py.
  • I think it is completed but I'm not sure.

Please let me know if there's anything unclear or forgotten.

@letmaik
Copy link
Owner

letmaik commented Apr 18, 2021

Looks great! Do you mind if I commit to your branch and fix up the remaining issues?

@graphemecluster
Copy link
Contributor Author

graphemecluster commented Apr 18, 2021

By all means! I'm trying to fix it too.
Edit: Although the errors are fixed, be sure to check if it is completed.

@graphemecluster
Copy link
Contributor Author

graphemecluster commented Apr 18, 2021

If the RegGetValueW method does not work at all, try replacing

if (RegGetValueW(HKEY_CLASSES_ROOT, guid, L"", RRF_RT_REG_SZ, NULL, &str, &size) != ERROR_SUCCESS) return false;

with the following:

HKEY key;
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, guid, 0, KEY_READ, &key) != ERROR_SUCCESS) return false;
if (RegQueryValueExW(key, L"", NULL, NULL, (LPBYTE)str, &size) != ERROR_SUCCESS) return false;

I don’t know which one is better if both work though.

@letmaik
Copy link
Owner

letmaik commented Apr 18, 2021

I did the following changes:

  • For now, let's not use unicode. This has to be done across all backends, and in theory it makes sense, but should be a separate PR.
  • I renamed some bits to be more consistent. I think I'll rename the folder as well, to native_windows_unity_capture.
  • SendIsReady, as you had it earlier, unfortunately has to go into send() as it will always be false if no app is capturing the cam yet.

I'll do some more testing etc. later today, also it has to be added to CI.
The color is still wrong for me. Can you try in multiple apps? The latency.py sample gives you a good idea of the expected color (see terminal output).

  • Some minor fixes and cleanup.

@graphemecluster
Copy link
Contributor Author

Note that std::stoi() return 3 for strings like 3DCamera, which is not expected.

@letmaik
Copy link
Owner

letmaik commented Apr 18, 2021

Note that std::stoi() return 3 for strings like 3DCamera, which is not expected.

You're right, didn't think of that. It may make sense to remove the numeric case altogether for now since it becomes ambiguous if multiple backends do the same. It probably makes more sense for each backend to expose all available devices and then let the user choose, based on some criteria, but that's out of scope in this PR.

@graphemecluster
Copy link
Contributor Author

OK, let’s remove it then.
Oh, I didn’t edit the README either.

@letmaik
Copy link
Owner

letmaik commented Apr 18, 2021

I've added the proper pixel format conversions now and it looks fine for me. Could you give it a try? If it doesn't look right for you, then your frames are probably not in RGBA channel order. This would be fully opaque red: [255, 0, 0, 255]

@letmaik
Copy link
Owner

letmaik commented Apr 19, 2021

I just stumbled upon these:
obsproject/obs-studio#4476
https://stackoverflow.com/q/66854423
Once we're done here those two should get a follow-up response.

@letmaik
Copy link
Owner

letmaik commented Apr 19, 2021

I updated the README and added a new sample which sends transparent gif frames to the cam. I included some instructions on how to configure OBS to capture in RGBA.
From my side I'm done now. Would be good if you could give it a final test before I merge it. Note that you can also download the wheels from the artifacts here: https://github.com/letmaik/pyvirtualcam/suites/2528611452/artifacts/54812494

@@ -58,7 +58,7 @@ struct SharedImageMemory
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);
ReleaseMutex(m_hMutex); //unlock mutex

return RECEIVERES_NEWFRAME;
return (IsNewFrame ? RECEIVERES_NEWFRAME : RECEIVERES_OLDFRAME);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this line, the timeout should be set to a large enough number (previously I set it to 100000000) in milliseconds or the frame will be cleared immediately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just don't want the frame being cleared even after a day, but that's up to you.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the receiver, so there's no point changing any code in it as it won't be used by pyvirtualcam but rather by the filter DLL that you install. I'll have a look at the timeout value on the sender side, it's a fair point.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah.. the timeout definitely needs to be higher, otherwise low framerates also lead to interruptions. I've changed it to 1 day.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The receive method is used here and this is the only place which prints "Unity has stopped sending image data". That's OK if you think 1 day is enough (though I want to keep the frame forever).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The receive method is only used in the filter. I can remove the receive method in the backend and everything would compile fine. Do you see? Any change in that method does not have an effect on our side. The shared.inl is a header which gets pulled in both by the receiver (filter) and sender (us). Some code is only used by one of both.

What's your use case for keeping a frame longer than a day? I'm happy to make the timeout longer but would like to understand a bit more.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout is of type int (signed 32 bit) and since it's in milliseconds the maximum you can get is something like 24 days.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to the maximum now :)
d03b9a6

Copy link
Contributor Author

@graphemecluster graphemecluster Apr 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, no special reason – just think that I am a perfectionist.
Anyway, the point is that this is actually a feature written in README.md of UnityCapture. So I am opening an issue to due with it.
schellingb/UnityCapture#18

@graphemecluster
Copy link
Contributor Author

Sorry for letting you wait. I am in a different time zone from you.
Currently there are some problems to my computer so I am not able to test it.
It should work on most computers without my test.
Thank you for your patience.

@letmaik
Copy link
Owner

letmaik commented Apr 19, 2021

I found an issue when using multiple cameras which requires a fix upstream schellingb/UnityCapture#17.

@letmaik letmaik merged commit 6218473 into letmaik:master Apr 19, 2021
@letmaik letmaik changed the title Unity Video Capture Unity Video Capture backend Apr 19, 2021
@graphemecluster
Copy link
Contributor Author

For now, let's not use unicode. This has to be done across all backends, and in theory it makes sense, but should be a separate PR.

So should all of them be changed to wide-characters now? This is a minor problem though.

@letmaik
Copy link
Owner

letmaik commented Apr 19, 2021

For now, let's not use unicode. This has to be done across all backends, and in theory it makes sense, but should be a separate PR.

So should all of them be changed to wide-characters now? This is a minor problem though.

Sure, I won't have time for it though. So far, all the virtual cameras didn't need it as the names are all ascii. And I have a feeling that probably no one is using custom names for unity capture. Do you have a use case?

@graphemecluster
Copy link
Contributor Author

For now, let's not use unicode. This has to be done across all backends, and in theory it makes sense, but should be a separate PR.

So should all of them be changed to wide-characters now? This is a minor problem though.

Sure, I won't have time for it though. So far, all the virtual cameras didn't need it as the names are all ascii. And I have a feeling that probably no one is using custom names for unity capture. Do you have a use case?

I think so too. Actually I don't need it either, so let's just put it aside.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants