Many teams in our company use SCRUM approach. As a part of this methodology they have daily SCRUM-meetings with customers via Skype. It’s a very important part of product development and of course we don’t want to miss anything. So, we decided to record our meetings to be able to listen to them again in the future. The main goal of recording is to archive important information and decisions related to the project issues, and to improve language knowledge of team members. The requirements for the application that records Skype calls:
- high-quality recording with possibility to improve resulting sound,
- silent working without any prompts,
- auto recording conversations,
- auto start with Windows and auto connect/reconnect to Skype,
- flexible configuration,
- filters for recording conversations with only specified contacts,
- distribution of recorded files to different locations (including network paths) depends on filters,
- confidence in application that it doesn’t steal conversations.
We tried the most popular recorders but none of them satisfies all requirements. Wish to make something useful for our company :) led me to the creation of my own application that will record Skype conversation and meets all points listed above. I spent few evenings and figured out one interesting thing about Skype: it is able to record sound without any additions! All you need is to send Skype API command that will redirect sound channels to the files! Of course, there are some limitations:
- the format of redirected sound is WAV,
- Skype redirect channels separately from microphone and from speakers.
So on the output we have two huge files (since WAV doesn’t have compression), which is inconvenient to store and listen. To create Skype recorder you should write some wrapper for Skype API and “class-handler” that will send and receive messages from Skype. It is also possible to use official Skype4COM library instead of implementing own wrapper, but it was interesting for me to do it myself. Here is the small tutorial how to use Skype API.
Connecting to Skype and “subscribing” to events.
First of all, we need to register “discover” and “attach” Windows API messages since Skype uses them for communication:
[DllImport("user32.dll")]
public static extern uint RegisterWindowMessage(string message);
skypeApiDiscover = RegisterWindowMessage("SkypeControlAPIDiscover");
skypeApiAttach = RegisterWindowMessage("SkypeControlAPIAttach");
Now, to attach to the Skype, you should send attachment request via Windows API broadcast message. As a parameter you must provide discover message ID skypeApiDiscover and window handle that will receive further messages from the Skype.
[DllImport("user32.dll")]
private static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint msg, IntPtr wParam,
ref CopyDataStruct lParam, uint flags, uint timeout, out IntPtr result);
SendMessageTimeout(new IntPtr(-1), skypeApiDiscover, ourWindowHandle,
IntPtr.Zero, IntPtr.Zero, 100, out result)
Skype will show prompt for attachment request: After clicking one of the buttons Skype sends Window API message to the window which handle we have specified. The window must contain WndProc to be able to process message.
public IntPtr WndProc(IntPtr hWnd, int message,
IntPtr wParam, IntPtr lParam, ref bool handled)
{
// ...
}
As an attachment result Skype sends message with message == skypeApiAttach and lParam == 0 (in case of success; see other codes in the Skype API documentation). Parameter wParam contains Skype window handle that we will use to send commands. If attachment was done then WndProc will start receiving messages for all Skype events. To filter them we can check that message == WM_COPYDATA (0x004A) and wParam == skypeWindowHandle. The information about event is stored in the lParam (COPY_DATA structure). For example, the COPY_DATA content for the event of call beginning looks like: CALL 1234 STATUS INPROGRESS where 1234 – is the unique ID of the call. It must be used in the other Skype commands to have control over this conversation.
Sending commands to Skype.
For sending certain Skype command you should prepare COPY_DATA structure (include command text as content) and send it by using SendMessageTimeout Windows API function. For example, if we want to redirect current conversation sound to the file, we should use two Skype commands:
ALTER CALL {0} SET_OUTPUT FILE=\"{1}\" – redirecting speakers sound, that we hear from our conversation partner (in Skype terms);
ALTER CALL {0} SET_CAPTURE_MIC FILE=\"{1}\" – redirecting our microphone, what we say to our conversation partner.
The fragments of the code that send these commands:
[StructLayout(LayoutKind.Sequential)]
internal struct CopyDataStruct
{
public string Id;
public int Size;
public string Data;
}
[DllImport("user32.dll")]
private static extern IntPtr SendMessageTimeout(IntPtr windowHandle, uint message,
IntPtr wParam, ref CopyDataStruct lParam, SendMessageTimeoutFlags flags,
uint timeout, out IntPtr result);
private void sendSkypeCommand(string command)
{
var data = new CopyDataStruct
{ Id = "1", Size = command.Length + 1, Data = command };
IntPtr result;
SendMessageTimeout(skypeWindowHandle, WM_COPYDATA, ourWindowHandle, ref data,
SendMessageTimeoutFlags.Normal, 100, out result);
}
public void Redirect(string inFileName, string outFileName)
{
var recordInCommand = string.Format("ALTER CALL {0} SET_OUTPUT FILE=\"{1}\"",
currentCallNumber, inFileName);
var recordOutCommand =
string.Format("ALTER CALL {0} SET_CAPTURE_MIC FILE=\"{1}\"",
currentCallNumber, outFileName);
sendSkypeCommand(recordInCommand);
sendSkypeCommand(recordOutCommand);
Note, that after redirection you will continue hearing your partner and microphone will work. Furthermore, different application can send redirection simultaneously and Skype will handle each separately.
Skype API issues.
I figured out that in order to develop application that will automatically detect Skype presence and have an ability to reconnect, you need to invent some workarounds for the few Skype API issues:
- You don’t know when Skype starts since you don’t get API messages without connection. As a workaround you can wait for Skype.exe process, but here is the second issue.
- Skype API works only on the main screen, when you are logged in. The first login screen will not “answer” your requests. So, to get to know when exactly you can try to connect, you should wait definite window (for example, check its presence by window class name via FindWindow or set global shell hook).
- Skype will not send any API messages if you simply close it (not logged out) or kill the process. In that case your application will still think that it is connected. Furthermore, if Skype will be started again, you will not receive any messages. You must explicitly reconnect by sending new request.
The main steps to write a simple recorder.
- Implement wrapper for Skype API that will allow sending and receiving messages, or use official library.
- Implement some kind of connector that will process received messages and watch for Skype presence.
- Implement application settings (filters, black-list, etc.).
- Implement some logic that will make a decision how to react on Skype events depends on settings.
- Implement post-processing for redirected sound files.
Post-processing for redirected sound files.
Skype saves sound “channels” separately in WAV format. To get MP3 file you need to :
- Merge files into one file.
- Convert WAV to MP3.
The easiest way is to use some external tool. For example, you can use such free open-source solutions like SoX (Sound eXchange, http://sox.sourceforge.net/) and LAME (http://lame.sourceforge.net/). SkypeAutoRecorder. The result of my work is an open-source application SkypeAutoRecorder that is used now in the conference room and workplaces in our company. Application is developed using C# and Windows Presentation Foundation. It is always visible in the tray: The tray icon and its tooltip show the current application state (connected, waiting for calls, recording). The context menu provides access to the settings window:
Following settings are available:
- Auto start with Windows (setting is stored in the Run section of Windows registry).
- “Filters” setting: you can bind different file names for different contacts (or set of contacts). Filter provides a convenient way to distribute and sort recorded files automatically. For example, our team gets recorded file to the workplace computers (filter contains network path) just after meeting.
- Optionally, all calls can be recorded to the specified file name (“Record unfiltered conversations to” setting); otherwise, SkypeAutoRecorder processes only conversations with contacts specified in the filters. File name from this setting is used only when application can’t find filter for contact.
- Some of contacts can be ignored if previous setting is enabled (“Don’t record conversations with contacts” setting). Note, that its works only for contacts which are not specified in the filters.
- And the last setting – increasing volume of the resulting MP3 file. Useful, when your partner has low microphone.
File names can contain pattern placeholders for date-time of conversation start and contact name. Settings window verifies all inputs to exclude possible mistakes in the file names. All settings are stored per user in the Windows Roaming folder. The output format of the recorded files after processing is MP3 with variable bitrate and optionally increased sound volume. If application for some reason can’t distribute files to the specified location (for instance, network path or removable drive is inaccessible) then it will store it to the default Music folder of the current user and tell about this via message box. Developed application fully meets requirements listed above. The latest version and source code can be found on http://skypeautorecorder.codeplex.com/. Feel free to use it! :) Kirill .NET Developer