Created
July 29, 2018 19:54
-
-
Save imorrish/2b950adeec07d69f68d5e6527e575058 to your computer and use it in GitHub Desktop.
Windows PowerShell script to show ATEM Multiview using ffplay and touch overlay to switch ATEM inputs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Ian Morrish | |
# https://ianmorrish.wordpress.com | |
# Powershell script to to allow touch screen control of ATEM by overlaying multiview source with transparent WPF grid | |
# Any USB 3 capture card that can be detected by ffplay (Direct Show drivers) can be used to display the multiView | |
#launch ffplay | |
start-process -filepath c:\tools\launchcamlink.cmd | |
Start-Sleep 2 | |
function Show-Process($Process, [Switch]$Maximize) | |
{ | |
$sig = ' | |
[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); | |
[DllImport("user32.dll")] public static extern int SetForegroundWindow(IntPtr hwnd); | |
' | |
if ($Maximize) { $Mode = 3 } else { $Mode = 4 } | |
$type = Add-Type -MemberDefinition $sig -Name WindowAPI -PassThru | |
$hwnd = $process.MainWindowHandle | |
$null = $type::ShowWindowAsync($hwnd, $Mode) | |
$null = $type::SetForegroundWindow($hwnd) | |
} | |
show-process (Get-Process -ProcessName "ffplay") -Maximize | |
#Show-Process "overlay" | |
# Connect to ATEM | |
function ConnectToATEM() | |
{ | |
Try{ | |
$ATEMipAddress = (Get-ItemProperty -path 'HKCU:\Software\Blackmagic Design\ATEM Software Control').ipAddress | |
$DocumentsPath = [Environment]::GetFolderPath("MyDocuments") + '\windowspowershell\SwitcherLib.dll' | |
add-type -path $DocumentsPath | |
$Global:atem = New-Object SwitcherLib.Switcher($ATEMipAddress) | |
$atem.Connect() | |
} | |
catch{ | |
write-host "Can't connect to ATEM on $($ATEMipAddrss)." | |
Write-Host "ATEM controle software must be installed and have connected to switcher at least one time" | |
Stop | |
} | |
} | |
function CreateATEMObjects() | |
{ | |
$me=$atem.GetMEs() | |
$Global:me1=$me[0] | |
$Global:me2=$me[1] | |
$Global:activeME = $me1 | |
if($me.count -gt 1){ | |
$Global:activeME = $me[1] | |
} | |
$MediaPlayers = $atem.GetMediaPlayers() | |
$Global:MP1=$MediaPlayers[0] | |
$Global:MP2=$MediaPlayers[1] | |
$Global:Auxs=$atem.GetAuxInputs() | |
$Global:aux1 = $auxs[0] | |
$Global:USK = $ATEM.GetKeys() | |
$Global:activeUSK = $USK[0] | |
$Global:DSK = $ATEM.GetDSKs() | |
$Global:activeDSK = $DSK[0] | |
$global:mv=$atem.GetMultiViews() | |
} | |
ConnectToATEM | |
CreateATEMObjects | |
#Create hash of multiview window sources | |
$mvSource=@{} | |
$i=2 | |
for($i; $i -lt 10; $i++){ | |
$mvSource.Add($i, $mv[0].GetWindowInput($i)) | |
} | |
#ATEM MultiView click overlay | |
$inputXML = @" | |
<Window | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
x:Name="Window" | |
WindowStyle = "None" WindowState="Maximized" | |
ResizeMode = "NoResize" Title = "overlay" AllowsTransparency = "True" Background = "Transparent" Opacity = "1" Topmost = "True"> | |
<Grid x:Name = "Grid" Background = "Transparent" ShowGridLines="True"> | |
<Grid.ColumnDefinitions> | |
<ColumnDefinition Width="*" /> | |
<ColumnDefinition Width="*" /> | |
<ColumnDefinition Width="*" /> | |
<ColumnDefinition Width="*" /> | |
</Grid.ColumnDefinitions> | |
<Grid.RowDefinitions> | |
<RowDefinition Height="80" /> | |
<RowDefinition Height="*" /> | |
<RowDefinition Height="*" /> | |
<RowDefinition Height="*" /> | |
<RowDefinition Height="*" /> | |
<RowDefinition Height="80" /> | |
</Grid.RowDefinitions> | |
<Button Name="Cell1" Grid.Row="0" Grid.Column="0" Opacity=".8"/> | |
<Button Name="Cell2" Grid.Row="0" Grid.Column="1" Opacity=".8"/> | |
<Button Name="Cell3" Grid.Row="0" Grid.Column="2" Opacity=".8"/> | |
<Button Name="Cell4" Grid.Row="0" Grid.Column="3" Opacity=".8"/> | |
<Button Name="Cell5" Grid.Row="1" Grid.Column="0" Opacity="0.01"/> | |
<Button Name="Cell6" Grid.Row="1" Grid.Column="1" Opacity="0.01" /> | |
<Button Name="Cell7" Grid.Row="1" Grid.Column="2" Opacity="0.01"/> | |
<Button Name="Cell8" Grid.Row="1" Grid.Column="3" Opacity="0.01"/> | |
<Button Name="Cell9" Grid.Row="2" Grid.Column="0" Opacity="0.01"/> | |
<Button Name="Cell10" Grid.Row="2" Grid.Column="1" Opacity="0.01"/> | |
<Button Name="Cell11" Grid.Row="2" Grid.Column="2" Opacity="0.01"/> | |
<Button Name="Cell12" Grid.Row="2" Grid.Column="3" Opacity="0.01"/> | |
<Button Name="Cell13" Grid.Row="3" Grid.Column="0" Opacity="0.01"/> | |
<Button Name="Cell14" Grid.Row="3" Grid.Column="1" Opacity="0.01"/> | |
<Button Name="Cell15" Grid.Row="3" Grid.Column="2" Opacity="0.01"/> | |
<Button Name="Cell16" Grid.Row="3" Grid.Column="3" Opacity="0.01"/> | |
<Button Name="Cell17" Grid.Row="4" Grid.Column="0" Opacity="0.01"/> | |
<Button Name="Cell18" Grid.Row="4" Grid.Column="1" Opacity="0.01"/> | |
<Button Name="Cell19" Grid.Row="4" Grid.Column="2" Opacity="0.01"/> | |
<Button Name="Cell20" Grid.Row="4" Grid.Column="3" Opacity="0.01"/> | |
</Grid> | |
</Window> | |
"@ | |
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' | |
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | |
[xml]$XAML = $inputXML | |
#Read XAML | |
$reader=(New-Object System.Xml.XmlNodeReader $xaml) | |
try{$Window=[Windows.Markup.XamlReader]::Load( $reader )} | |
catch [System.Management.Automation.MethodInvocationException] { | |
Write-Warning "We ran into a problem with the XAML code. Check the syntax for this control..." | |
write-host $error[0].Exception.Message -ForegroundColor Red | |
if ($error[0].Exception.Message -like "*button*"){ | |
write-warning "Ensure your <button in the `$inputXML does NOT have a Click=ButtonClick property. PS can't handle this`n`n`n`n"} | |
} | |
catch{#if it broke some other way :D | |
Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed." | |
} | |
# Create PowerShell object for each WPF button | |
$xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Window.FindName($_.Name)} | |
# Map multiview window click to actual ATEM input ID | |
function buttonclicked($btnNAme){ | |
write-host $btnNAme | |
switch($btnNAme){ | |
0{$Script:Window.close();break | |
} | |
{1..8 -contains $_}{ | |
#$me1.preview=$_ | |
$me1.preview=$mvSource[$btnNAme+1] | |
} | |
} | |
} | |
#Create event handlers (tried doing this in a cleaver foreach but it just wouldn't work) | |
$WPFCell4.add_click({buttonclicked 0}) | |
$WPFCell13.add_click({buttonclicked 1}) | |
$WPFCell14.add_click({buttonclicked 2}) | |
$WPFCell15.add_click({buttonclicked 3}) | |
$WPFCell16.add_click({buttonclicked 4}) | |
$WPFCell17.add_click({buttonclicked 5}) | |
$WPFCell18.add_click({buttonclicked 6}) | |
$WPFCell19.add_click({buttonclicked 7}) | |
$WPFCell20.add_click({buttonclicked 8}) | |
$WPFCell3.add_click({$me1.cut()}) | |
# Turn on WPF overlay display | |
$Window.ShowDialog() | out-null | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cd \tools | |
ffplay -f dshow -video_size 1280x720 -framerate 50 -i video="Cam Link" -threads 2 -fast -an -loglevel quiet |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Create desktop shortcut to run the script. Command is
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command "& c:\tools\scripts\atem_multiview.ps1"
Set “Start In” value to directory holding the files
See https://ianmorrish.wordpress.com for more