capture audio and video in sunshine

This commit is contained in:
pigeatgarlic 2025-08-15 14:00:38 +07:00
parent baa0cc1e54
commit fe9dbd3352
13 changed files with 140 additions and 131 deletions

View File

@ -1,4 +1,7 @@
#define QUEUE_SIZE 64
#define OUT_QUEUE_SIZE 8
#define IN_QUEUE_SIZE 8
#define TAG_SIZE 8192
#define PACKET_SIZE 5 * 1024 * 1024
typedef struct {
@ -21,9 +24,20 @@ typedef struct {
typedef struct _Queue{
int inindex;
int outindex;
void* handle;
QueueMetadata metadata;
Packet incoming[IN_QUEUE_SIZE];
Packet outgoing[OUT_QUEUE_SIZE];
}Queue;
Packet incoming[QUEUE_SIZE];
Packet outcoming[QUEUE_SIZE];
}Queue;
typedef struct _DisplayQueue{
Queue internal;
QueueMetadata metadata;
}DisplayQueue;
typedef struct _Memory {
DisplayQueue video;
Queue audio;
Queue data;
char worker_info[TAG_SIZE];
}Memory;

View File

@ -30,7 +30,6 @@ IVSHMEM::IVSHMEM() :
m_initialized(false),
m_handle(INVALID_HANDLE_VALUE),
m_gotSize(false),
m_gotPeerID(false),
m_gotMemory(false)
{
@ -112,7 +111,7 @@ void IVSHMEM::DeInitialize()
if (m_gotMemory)
{
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_RELEASE_MMAP, NULL, 0, NULL, 0, NULL, NULL))
printf("DeviceIoControl failed: %d", (int)GetLastError());
printf("Deintialize DeviceIoControl failed: %d", (int)GetLastError());
m_memory = NULL;
}
@ -122,7 +121,6 @@ void IVSHMEM::DeInitialize()
m_initialized = false;
m_handle = INVALID_HANDLE_VALUE;
m_gotSize = false;
m_gotPeerID = false;
m_gotMemory = false;
}
@ -142,7 +140,7 @@ UINT64 IVSHMEM::GetSize()
IVSHMEM_SIZE size;
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_SIZE, NULL, 0, &size, sizeof(IVSHMEM_SIZE), NULL, NULL))
{
printf("DeviceIoControl Failed: %d", (int)GetLastError());
printf("GetSize DeviceIoControl Failed: %d", (int)GetLastError());
return 0;
}
@ -151,25 +149,7 @@ UINT64 IVSHMEM::GetSize()
return m_size;
}
UINT16 IVSHMEM::GetPeerID()
{
if (!m_initialized)
return 0;
if (m_gotPeerID)
return m_peerID;
IVSHMEM_PEERID peerID;
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_SIZE, NULL, 0, &peerID, sizeof(IVSHMEM_PEERID), NULL, NULL))
{
printf("DeviceIoControl Failed: %d", (int)GetLastError());
return 0;
}
m_gotPeerID = true;
m_peerID = static_cast<UINT16>(peerID);
return m_peerID;
}
void * IVSHMEM::GetMemory()
@ -201,15 +181,13 @@ void * IVSHMEM::GetMemory()
&map , sizeof(IVSHMEM_MMAP ),
NULL, NULL))
{
printf("DeviceIoControl Failed: %d", (int)GetLastError());
printf("GetMemory DeviceIoControl Failed: %d", (int)GetLastError());
return NULL;
}
m_gotSize = true;
m_gotPeerID = true;
m_gotMemory = true;
m_size = static_cast<UINT64>(map.size );
m_peerID = static_cast<UINT16>(map.peerID );
m_memory = map.ptr;
return m_memory;

View File

@ -110,7 +110,6 @@ public:
bool IsInitialized();
UINT64 GetSize();
UINT16 GetPeerID();
void * GetMemory();
HANDLE getHandle();
@ -122,6 +121,5 @@ private:
bool m_initialized;
HANDLE m_handle;
UINT64 m_size ; bool m_gotSize ;
UINT16 m_peerID ; bool m_gotPeerID;
void * m_memory ; bool m_gotMemory;
};

View File

