├── .gitignore
├── ATEM Switcher Library.sln
├── MediaPool
├── App.config
├── MediaPool.cs
├── MediaPool.csproj
├── Properties
│ └── AssemblyInfo.cs
└── packages.config
├── MediaUpload
├── App.config
├── MediaUpload.cs
├── MediaUpload.csproj
└── Properties
│ └── AssemblyInfo.cs
├── Prep Release.cmd
├── SwitcherLib
├── Callbacks
│ ├── Stills.cs
│ └── UploadLock.cs
├── ConsoleUtils.cs
├── Log.cs
├── MediaStill.cs
├── Properties
│ └── AssemblyInfo.cs
├── Switcher.cs
├── SwitcherLib.csproj
├── SwitcherLibException.cs
└── Upload.cs
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /Release
2 | /SwitcherLib/bin
3 | /MediaPool/bin
4 | /MediaUpload/bin
5 | /SwitcherLib/obj
6 | /MediaPool/obj
7 | /MediaUpload/obj
8 | /packages
9 | /*.suo
10 |
--------------------------------------------------------------------------------
/ATEM Switcher Library.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwitcherLib", "SwitcherLib\SwitcherLib.csproj", "{5F9F52A6-9EF4-4C71-9A91-F5B864A57ABB}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaPool", "MediaPool\MediaPool.csproj", "{A17A1F1D-C1F7-4489-8D55-E75C5FEFFF69}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaUpload", "MediaUpload\MediaUpload.csproj", "{ED13F395-B644-42DC-9BBA-5157B238F277}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {5F9F52A6-9EF4-4C71-9A91-F5B864A57ABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {5F9F52A6-9EF4-4C71-9A91-F5B864A57ABB}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {5F9F52A6-9EF4-4C71-9A91-F5B864A57ABB}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {5F9F52A6-9EF4-4C71-9A91-F5B864A57ABB}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {A17A1F1D-C1F7-4489-8D55-E75C5FEFFF69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {A17A1F1D-C1F7-4489-8D55-E75C5FEFFF69}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {A17A1F1D-C1F7-4489-8D55-E75C5FEFFF69}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {A17A1F1D-C1F7-4489-8D55-E75C5FEFFF69}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {ED13F395-B644-42DC-9BBA-5157B238F277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {ED13F395-B644-42DC-9BBA-5157B238F277}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {ED13F395-B644-42DC-9BBA-5157B238F277}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {ED13F395-B644-42DC-9BBA-5157B238F277}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/MediaPool/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MediaPool/MediaPool.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using SwitcherLib;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Xml.Serialization;
10 |
11 | namespace MediaPool
12 | {
13 | class MediaPool
14 | {
15 | private enum Format
16 | {
17 | Text,
18 | JSON,
19 | XML,
20 | CSV,
21 | }
22 |
23 | static int Main(string[] args)
24 | {
25 | try
26 | {
27 | MediaPool.ProcessArgs(args);
28 | return 0;
29 | }
30 | catch (SwitcherLibException ex)
31 | {
32 | Console.Error.WriteLine(ex.Message);
33 | return -1;
34 | }
35 | }
36 |
37 | private static void Help()
38 | {
39 | ConsoleUtils.Version();
40 | Console.Out.WriteLine();
41 | Console.Out.WriteLine("Usage: mediapool.exe [options] ");
42 | Console.Out.WriteLine("Gets the info for all the media in the media pool for an ATEM switcher");
43 | Console.Out.WriteLine();
44 | Console.Out.WriteLine("Arguments:");
45 | Console.Out.WriteLine();
46 | Console.Out.WriteLine(" hostname - The hostname or IP of the ATEM switcher");
47 | Console.Out.WriteLine();
48 | Console.Out.WriteLine("Options:");
49 | Console.Out.WriteLine();
50 | Console.Out.WriteLine(" -h, --help - This help message");
51 | Console.Out.WriteLine(" -d, --debug - Debug output");
52 | Console.Out.WriteLine(" -v, --version - Version information");
53 | Console.Out.WriteLine(" -f, --format - The output format. Either xml, csv, json or text");
54 | Console.Out.WriteLine();
55 | }
56 |
57 | private static void ProcessArgs(string[] args)
58 | {
59 | IList args1 = new List();
60 | MediaPool.Format format = MediaPool.Format.Text;
61 | for (int index = 0; index < args.Length; index++)
62 | {
63 | switch (args[index])
64 | {
65 | case "-h":
66 | case "--help":
67 | case "-?":
68 | case "/?":
69 | case "/h":
70 | case "/help":
71 | MediaPool.Help();
72 | return;
73 |
74 | case "-v":
75 | case "--version":
76 | case "/v":
77 | case "/version":
78 | ConsoleUtils.Version();
79 | return;
80 |
81 | case "-d":
82 | case "--debug":
83 | case "/d":
84 | case "/debug":
85 | Log.CurrentLevel = Log.Level.Debug;
86 | break;
87 |
88 | case "-f":
89 | case "--format":
90 | if (args.Length > index)
91 | {
92 | switch (args[index + 1].ToLower())
93 | {
94 | case "json":
95 | format = MediaPool.Format.JSON;
96 | break;
97 | case "xml":
98 | format = MediaPool.Format.XML;
99 | break;
100 | case "csv":
101 | format = MediaPool.Format.CSV;
102 | break;
103 | case "text":
104 | format = MediaPool.Format.Text;
105 | break;
106 | default:
107 | throw new SwitcherLibException(String.Format("Unknown format: {0}", format));
108 | }
109 | index++;
110 | break;
111 | }
112 | break;
113 |
114 | default:
115 | args1.Add(args[index]);
116 | break;
117 | }
118 | }
119 | MediaPool.ListMediaPool(format, args1);
120 | }
121 |
122 | private static void ListMediaPool(MediaPool.Format format, IList args)
123 | {
124 | if (args.Count < 1)
125 | {
126 | MediaPool.Help();
127 | throw new SwitcherLibException("Invalid arguments");
128 | }
129 |
130 | Switcher switcher = new Switcher(args[0]);
131 | Log.Debug(String.Format("Switcher: {0}", switcher.GetProductName()));
132 | IList stills = switcher.GetStills();
133 |
134 | switch (format)
135 | {
136 | case MediaPool.Format.Text:
137 | foreach (MediaStill still in stills)
138 | {
139 | Console.Out.WriteLine();
140 | Console.Out.WriteLine(String.Format(" Name: {0}", still.Name));
141 | Console.Out.WriteLine(String.Format(" Hash: {0}", still.Hash));
142 | Console.Out.WriteLine(String.Format(" Slot: {0}", still.Slot.ToString()));
143 | Console.Out.WriteLine(String.Format(" Media Player: {0}", still.MediaPlayer.ToString()));
144 | }
145 | break;
146 |
147 | case MediaPool.Format.JSON:
148 | Console.Out.WriteLine(JsonConvert.SerializeObject(stills));
149 | break;
150 |
151 | case MediaPool.Format.XML:
152 | XmlSerializer xml = new XmlSerializer(stills.GetType());
153 | xml.Serialize(Console.Out, stills);
154 | break;
155 |
156 | case MediaPool.Format.CSV:
157 | foreach (MediaStill still in stills)
158 | {
159 | Console.Out.WriteLine(still.ToCSV());
160 | }
161 | break;
162 |
163 | default:
164 | Console.Out.WriteLine(stills.ToString());
165 | break;
166 | }
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/MediaPool/MediaPool.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {A17A1F1D-C1F7-4489-8D55-E75C5FEFFF69}
8 | Exe
9 | Properties
10 | MediaPool
11 | MediaPool
12 | v4.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | False
37 | packages\Newtonsoft.Json.6.0.7\lib\net45\Newtonsoft.Json.dll
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {78a34361-5d3c-40aa-a98f-82311acaff49}
58 | SwitcherLib
59 |
60 |
61 |
62 |
69 |
--------------------------------------------------------------------------------
/MediaPool/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ATEM Media Pool List")]
9 | [assembly: AssemblyDescription("Retrieves a list of media in the media pool of an ATEM Switcher")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Jessica Smith")]
12 | [assembly: AssemblyProduct("ATEM Switcher Library")]
13 | [assembly: AssemblyCopyright("© Copyright Jessica Smith, 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("d11131f6-2beb-4525-a08e-5958812bb536")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("2.0.1.0")]
36 | [assembly: AssemblyFileVersion("2.0.1.0")]
37 |
--------------------------------------------------------------------------------
/MediaPool/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/MediaUpload/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MediaUpload/MediaUpload.cs:
--------------------------------------------------------------------------------
1 | using SwitcherLib;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace MediaUpload
11 | {
12 | class MediaUpload
13 | {
14 | private static int Main(string[] args)
15 | {
16 | try
17 | {
18 | MediaUpload.ProcessArgs(args);
19 | return 0;
20 | }
21 | catch (SwitcherLibException ex)
22 | {
23 | Console.Error.WriteLine(ex.Message);
24 | return -1;
25 | }
26 | }
27 |
28 | private static void Help()
29 | {
30 | ConsoleUtils.Version();
31 | Console.Out.WriteLine();
32 | Console.Out.WriteLine("Usage: mediaupload.exe [options] ");
33 | Console.Out.WriteLine("Uploads an image to a BlackMagic ATEM switcher");
34 | Console.Out.WriteLine();
35 | Console.Out.WriteLine("Arguments:");
36 | Console.Out.WriteLine();
37 | Console.Out.WriteLine(" hostname - The hostname or IP of the ATEM switcher");
38 | Console.Out.WriteLine(" slot - The number of the media slot to upload to");
39 | Console.Out.WriteLine(" filename - The filename of the image to upload");
40 | Console.Out.WriteLine();
41 | Console.Out.WriteLine("Options:");
42 | Console.Out.WriteLine();
43 | Console.Out.WriteLine(" -h, --help - This help message");
44 | Console.Out.WriteLine(" -d, --debug - Debug output");
45 | Console.Out.WriteLine(" -v, --version - Version information");
46 | Console.Out.WriteLine(" -n, --name - The name for the item in the media pool");
47 | Console.Out.WriteLine();
48 | Console.Out.WriteLine("Image Format:");
49 | Console.Out.WriteLine();
50 | Console.Out.WriteLine("The image must be the same resolution as the switcher. Accepted formats are BMP, JPEG, GIF, PNG and TIFF. Alpha channels are supported.");
51 | }
52 |
53 | private static void ProcessArgs(string[] args)
54 | {
55 | IList args1 = new List();
56 | string name = "";
57 | for (int index = 0; index < args.Length; index++)
58 | {
59 | switch (args[index])
60 | {
61 | case "-h":
62 | case "--help":
63 | case "-?":
64 | case "/?":
65 | case "/h":
66 | case "/help":
67 | MediaUpload.Help();
68 | return;
69 |
70 | case "-v":
71 | case "--version":
72 | case "/v":
73 | case "/version":
74 | ConsoleUtils.Version();
75 | return;
76 |
77 | case "-d":
78 | case "--debug":
79 | case "/d":
80 | case "/debug":
81 | Log.CurrentLevel = Log.Level.Debug;
82 | break;
83 |
84 | case "-n":
85 | case "--name":
86 | case "/n":
87 | case "/name":
88 | if (index < args.Length)
89 | {
90 | name = args[index + 1];
91 | index++;
92 | break;
93 | }
94 | break;
95 |
96 | default:
97 | args1.Add(args[index]);
98 | break;
99 | }
100 | }
101 | MediaUpload.Upload(name, args1);
102 | }
103 |
104 | private static void Upload(string name, IList args)
105 | {
106 | if (args.Count < 3)
107 | {
108 | MediaUpload.Help();
109 | throw new SwitcherLibException("Invalid arguments");
110 | }
111 |
112 | Switcher switcher = new Switcher(args[0]);
113 | int slot = MediaUpload.GetSlot(args[1]);
114 | Log.Debug(String.Format("Switcher: {0}", switcher.GetProductName()));
115 | Log.Debug(String.Format("Resolution: {0}x{1}", switcher.GetVideoWidth().ToString(), switcher.GetVideoHeight().ToString()));
116 | args.RemoveAt(0);
117 | args.RemoveAt(0);
118 |
119 | string filename = String.Join(" ", args);
120 | Upload upload = new Upload(switcher, filename, slot);
121 | if (name != "")
122 | {
123 | upload.SetName(name);
124 | }
125 | upload.Start();
126 | while (upload.InProgress())
127 | {
128 | Log.Info(String.Format("Progress: {0}%", upload.GetProgress().ToString()));
129 | Thread.Sleep(100);
130 | }
131 | }
132 |
133 | private static int GetSlot(string arg)
134 | {
135 | try
136 | {
137 | return Convert.ToInt32(arg) - 1;
138 | }
139 | catch (Exception ex)
140 | {
141 | throw new SwitcherLibException(String.Format("Invalid slot: {0}", arg), ex);
142 | }
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/MediaUpload/MediaUpload.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {ED13F395-B644-42DC-9BBA-5157B238F277}
8 | Exe
9 | Properties
10 | MediaUpload
11 | MediaUpload
12 | v4.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | {78a34361-5d3c-40aa-a98f-82311acaff49}
53 | SwitcherLib
54 |
55 |
56 |
57 |
64 |
--------------------------------------------------------------------------------
/MediaUpload/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ATEM Media Upload")]
9 | [assembly: AssemblyDescription("Uploads still images to the media library of ATEM switchers")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Jessica Smith")]
12 | [assembly: AssemblyProduct("ATEM Switcher Library")]
13 | [assembly: AssemblyCopyright("© Copyright Jessica Smith, 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("196a68c6-a112-4239-9756-c44a7449c8cf")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("2.0.1.0")]
36 | [assembly: AssemblyFileVersion("2.0.1.0")]
37 |
--------------------------------------------------------------------------------
/Prep Release.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | del /F /S /Q Release
3 | xcopy MediaPool\bin\release\MediaPool.exe Release /C /H /Y
4 | xcopy MediaPool\bin\release\*.dll Release /C /H /Y
5 | xcopy MediaUpload\bin\release\MediaUpload.exe Release /C /H /Y
6 | xcopy MediaUpload\bin\release\*.dll Release /C /H /Y
7 | xcopy readme.md Release\readme.md /C /H /Y
8 | pause
--------------------------------------------------------------------------------
/SwitcherLib/Callbacks/Stills.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using BMDSwitcherAPI;
7 |
8 | namespace SwitcherLib.Callbacks
9 | {
10 | class Stills : IBMDSwitcherStillsCallback
11 | {
12 | private Upload upload;
13 |
14 | public Stills(Upload upload)
15 | {
16 | this.upload = upload;
17 | }
18 |
19 | public void Notify(_BMDSwitcherMediaPoolEventType eventType, IBMDSwitcherFrame frame, int index)
20 | {
21 | Log.Debug(String.Format("Stills Callback: {0}", eventType.ToString()));
22 | _BMDSwitcherMediaPoolEventType mediaPoolEventType = eventType;
23 |
24 | if (mediaPoolEventType != _BMDSwitcherMediaPoolEventType.bmdSwitcherMediaPoolEventTypeTransferCompleted)
25 | {
26 | return;
27 | }
28 | this.upload.TransferCompleted();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SwitcherLib/Callbacks/UploadLock.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using BMDSwitcherAPI;
7 |
8 | namespace SwitcherLib.Callbacks
9 | {
10 | class UploadLock : IBMDSwitcherLockCallback
11 | {
12 | private Upload upload;
13 |
14 | public UploadLock(Upload upload)
15 | {
16 | this.upload = upload;
17 | }
18 |
19 | public void Obtained()
20 | {
21 | Log.Debug("Still upload lock obtained");
22 | this.upload.LockCallback();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/SwitcherLib/ConsoleUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace SwitcherLib
9 | {
10 | public class ConsoleUtils
11 | {
12 | public static void Version()
13 | {
14 | Version version = Assembly.GetEntryAssembly().GetName().Version;
15 | AssemblyTitleAttribute title = (AssemblyTitleAttribute)Assembly.GetEntryAssembly().GetCustomAttribute(typeof(AssemblyTitleAttribute));
16 | Console.Out.WriteLine(String.Format("{0} {1}.{2}.{3}", title.Title, version.Major.ToString(), version.Minor.ToString(), version.Revision.ToString()));
17 | Console.Out.WriteLine("Jessica Smith ");
18 | Console.Out.WriteLine("This software is released under the MIT License");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SwitcherLib/Log.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace SwitcherLib
8 | {
9 | public class Log
10 | {
11 | public enum Level
12 | {
13 | Debug,
14 | Info,
15 | Notice,
16 | Warning,
17 | Error,
18 | }
19 |
20 | public static Log.Level CurrentLevel = Log.Level.Info;
21 |
22 | public static void Debug(string message)
23 | {
24 | Log.LogMessage(Log.Level.Debug, message);
25 | }
26 |
27 | public static void Info(string message)
28 | {
29 | Log.LogMessage(Log.Level.Info, message);
30 | }
31 |
32 | public static void Notice(string message)
33 | {
34 | Log.LogMessage(Log.Level.Notice, message);
35 | }
36 |
37 | public static void Warning(string message)
38 | {
39 | Log.LogMessage(Log.Level.Warning, message);
40 | }
41 |
42 | public static void Error(string message)
43 | {
44 | Log.LogMessage(Log.Level.Error, message);
45 | }
46 |
47 | protected static void LogMessage(Log.Level level, string message)
48 | {
49 | if (level < Log.CurrentLevel)
50 | {
51 | return;
52 | }
53 | Console.Out.WriteLine(message);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/SwitcherLib/MediaStill.cs:
--------------------------------------------------------------------------------
1 | using BMDSwitcherAPI;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace SwitcherLib
9 | {
10 | public class MediaStill
11 | {
12 | public String Name;
13 | public String Hash;
14 | public int Slot;
15 | public int MediaPlayer;
16 |
17 | public MediaStill()
18 | {
19 | }
20 |
21 | public MediaStill(IBMDSwitcherStills stills, uint index)
22 | {
23 | BMDSwitcherHash hash;
24 | stills.GetHash(index, out hash);
25 | this.Hash = String.Join("", BitConverter.ToString(hash.data).Split('-'));
26 | stills.GetName(index, out this.Name);
27 | this.Slot = (int)index + 1;
28 | }
29 |
30 | public String ToCSV()
31 | {
32 | return String.Join(",", this.Slot.ToString(), "\"" + this.Name + "\"", "\"" + this.Hash + "\"", this.MediaPlayer.ToString());
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SwitcherLib/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ATEM Switcher Library")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ATEM Switcher Library")]
13 | [assembly: AssemblyCopyright("© Copyright Jessica Smith, 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("b8ea4e08-548d-49a9-8fa0-50996b2d0362")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("2.0.1.0")]
36 | [assembly: AssemblyFileVersion("2.0.1.0")]
37 |
--------------------------------------------------------------------------------
/SwitcherLib/Switcher.cs:
--------------------------------------------------------------------------------
1 | using BMDSwitcherAPI;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace SwitcherLib
10 | {
11 | public class Switcher
12 | {
13 | protected IBMDSwitcher switcher;
14 | protected String deviceAddress;
15 | protected bool connected;
16 |
17 | public Switcher(string deviceAddress)
18 | {
19 | this.deviceAddress = deviceAddress;
20 | }
21 |
22 | public IBMDSwitcher GetSwitcher()
23 | {
24 | return this.switcher;
25 | }
26 |
27 | public void Connect()
28 | {
29 | if (this.connected)
30 | {
31 | return;
32 | }
33 |
34 | IBMDSwitcherDiscovery switcherDiscovery = new CBMDSwitcherDiscovery();
35 | _BMDSwitcherConnectToFailure failReason = 0;
36 |
37 | try
38 | {
39 | switcherDiscovery.ConnectTo(this.deviceAddress, out this.switcher, out failReason);
40 | this.connected = true;
41 | }
42 | catch (COMException ex)
43 | {
44 | switch (failReason)
45 | {
46 | case _BMDSwitcherConnectToFailure.bmdSwitcherConnectToFailureIncompatibleFirmware:
47 | throw new SwitcherLibException("Incompatible firmware");
48 |
49 | case _BMDSwitcherConnectToFailure.bmdSwitcherConnectToFailureNoResponse:
50 | throw new SwitcherLibException(String.Format("No response from {0}", this.deviceAddress));
51 |
52 | default:
53 | throw new SwitcherLibException(String.Format("Unknown Error: {0}", ex.Message));
54 | }
55 | }
56 | catch (Exception ex)
57 | {
58 | throw new SwitcherLibException(String.Format("Unable to connect to switcher: {0}", ex.Message));
59 | }
60 | }
61 |
62 | public String GetProductName()
63 | {
64 | this.Connect();
65 | String productName;
66 | this.switcher.GetProductName(out productName);
67 | return productName;
68 | }
69 |
70 | public int GetVideoHeight()
71 | {
72 | this.Connect();
73 | _BMDSwitcherVideoMode videoMode;
74 | this.switcher.GetVideoMode(out videoMode);
75 | _BMDSwitcherVideoMode switcherVideoMode = videoMode;
76 | switch (switcherVideoMode)
77 | {
78 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode4KHDp2398:
79 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode4KHDp24:
80 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode4KHDp25:
81 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode4KHDp2997:
82 | return 2160;
83 |
84 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode720p50:
85 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode720p5994:
86 | return 720;
87 |
88 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode1080i50:
89 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode1080i5994:
90 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode1080p50:
91 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode1080p2398:
92 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode1080p24:
93 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode1080p25:
94 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode1080p2997:
95 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode1080p5994:
96 | return 1080;
97 |
98 | case _BMDSwitcherVideoMode.bmdSwitcherVideoMode525i5994NTSC:
99 | return 480;
100 | default:
101 | throw new SwitcherLibException(String.Format("Unsupported resolution: {0}", videoMode.ToString()));
102 | }
103 | }
104 |
105 | public int GetVideoWidth()
106 | {
107 | int videoHeight = this.GetVideoHeight();
108 | switch (videoHeight)
109 | {
110 | case 720:
111 | return 1280;
112 |
113 | case 1080:
114 | return 1920;
115 |
116 | case 2160:
117 | return 3840;
118 |
119 | case 480:
120 | return 720;
121 | default:
122 | throw new SwitcherLibException(String.Format("Unsupported video height: {0}", videoHeight.ToString()));
123 | }
124 | }
125 |
126 | public IList GetStills()
127 | {
128 | IList list = new List();
129 |
130 | IBMDSwitcherMediaPool switcherMediaPool = (IBMDSwitcherMediaPool)this.switcher;
131 |
132 | IBMDSwitcherStills stills;
133 | switcherMediaPool.GetStills(out stills);
134 |
135 | uint count;
136 | stills.GetCount(out count);
137 | for (uint index = 0; index < count; index++)
138 | {
139 | MediaStill mediaStill = new MediaStill(stills, index);
140 | list.Add(mediaStill);
141 | }
142 |
143 | IntPtr mediaPlayerIteratorPtr;
144 | Guid mediaIteratorIID = typeof(IBMDSwitcherMediaPlayerIterator).GUID;
145 | this.switcher.CreateIterator(ref mediaIteratorIID, out mediaPlayerIteratorPtr);
146 | IBMDSwitcherMediaPlayerIterator mediaPlayerIterator = (IBMDSwitcherMediaPlayerIterator)Marshal.GetObjectForIUnknown(mediaPlayerIteratorPtr);
147 |
148 | IBMDSwitcherMediaPlayer mediaPlayer;
149 | mediaPlayerIterator.Next(out mediaPlayer);
150 | int num1 = 1;
151 | while (mediaPlayer != null)
152 | {
153 | _BMDSwitcherMediaPlayerSourceType type;
154 | uint index;
155 | mediaPlayer.GetSource(out type, out index);
156 | if (type == _BMDSwitcherMediaPlayerSourceType.bmdSwitcherMediaPlayerSourceTypeStill)
157 | {
158 | int num2 = (int)index + 1;
159 | foreach (MediaStill mediaStill in list)
160 | {
161 | if (mediaStill.Slot == num2)
162 | {
163 | mediaStill.MediaPlayer = num1;
164 | break;
165 | }
166 | }
167 | }
168 | num1++;
169 | mediaPlayerIterator.Next(out mediaPlayer);
170 | }
171 | return list;
172 | }
173 |
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/SwitcherLib/SwitcherLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {5F9F52A6-9EF4-4C71-9A91-F5B864A57ABB}
8 | Library
9 | Properties
10 | SwitcherLib
11 | SwitcherLib
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {8A92B919-156C-4D61-94EF-03F9BE4004B0}
59 | 1
60 | 0
61 | 0
62 | tlbimp
63 | False
64 | True
65 |
66 |
67 |
68 |
75 |
--------------------------------------------------------------------------------
/SwitcherLib/SwitcherLibException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SwitcherLib
4 | {
5 | public class SwitcherLibException : Exception
6 | {
7 | public SwitcherLibException()
8 | {
9 | }
10 |
11 | public SwitcherLibException(string message)
12 | : base(message)
13 | {
14 | }
15 |
16 | public SwitcherLibException(string message, Exception inner)
17 | : base(message, inner)
18 | {
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SwitcherLib/Upload.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Drawing;
7 | using BMDSwitcherAPI;
8 | using System.IO;
9 | using System.Runtime.InteropServices;
10 | using SwitcherLib.Callbacks;
11 |
12 | namespace SwitcherLib
13 | {
14 | public class Upload
15 | {
16 | private enum Status
17 | {
18 | NotStarted,
19 | Started,
20 | Completed,
21 | }
22 |
23 | private Upload.Status currentStatus;
24 | private String filename;
25 | private int uploadSlot;
26 | private String name;
27 | private Switcher switcher;
28 | private IBMDSwitcherFrame frame;
29 | private IBMDSwitcherStills stills;
30 | private IBMDSwitcherLockCallback lockCallback;
31 |
32 | public Upload(Switcher switcher, String filename, int uploadSlot)
33 | {
34 | this.switcher = switcher;
35 | this.filename = filename;
36 | this.uploadSlot = uploadSlot;
37 |
38 | if (!File.Exists(filename))
39 | {
40 | throw new SwitcherLibException(String.Format("{0} does not exist", filename));
41 | }
42 |
43 | this.switcher.Connect();
44 | this.stills = this.GetStills();
45 | }
46 |
47 | public bool InProgress()
48 | {
49 | return this.currentStatus == Upload.Status.Started;
50 | }
51 |
52 | public void SetName(String name)
53 | {
54 | this.name = name;
55 | }
56 |
57 | public int GetProgress()
58 | {
59 | if (this.currentStatus == Upload.Status.NotStarted)
60 | {
61 | return 0;
62 | }
63 | if (this.currentStatus == Upload.Status.Completed)
64 | {
65 | return 100;
66 | }
67 |
68 | double progress;
69 | this.stills.GetProgress(out progress);
70 | return (int)Math.Round(progress * 100.0);
71 | }
72 |
73 | public void Start()
74 | {
75 | this.currentStatus = Upload.Status.Started;
76 | this.frame = this.GetFrame();
77 | this.lockCallback = (IBMDSwitcherLockCallback)new UploadLock(this);
78 | this.stills.Lock(this.lockCallback);
79 | }
80 |
81 | protected IBMDSwitcherFrame GetFrame()
82 | {
83 | IBMDSwitcherMediaPool switcherMediaPool = (IBMDSwitcherMediaPool)this.switcher.GetSwitcher();
84 | IBMDSwitcherFrame frame;
85 | switcherMediaPool.CreateFrame(_BMDSwitcherPixelFormat.bmdSwitcherPixelFormat8BitARGB, (uint)this.switcher.GetVideoWidth(), (uint)this.switcher.GetVideoHeight(), out frame);
86 | IntPtr buffer;
87 | frame.GetBytes(out buffer);
88 | byte[] source = this.ConvertImage();
89 | Marshal.Copy(source, 0, buffer, source.Length);
90 | return frame;
91 | }
92 |
93 | protected byte[] ConvertImage()
94 | {
95 | try
96 | {
97 | Bitmap image = new Bitmap(this.filename);
98 |
99 | if (image.Width != this.switcher.GetVideoWidth() || image.Height != this.switcher.GetVideoHeight())
100 | {
101 | throw new SwitcherLibException(String.Format("Image is {0}x{1} it needs to be the same resolution as the switcher", image.Width.ToString(), image.Height.ToString()));
102 | }
103 |
104 | byte[] numArray = new byte[image.Width * image.Height * 4];
105 | for (int index1 = 0; index1 < image.Width * image.Height; index1++)
106 | {
107 | Color pixel = this.GetPixel(image, index1);
108 | int index2 = index1 * 4;
109 | numArray[index2] = pixel.B;
110 | numArray[index2 + 1] = pixel.G;
111 | numArray[index2 + 2] = pixel.R;
112 | numArray[index2 + 3] = pixel.A;
113 | }
114 | return numArray;
115 | }
116 | catch (Exception ex)
117 | {
118 | throw new SwitcherLibException(ex.Message, ex);
119 | }
120 | }
121 |
122 | protected Color GetPixel(Bitmap image, int index)
123 | {
124 | int x = index % image.Width;
125 | int y = (index - x) / image.Width;
126 | return image.GetPixel(x, y);
127 | }
128 |
129 | protected IBMDSwitcherStills GetStills()
130 | {
131 | IBMDSwitcherMediaPool switcherMediaPool = (IBMDSwitcherMediaPool)this.switcher.GetSwitcher();
132 | IBMDSwitcherStills stills;
133 | switcherMediaPool.GetStills(out stills);
134 | return stills;
135 | }
136 |
137 | public void UnlockCallback()
138 | {
139 | this.currentStatus = Upload.Status.Completed;
140 | }
141 |
142 | public void LockCallback()
143 | {
144 | IBMDSwitcherStillsCallback callback = (IBMDSwitcherStillsCallback)new Stills(this);
145 | this.stills.AddCallback(callback);
146 | this.stills.Upload((uint)this.uploadSlot, this.GetName(), this.frame);
147 | }
148 |
149 | public void TransferCompleted()
150 | {
151 | Log.Debug("Completed upload");
152 | this.stills.Unlock(this.lockCallback);
153 | this.currentStatus = Upload.Status.Completed;
154 | }
155 |
156 | public String GetName()
157 | {
158 | if (this.name != null)
159 | {
160 | return this.name;
161 | }
162 | return Path.GetFileNameWithoutExtension(this.filename);
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Blackmagic ATEM C# Library
2 |
3 | ## Introduction
4 |
5 | This library is a collection of tools for working with Blackmagic ATEM video switchers. It is intended to be used as part of automation solutions.
6 |
7 | MediaUpload.exe allows you to upload images to specific slots in a BlackMagic ATEM switcher's media pool.
8 | MediaPool.exe lists all the media in the switcher's media pool.
9 |
10 | ## Usage
11 |
12 | ### Media Upload
13 |
14 | ```
15 | MediaUpload.exe [options]
16 |
17 | Arguments:
18 |
19 | hostname - The hostname or IP address of the switcher
20 | slot - The slot to upload to
21 | filename - The filename of the image to upload
22 |
23 | Options:
24 |
25 | -h, --help - Help information
26 | -d, --debug - Enable debug output
27 | -v, --version - View version information
28 | -n, --name - Set the name of the image in the media pool
29 | ```
30 |
31 | Example:
32 |
33 | To upload myfile.png to Slot 1 on a switcher at 192.168.0.254:
34 |
35 | MediaUpload.exe 192.168.0.254 1 myfile.png
36 |
37 | ### Media Pool
38 |
39 | ```
40 | MediaPool.exe [options]
41 |
42 | Arguments:
43 |
44 | hostname - The hostname or IP of the ATEM switcher
45 |
46 | Options:
47 |
48 | -h, --help - This help message
49 | -d, --debug - Debug output
50 | -v, --version - Version information
51 | -f, --format - The output format. Either xml, csv, json or text
52 | ```
53 |
54 | Example:
55 |
56 | To see what's in the media pool for a switcher at 192.168.0.254:
57 |
58 | MediaPool.exe 192.168.0.254
59 |
60 | To view the output in JSON format:
61 |
62 | MediaPool.exe -f json 192.168.0.254
63 |
64 | ## Requirements
65 |
66 | - [Microsoft .NET Framework 4.5](http://www.microsoft.com/en-gb/download/details.aspx?id=30653)
67 | - [Blackmagic ATEM Switchers Update 7.3](https://www.blackmagicdesign.com/uk/support/family/atem-live-production-switchers) or later
68 |
69 | ## Supported Image Formats
70 |
71 | The Windows GD+ library is used for image manipulation. This currently supports:
72 |
73 | - PNG
74 | - BMP
75 | - JPEG
76 | - GIF
77 | - TIFF
78 |
79 | Alpha channels are supported and will be included in the images sent to the switcher.
80 |
81 | Images will need to be the same resolution as the switcher. Running in debug mode you can see the detected resolution on the switcher.
82 |
83 | ## Notes
84 |
85 | This has been tested with a Blackmagic Design ATEM Production Studio 4K. I do not have access to any other switchers to test with, but if they use version 6.2 or greater of the SDK, then they should work.
86 |
87 |
88 | ## Contact Details
89 |
90 | If you're using this for anything interesting, I'd love to hear about it.
91 |
92 | - Web: http://www.mintopia.net
93 | - Email: jess@mintopia.net
94 | - Twitter: @MintopiaUK
95 |
96 | - Bitcoin: 1FhMKKabMSJx4M4Trm73JTTrALg7DmxbbP
97 | - Ethereum: 0x8063501c3944846579fb62aaAe3965d933638f35
98 |
99 | ## ChangeLog
100 |
101 | ### Version 2.0.2 - 2018-02-02:
102 | - Add support for NTSC SD
103 |
104 | ### Version 2.0.1 - 2018-02-01:
105 | - Built against Blackmagic Switcher SDK 7.3
106 |
107 | ### Version 2.0.0 - 2014-12-24:
108 | - Rebuilt from decompiled source of original binary
109 | - Added enumerating of the media pool
110 |
111 | ### Version 1.0.1 - 2014-09-22:
112 | - Moved switcher functions into a separate library to allow development of more tools
113 | - Slight change to arguments
114 | - Add support for specifying the name of the image when uploading it
115 |
116 | ### Version 1.0.0 - 2014-09-21:
117 | - Initial version
118 |
119 | ## MIT License
120 |
121 | Copyright (C) 2016 by Jessica Smith
122 |
123 | Permission is hereby granted, free of charge, to any person obtaining a copy
124 | of this software and associated documentation files (the "Software"), to deal
125 | in the Software without restriction, including without limitation the rights
126 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
127 | copies of the Software, and to permit persons to whom the Software is
128 | furnished to do so, subject to the following conditions:
129 |
130 | The above copyright notice and this permission notice shall be included in
131 | all copies or substantial portions of the Software.
132 |
133 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
134 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
135 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
136 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
137 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
138 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
139 | THE SOFTWARE.
140 |
--------------------------------------------------------------------------------