If you check out my blog archives or check out the Remote Work category you'll see that I'm always tinkering with my remote work situation. I'm the most interested in high quality and pervasive video. I'm so interested in this that for a while I was running a 10 hour a day persistent "Portal" between Portland and Seattle. I still highly recommend this for co-located teams that have the bandwidth to burn. It's great to be able to turn one's head and see their teammate right there by their side - even though they are 200 miles away.
如果您查看我的博客档案或“远程工作”类别,您会发现我一直在修改远程工作情况。 我对高质量和普及性视频最感兴趣。 我对此非常感兴趣,以至于有一天我每天在波特兰和西雅图之间连续运行10个小时的“门户” 。 对于有带宽需要燃烧的同地办公团队,我仍然强烈建议这样做。 能够转过头去看着他们的队友就在他们身边,这很棒,即使他们在200英里之外。
I recently picked up a pair of Logitech BCC950 Conference Cams as a possible replacement for the very expensive "RoundTables" that some rooms at Microsoft have. The RoundTables are lovely but they are becoming scarce at the office and the Logitech is literally one-tenth the price. I'll blog a full and detailed review of the BCC950 later on this week but for now my biggest issue was that the Video Kiosk software I was using was starting to show its age. It's flaky and unreliable and build on the Office Communicator 2007 interfaces while I've been on Lync 2010 for a while.
我最近拿起了一对Logitech BCC950会议摄像头,可以替代微软某些会议室非常昂贵的“圆桌会议”。 圆桌会议很可爱,但是在办公室却越来越稀缺,而罗技的价格实际上只有价格的十分之一。 我将在本周晚些时候对BCC950进行完整而详细的评论,但现在我最大的问题是我使用的Video Kiosk软件开始显示其年龄。 当我使用Lync 2010已有一段时间时,它是片状且不可靠的,并且基于Office Communicator 2007界面构建。
Additionally, the researchers that wrote the software are always adding features I don't need for hardware I don't have. My remote buddy Damian Edwards and I decided we needed to make a software break.
此外,编写该软件的研究人员总是在添加我不需要的硬件所没有的功能。 我和我的异乡好友Damian Edwards决定我们需要中断软件。
特征 (Features )
We want these simple features to start:
我们希望这些简单的功能开始:
- Auto-answer of video calls - possibly with some whitelist for security 自动应答视频通话-可能带有一些白名单以确保安全
- Auto-fullscreen of video calls on the remote machine - the single purpose kiosk in my Seattle office 远程计算机上的视频通话自动全屏显示-我西雅图办公室的一站式服务亭
- Presence information and a simple UI for making calls - by simple I mean "my boss's boss" simple 状态信息和用于拨打电话的简单用户界面-简单来说,我的意思是“我老板的老板”很简单
- Remote control of Pan Tilt Zoom (PTZ) features on the same - ideally using the standard "inbox UVC" drivers and no 3rd party software 可以同时控制Pan Tilt Zoom(PTZ)功能-理想情况下,使用标准的“ inbox UVC”驱动程序,而无需第三方软件
Tonight I sat down and did the first three of these and put it on GitHub. I call it - wait for it - the Lync 2012 Super Simple Auto Answer Video Kiosk with Full Screen since the name "SmartGlass" was already taken. ;)
今晚我坐下来做了其中的前三个,并将其放在GitHub上。 我称它-等待它- 具有名称“ SmartGlass ”的Lync 2012全屏超级简单自动应答视频亭。 ;)
I searched ALL over to find out if there was SOME sample code out there that would just auto-answer a call from Lync and start video. I could find dozens of samples that made calls, that started chats, but none that would answer and auto-start video. You'd think this would be the FIRST thing that folks would want to do. I can only assume it's not a setting for security reasons.
我到处搜索,以查找是否有一些示例代码可以自动应答来自Lync的呼叫并开始视频。 我可以找到数十个进行通话,开始聊天的示例,但没有一个可以应答并自动启动视频。 您可能会认为这是人们想要做的第一件事。 出于安全原因,我只能假设这不是设置。
自动应答带有视频的Lync呼叫 (Auto-Answering Lync Calls with Video)
Now, it's late and there's likely problems so pull requests are welcome if I have done something stupid. Lync is complex and while you'd think there'd be an "AutoAnswer = true" it's actually a more complex API than that. I started from this MSDN article on "Responding to a Lync Conversation Invitation."
现在,已经很晚了,可能还有问题,所以如果我做过一些愚蠢的事情,欢迎请求拉取。 Lync很复杂,尽管您认为会有一个“ AutoAnswer = true”,但实际上它是一个比这更复杂的API。 我从这篇MSDN文章开始,内容是“响应Lync对话邀请”。
var lync = LyncClient.GetClient();
var conversationmgr = lync.ConversationManager;
conversationmgr.ConversationAdded += (_, cmea) =>
{
bool IncomingAV = false;
StringBuilder sb = new StringBuilder();
//Is this an audio/video invitation?
if (cmea.Conversation.Modalities[ModalityTypes.AudioVideo].State == ModalityState.Notified)
{
if (lync.DeviceManager.ActiveAudioDevice != null)
{
sb.Append("Incoming call from ");
IncomingAV = true;
}
else
{
cmea.Conversation.Modalities[ModalityTypes.AudioVideo].Reject(ModalityDisconnectReason.NotAcceptableHere);
}
}
if (cmea.Conversation.Modalities[ModalityTypes.InstantMessage].State == ModalityState.Connected)
{
sb.Append("Incoming IM from ");
}
sb.Append(String.Join(", ", cmea.Conversation.Participants.Select(i => i.Contact.Uri)));
Debug.WriteLine(sb.ToString());
if (IncomingAV == true && Properties.Settings.Default.autoAnswer == true) //I added that setting later on
{
InitiateAVStream(cmea.Conversation);
}
};
You watch for a Conversation to start and see if it's an Audio/Video on. If it is, then we call our InitiateAVStream method. You can't do all this stuff synchronously as Lync is full of async native COM APIs and events that you need to respond to. Here we accept the video which lets us see who called us but doesn't yet start OUR video. Remember "we" are the dumb Kiosk who is receiving a call from me.
您会观察对话是否开始,并查看是否打开了音频/视频。 如果是这样,则我们调用InitiateAVStream方法。 由于Lync充满了需要响应的异步本机COM API和事件,因此您无法同步完成所有这些工作。 在这里,我们接受视频,使我们可以看到谁打了电话给我们,但还没有开始播放我们的视频。 请记住,“我们”是接到我电话的傻小报亭。
private static void InitiateAVStream(Conversation pConversation)
{
if (pConversation.State == ConversationState.Terminated) { return; }
if (pConversation.Modalities[ModalityTypes.AudioVideo].CanInvoke(ModalityAction.Connect))
{
var video = (AVModality)pConversation.Modalities[ModalityTypes.AudioVideo];
video.Accept();
//Get ready to be connected, then WE can start OUR video
video.ModalityStateChanged += _AVModality_ModalityStateChanged;
}
}
See how they are chaining handlers? I think this code could be made cleaner with a series of nested closures like above in the ConversationAdded example, but then again, maybe not. It'll get four deep before we're done.
看看他们是如何链接处理程序的? 我认为可以使用上面嵌套的ConversationAdded示例中的一系列嵌套闭包来使此代码更简洁,但是,也许不是。 在我们完成之前,它将深入四点。
Now the call is being connected but perhaps not yet. When its state changes the VideoChannel has opened up and we watch for the video to be received.
现在通话已接通,但可能尚未接通。 当其状态更改时,VideoChannel已打开,我们等待视频的接收。
static void _AVModality_ModalityStateChanged(object sender, ModalityStateChangedEventArgs e)
{
VideoChannel vc = null;
switch (e.NewState)
{
//we can't start video until it's connected
case ModalityState.Connected:
if (vc == null)
{
vc = ((AVModality)sender).VideoChannel;
vc.StateChanged += new EventHandler<ChannelStateChangedEventArgs>(_VideoChannel_StateChanged);
}
break;
}
}
As the video starts up, I can see if the system is ready for the Kiosk to start its video. If so, we call BeginStart (and the SDK says we HAVE to call EndStart, so watch out!).
在视频启动时,我可以看到系统是否已准备好让Kiosk开始其视频。 如果是这样,我们调用BeginStart(SDK表示我们必须调用EndStart,所以要当心!)。
static void _VideoChannel_StateChanged(object sender, ChannelStateChangedEventArgs e)
{
VideoChannel vc = (VideoChannel)sender;
//Are we receiving? Let's try to send!
if (e.NewState == ChannelState.Receive)
{
if (vc.CanInvoke(ChannelAction.Start))
{
vc.BeginStart(videoCallBack, vc);
}
else { Debug.WriteLine("CanInvoke said NO!"); }
//Go looking around for the IM Window (there had better just be the one we just started)
// and force it to the foreground
IntPtr childHandle = UnsafeNativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "IMWindowClass", null);
UnsafeNativeMethods.SetForegroundWindow(childHandle);
//Try to get the video to go full screen by pressing F5
WindowsInput.InputSimulator.SimulateKeyPress(WindowsInput.VirtualKeyCode.F5);
}
}
private static void videoCallBack(IAsyncResult ar)
{
((VideoChannel)ar.AsyncState).EndStart(ar);
}
I'm pretty frustrated as while this is SUPER powerful, it's also SUPER complex for basic scenarios in my opinion. I think there's an opportunity here for a small layer on top of Lync that handles the 80% cases like the small Lync Abstraction layer introduced in this "ScreenPop" example application.
我非常沮丧,因为虽然它具有超级强大的功能,但在我看来,对于基本场景而言,它也超级复杂。 我认为在Lync之上有一个小的层可以处理80%的情况,例如本“ ScreenPop”示例应用程序中引入的小的Lync抽象层。
优点-WPF控件 (The Goodness - The WPF Controls)
At this point in the code I had everything working in a Console Application. You can go cherry-pick that commit if you want just a Console app that Auto-Answers video calls from Lync.
至此,我已经在控制台应用程序中完成了所有工作。 如果您只需要一个控制台应用程序,可以自动应答来自Lync的视频通话,则可以选择提交。
Even though I NEED to stop as I've got it working in a Console and I should be sleeping I noticed this in Visual Studio and it was too epic to not try.
即使我需要停下来,因为我已经在控制台中使它工作了,而且我应该睡觉了,但我在Visual Studio中注意到了这一点,这太史诗了,无法尝试。
![File | New Lync WPF Application File | New Lync WPF Application](https://i-blog.csdnimg.cn/blog_migrate/7b1b9bcc3debbb8387ae893b3d02af2b.png)
You know how it is. It's 2am, you're done with your goals. OF COURSE you're going to try to convert your Console App to a GUI before bed. Of course.
你知道是怎么回事。 现在是凌晨2点,您已经完成了目标。 当然,您打算在睡觉前将Console App转换为GUI。 当然。
Turns out there's a MESS of visual controls that you can put into existing applications to make them Lync-ified in literally minutes.
事实证明,您可以将MESS视觉控件放入现有应用程序中,以在几分钟内使它们成为Lync-ified。
![All the Lync Controls like SendEmailButton and StartVideoCallButton All the Lync Controls like SendEmailButton and StartVideoCallButton](https://i-blog.csdnimg.cn/blog_migrate/0449f75f4ef44038dbbdd46b130ff420.png)
Ok, awesome. I took the basic UI and added a checkbox for "Auto Answer."
好,太棒了我使用了基本的UI,并为“自动应答”添加了一个复选框。
![Perhaps the lamest UI ever. It's a head, a button and a checkbox Perhaps the lamest UI ever. It's a head, a button and a checkbox](https://i-blog.csdnimg.cn/blog_migrate/05b3d5dbc6f435d56d5b0290458daa6b.png)
(ASIDE: You DO realize that the outline of the "unknown face" in Lync there looks an AWFUL lot like Bill Gates' legendary 1977 mug shot, right? I just noticed that.)
(旁白:您确实意识到Lync中“未知面Kong”的轮廓看起来很像Bill Gates 1977年传奇的大头照,对吧?我只是注意到了。)
![Lync 2010's default Person matches the outline of Bill Gates' 1977 Mug Shot Lync 2010's default Person matches the outline of Bill Gates' 1977 Mug Shot](https://i-blog.csdnimg.cn/blog_migrate/319b48f4e670ddcb6fe224747ac0f3dd.png)
Anyway, then I made two settings, one for my "sip" address (that's in my app.config file as "sipEmailAddress" and one boolean for AutoAnswer. The complete XAML is just:
无论如何,然后我进行了两项设置,一项用于我的“ sip”地址(在我的app.config文件中为“ sipEmailAddress”,另一项用于AutoAnswer的布尔值。完整的XAML只是:
<Window x:Class="SuperSimpleLyncKiosk.Main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:SuperSimpleLyncKiosk.Properties"
xmlns:controls="clr-namespace:Microsoft.Lync.Controls;assembly=Microsoft.Lync.Controls"
Title="The World's Simplest Lync Kiosk (with Auto Answer, too!)" Height="Auto" Width="Auto">
<Grid RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<ScaleTransform ScaleX="3" ScaleY="3"/>
</Grid.RenderTransform>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<!-- Show the presence indicator. Hover over the icon to see the contact card.
Set Source to a valid SIP URI in your organization. -->
<controls:PresenceIndicator
x:Name="Presence"
Source="{Binding Source={x:Static properties:Settings.Default}, Path=sipEmailAddress, Mode=OneWay}"
PhotoDisplayMode="Large"
/>
<!-- Use the DisplayName property from PresenceIndicator to show the user's name -->
<TextBlock
Text="{Binding DisplayName, ElementName=Presence}"
Margin="4,0,0,0"
VerticalAlignment="Center"
/>
<controls:StartVideoCallButton Source="{Binding Source={x:Static properties:Settings.Default}, Path=sipEmailAddress, Mode=OneWay}" x:Name="startVideoCall" />
<controls:ShareDesktopButton Source="{Binding Source={x:Static properties:Settings.Default}, Path=sipEmailAddress, Mode=OneWay}"/>
</StackPanel>
<CheckBox IsChecked="{Binding Source={x:Static properties:Settings.Default}, Path=autoAnswer, Mode=TwoWay}" Content="Auto Answer Video Calls"/>
</StackPanel>
</Grid>
</Window>
I also added a 3x transform to scale ALL these default controls so they'd look good on the 42" TV that is sitting in my office. Because they are native WPF vector controls they just scale perfectly to high resolutions without raster jaggies.
我还添加了3x变换来缩放所有这些默认控件,因此它们在我办公室的42英寸电视上看起来都不错。由于它们是本机WPF矢量控件,因此它们可以完美缩放至高分辨率,而不会出现光栅锯齿。
Then I added a call to make the app Maximized by default:
然后,我添加了一个调用以使该应用默认为“最大化”:
this.WindowState = System.Windows.WindowState.Maximized;
And it looks like this when running:
运行时看起来像这样:
And when I call it automatically answers. Looks like everyone's asleep and they've turned the lights out!
当我打电话时,它会自动应答。 看起来每个人都在睡觉,他们把灯关了!
![My Remote office in Seattle My Remote office in Seattle](https://i-blog.csdnimg.cn/blog_migrate/ef7e56397f6de48ee7792221ec20cf75.png)
Ah, I but I wish it was full screen so the people in Redmond don't need to do anything or touch anything...
啊,我但是我希望它是全屏的,所以雷德蒙德的人们不需要做任何事情或触摸任何东西...
坏事 (The Badness)
Some nights you're programming and you say "may God have mercy on my soul for this line of code."
— Scott Hanselman (@shanselman)在某些夜晚,您正在编程,然后说:“愿上帝为我的灵魂怜悯这行代码。”
— Scott Hanselman(@shanselman) July 3, 2012 2012年7月3日
I can auto-answer calls but sometimes the window isn't in front and once it gets in front there's no programmatic way to tell Lync to go Fullscreen with video.
我可以自动接听电话,但有时窗口不在最前面,一旦窗口出现在最前面,就无法通过编程方式告诉Lync使用视频全屏显示。
Two bad problems there. Both solved by breaking all the rules. I get the Window Class with a big assumption that the Kiosk only as one chat window open and then I "push" F5 which is the Lync hotkey for fullscreen video.
那里有两个坏问题。 两者都通过违反所有规则解决。 我得到Window类的一个很大的假设是,只有在打开一个聊天窗口时,信息亭才打开,然后我“按下” F5,这是全屏视频的Lync热键。
//Go looking around for the IM Window (there had better just be the one we just started)
// and force it to the foreground
IntPtr childHandle = UnsafeNativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "IMWindowClass", null);
UnsafeNativeMethods.SetForegroundWindow(childHandle);
//Try to get the video to go full screen by pressing F5
WindowsInput.InputSimulator.SimulateKeyPress(WindowsInput.VirtualKeyCode.F5);
Those last two, of course, are calls directly into Win32 from .NET:
当然,最后两个是从.NET直接调用Win32:
public static class UnsafeNativeMethods
{
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
But, it works! It's scandalous that this isn't built into the Lync SDK. Developers who are fans of Lync or who work on it all the time will say that my attempt at a "poor man's Kiosk" is silly and that I really want to use "UI Suppression in Lync" and just make an app that hosts Lync rather than automates Lync. They are likely right. However, frankly, it looked super-hard and I was tired. So, ya. If anyone wants to work on the Kiosk with me to make it simple answer and start video and do it all without showing Lync, that'd be awesome.
但是,它有效! 令人惊讶的是,Lync SDK中没有内置此功能。 一直是Lync的粉丝或一直致力于该应用程序的开发人员会说,我尝试“穷人报亭”的做法很愚蠢,而我真的想使用“ Lync中的UI抑制”,而只是制作一个托管Lync的应用,比自动化Lync。 他们可能是正确的。 但是,坦率地说,它看起来很难,我很累。 所以啊如果有人想和我一起在Kiosk上做一个简单的答案,开始播放视频并在不显示Lync的情况下完成所有操作,那将很棒。
谢谢 (Thanks)
I also want to make a special nod to the InputSimulator library. It's amazing and it just works. It's WAY WAY better than SendKeys which you should stop using NOW.
我还想对InputSimulator库特别点头。 太神奇了,而且效果很好。 它比SendKeys更好,您应该立即停止使用它。
The Windows Input Simulator provides a simple .NET (C#) interface to simulate Keyboard or Mouse input using the Win32 SendInput method. All of the Interop is done for you and there's a simple programming model for sending multiple keystrokes.
Windows输入模拟器提供了一个简单的.NET(C#)接口,以使用Win32 SendInput方法模拟键盘或鼠标输入。 所有Interop都已为您完成,并且有一个简单的编程模型可以发送多个击键。
Enjoy!
请享用!
Lync开发人员资源 (Lync Developer Resources)
相关链接(Related Links)
Get involved in Open Source today - How to contribute a patch to a GitHub hosted Open Source project
Hanselminutes Podcast 242 - The Plight of the Remote Worker with Pete Brown
The Weekly Source Code 57 -Controlling an Eagletron TrackerPod with C# 4, ASP.NET MVC and jQuery
Building an Embodied Social Proxy or Crazy Webcam Remote Cart Thing
How to Collaborate with Remote Employees with Office Communicator 2007 R2
Virtual Camaraderie - A Persistent Video "Portal" for the Remote Worker
Working Remotely from Home, Telepresence and Video Conferencing: One Year Later
Microsoft - Surviving First Three Weeks as a Remote Employee