@ -137,71 +137,64 @@ main(int argc, char *argv[]) {
ivshmem->DeInitialize();
});
int queuetype = QueueType::Video;
std::stringstream ss0; ss0 << argv[1];
std::string target; ss0 >> target;
if (target == "audio")
queuetype = QueueType::Audio;
int codec = 0;
if (argc == 3) {
std::stringstream ss1; ss1 << argv[2];
std::string codecs; ss1 >> codecs;
if (codecs == "h265")
codec = 1;
else if (codecs == "av1")
codec = 2;
}
std::stringstream ss1; ss1 << argv[1];
std::string codecs; ss1 >> codecs;
if (codecs == "h265")
codec = 1;
else if (codecs == "av1")
codec = 2;
if(queuetype == QueueType::Video) {
#ifdef _WIN32
// Modify relevant NVIDIA control panel settings if the system has corresponding gpu
if (nvprefs_instance.load()) {
// Restore global settings to the undo file left by improper termination of sunshine.exe
nvprefs_instance.restore_from_and_delete_undo_file_if_exists();
// Modify application settings for sunshine.exe
nvprefs_instance.modify_application_profile();
// Modify global settings, undo file is produced in the process to restore after improper termination
nvprefs_instance.modify_global_profile();
// Unload dynamic library to survive driver re-installation
nvprefs_instance.unload();
}
// Wait as long as possible to terminate Sunshine.exe during logoff/shutdown
SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);
// We must create a hidden window to receive shutdown notifications since we load gdi32.dll
std::promise<HWND> session_monitor_hwnd_promise;
auto session_monitor_hwnd_future = session_monitor_hwnd_promise.get_future();
std::promise<void> session_monitor_join_thread_promise;
auto session_monitor_join_thread_future = session_monitor_join_thread_promise.get_future();
#endif
// Modify relevant NVIDIA control panel settings if the system has corresponding gpu
if (nvprefs_instance.load()) {
// Restore global settings to the undo file left by improper termination of sunshine.exe
nvprefs_instance.restore_from_and_delete_undo_file_if_exists();
// Modify application settings for sunshine.exe
nvprefs_instance.modify_application_profile();
// Modify global settings, undo file is produced in the process to restore after improper termination
nvprefs_instance.modify_global_profile();
// Unload dynamic library to survive driver re-installation
nvprefs_instance.unload();
}
// Wait as long as possible to terminate Sunshine.exe during logoff/shutdown
SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);
// We must create a hidden window to receive shutdown notifications since we load gdi32.dll
std::promise<HWND> session_monitor_hwnd_promise;
auto session_monitor_hwnd_future = session_monitor_hwnd_promise.get_future();
std::promise<void> session_monitor_join_thread_promise;
auto session_monitor_join_thread_future = session_monitor_join_thread_promise.get_future();
auto platf_deinit_guard = platf::init();
if (ivshmem->GetSize() != (UINT64)sizeof(Queue)) {
BOOST_LOG(error) << "Invalid ivshmem size: "sv << ivshmem->GetSize();
Memory* memory = NULL;
if (ivshmem->GetSize() < (UINT64)sizeof(Memory)) {
BOOST_LOG(error) << "Invalid ivshmem size: "sv << ivshmem->GetSize();
BOOST_LOG(error) << "Expected ivshmem size: "sv << sizeof(Memory);
memory = (Memory*)malloc(sizeof(Memory));
} else {
BOOST_LOG(info) << "Found ivshmem shared memory"sv;
memory = (Memory*)ivshmem->GetMemory();
}
if (memory == NULL) {
BOOST_LOG(error) << "Failed to allocate shared memory"sv;
return StatusCode::INVALID_IVSHMEM;
}
auto queue = (Queue*)ivshmem->GetMemory();
if (!platf_deinit_guard) {
BOOST_LOG(error) << "Platform failed to initialize"sv;
return StatusCode::NO_ENCODER_AVAILABLE;
} else if(queuetype == QueueType::Video && video::probe_encoders()) {
} else if(video::probe_encoders()) {
BOOST_LOG(error) << "Video failed to find working encoder"sv;
return StatusCode::NO_ENCODER_AVAILABLE;
} else if (queue == nullptr) {
BOOST_LOG(error) << "Failed to find shared memory"sv;
queue = (Queue*)malloc(sizeof(Queue));
}
memset(queue,0,sizeof(Queue));
auto video_capture = [&](safe::mail_t mail, std::string displayin,int codec){
video::capture(mail,video::config_t{
displayin, 1920, 1080, 60, 6000, 1, 0, 1, codec, 0
@ -218,7 +211,7 @@ main(int argc, char *argv[]) {
auto mail = std::make_shared<safe::mail_raw_t>();
auto pull = [process_shutdown_event,queue,mail](){
auto pull = [process_shutdown_event,mail](Queue* queue){
auto timer = platf::create_high_precision_timer();
auto local_shutdown= mail->event<bool>(mail::shutdown);
auto bitrate = mail->event<int>(mail::bitrate);
@ -233,12 +226,12 @@ main(int argc, char *argv[]) {
timer->sleep_for(1ms);
memcpy(buffer,
queue->outcoming[expected_index].data,
queue->outcoming[expected_index].size
queue->outgoing[expected_index].data,
queue->outgoing[expected_index].size
);
expected_index++;
if (expected_index >= QUEUE_SIZE)
if (expected_index >= OUT_QUEUE_SIZE)
expected_index = 0;
switch (buffer[0]) {
@ -281,7 +274,7 @@ main(int argc, char *argv[]) {
};
auto push = [process_shutdown_event](safe::mail_t mail, Queue* queue, QueueType queue_type){
auto push_video = [process_shutdown_event](safe::mail_t mail, Queue* queue){
auto video_packets = mail->queue<video::packet_t>(mail::video_packets);
auto audio_packets = mail->queue<audio::packet_t>(mail::audio_packets);
auto local_shutdown= mail->event<bool>(mail::shutdown);
@ -294,54 +287,70 @@ main(int argc, char *argv[]) {
uint32_t findex = 0;
while (!process_shutdown_event->peek() && !local_shutdown->peek()) {
if (queue_type == QueueType::Video) {
do {
auto packet = video_packets->pop();
char* ptr = (char*)packet->data();
size_t size = packet->data_size();
uint64_t utimestamp = packet->frame_timestamp.value().time_since_epoch().count();
do {
auto packet = video_packets->pop();
char* ptr = (char*)packet->data();
size_t size = packet->data_size();
uint64_t utimestamp = packet->frame_timestamp.value().time_since_epoch().count();
auto updated = queue->inindex + 1;
if (updated >= QUEUE_SIZE)
updated = 0;
auto updated = queue->inindex + 1;
if (updated >= IN_QUEUE_SIZE)
updated = 0;
findex++;
uint16_t sum = 0;
queue->incoming[updated].size = 0;
copy_to_packet(&queue->incoming[updated],&findex,sizeof(uint32_t));
copy_to_packet(&queue->incoming[updated],&utimestamp,sizeof(uint64_t));
copy_to_packet(&queue->incoming[updated],&sum,sizeof(uint16_t));
copy_to_packet(&queue->incoming[updated],ptr,size);
queue->inindex = updated;
} while (video_packets->peek());
} else if (queue_type == QueueType::Audio) {
do {
auto packet = audio_packets->pop();
char* ptr = (char*)packet->second.begin();
size_t size = packet->second.size();
uint64_t utimestamp = std::chrono::steady_clock::now().time_since_epoch().count();
auto updated = queue->inindex + 1;
if (updated >= QUEUE_SIZE)
updated = 0;
findex++;
uint16_t sum = 0;
queue->incoming[updated].size = 0;
copy_to_packet(&queue->incoming[updated],&findex,sizeof(uint32_t));
copy_to_packet(&queue->incoming[updated],&utimestamp,sizeof(uint64_t));
copy_to_packet(&queue->incoming[updated],&sum,sizeof(uint16_t));
copy_to_packet(&queue->incoming[updated],ptr,size);
queue->inindex = updated;
} while (audio_packets->peek());
}
findex++;
uint16_t sum = 0;
queue->incoming[updated].size = 0;
copy_to_packet(&queue->incoming[updated],&findex,sizeof(uint32_t));
copy_to_packet(&queue->incoming[updated],&utimestamp,sizeof(uint64_t));
copy_to_packet(&queue->incoming[updated],&sum,sizeof(uint16_t));
copy_to_packet(&queue->incoming[updated],ptr,size);
queue->inindex = updated;
} while (video_packets->peek());
}
if (!local_shutdown->peek())
local_shutdown->raise(true);
};
auto touch_fun = [mail,process_shutdown_event](Queue* queue){
auto push_audio = [process_shutdown_event](safe::mail_t mail, Queue* queue){
auto video_packets = mail->queue<video::packet_t>(mail::video_packets);
auto audio_packets = mail->queue<audio::packet_t>(mail::audio_packets);
auto local_shutdown= mail->event<bool>(mail::shutdown);
auto touch_port = mail->event<input::touch_port_t>(mail::touch_port);
#ifdef _WIN32
platf::adjust_thread_priority(platf::thread_priority_e::critical);
#endif
uint32_t findex = 0;
while (!process_shutdown_event->peek() && !local_shutdown->peek()) {
do {
auto packet = audio_packets->pop();
char* ptr = (char*)packet->second.begin();
size_t size = packet->second.size();
uint64_t utimestamp = std::chrono::steady_clock::now().time_since_epoch().count();
auto updated = queue->inindex + 1;
if (updated >= IN_QUEUE_SIZE)
updated = 0;
findex++;
uint16_t sum = 0;
queue->incoming[updated].size = 0;
copy_to_packet(&queue->incoming[updated],&findex,sizeof(uint32_t));
copy_to_packet(&queue->incoming[updated],&utimestamp,sizeof(uint64_t));
copy_to_packet(&queue->incoming[updated],&sum,sizeof(uint16_t));
copy_to_packet(&queue->incoming[updated],ptr,size);
queue->inindex = updated;
} while (audio_packets->peek());
}
if (!local_shutdown->peek())
local_shutdown->raise(true);
};
auto touch_fun = [mail,process_shutdown_event](DisplayQueue* queue){
auto timer = platf::create_high_precision_timer();
auto local_shutdown= mail->event<bool>(mail::shutdown);
auto touch_port = mail->event<input::touch_port_t>(mail::touch_port);
@ -371,19 +380,20 @@ main(int argc, char *argv[]) {
};
BOOST_LOG(info) << "Starting capture on channel " << queuetype;
if (queuetype == QueueType::Video) {
auto capture = std::thread{video_capture,mail,target,codec};
auto forward = std::thread{push,mail,queue,(QueueType)queuetype};
auto touch_thread = std::thread{touch_fun,queue};
auto receive = std::thread{pull};
{
auto capture = std::thread{video_capture,mail,"TODO",codec};
auto forward = std::thread{push_video,mail,&memory->video.internal};
auto touch_thread = std::thread{touch_fun,&memory->video};
auto receive = std::thread{pull,&memory->video.internal};
receive.detach();
touch_thread.detach();
capture.detach();
forward.detach();
} else if (queuetype == QueueType::Audio) {
}
{
auto capture = std::thread{audio_capture,mail};
auto forward = std::thread{push,mail,queue,(QueueType)queuetype};
auto forward = std::thread{push_audio,mail,&memory->audio};
capture.detach();
forward.detach();
}

1
third-party/Simple-Web-Server vendored Submodule

@ -0,0 +1 @@
Subproject commit 187f798d54a9c6cee742f2eb2c54e9ba26f5a385

1
third-party/ViGEmClient vendored Submodule

@ -0,0 +1 @@
Subproject commit 8d71f6740ffff4671cdadbca255ce528e3cd3fef

1
third-party/doxyconfig vendored Submodule

@ -0,0 +1 @@
Subproject commit a73f908fb70fac4f6076a28f0a751239ea5ac2d3

1
third-party/googletest vendored Submodule

@ -0,0 +1 @@
Subproject commit 52eb8108c5bdec04579160ae17225d66034bd723

1
third-party/inputtino vendored Submodule

@ -0,0 +1 @@
Subproject commit 83cf70ef33196a2a022671ccf8a686fc8f67ae8e

1
third-party/libdisplaydevice vendored Submodule

@ -0,0 +1 @@
Subproject commit f31e46d8736fa6932d34c1417111e60ca507b29f

1
third-party/moonlight-common-c vendored Submodule

@ -0,0 +1 @@
Subproject commit 5f2280183cb62cba1052894d76e64e5f4153377d

1
third-party/nanors vendored Submodule

@ -0,0 +1 @@
Subproject commit 19f07b513e924e471cadd141943c1ec4adc8d0e0

1
third-party/tray vendored Submodule

@ -0,0 +1 @@
Subproject commit 0309a7cb84aad25079b60c40d1eae0bacd05b26d