00001
00002 using System;
00003 using System.IO;
00004 using System.Threading;
00005 using System.Diagnostics;
00006 using System.Collections.Generic;
00007 using NDesk.DBus;
00008 using org.freedesktop.Hal;
00009 using org.freedesktop.DBus;
00010
00011 namespace Foodolini.Activities.FoodRegistration.Devices{
00022 public class LinuxCaptureDeviceMonitor : ICaptureDeviceMonitor
00023 {
00024 private Bus dbus;
00025 private Thread worker;
00026 private HalManager hal;
00027 private bool disposed = false;
00028
00029 private LinuxCaptureDeviceMonitor(){
00030 this.dbus = Bus.System;
00031 this.worker = new Thread(this.iterate);
00032 this.worker.Start();
00033
00034 if(this.dbus.NameHasOwner("org.freedesktop.Hal")){
00035
00036 this.hal = this.dbus.GetObject<HalManager>("org.freedesktop.Hal", new ObjectPath("/org/freedesktop/Hal/Manager"));
00037
00038 foreach(ObjectPath path in this.hal.FindDeviceByCapability("video4linux.video_capture")){
00039 var device = this.DeviceInfo(path);
00040 if(device != null)
00041 this.devices.Add(path.ToString(), device);
00042 }
00043
00044 this.hal.DeviceAdded += HandleDeviceAdded;
00045 this.hal.DeviceRemoved += HandleDeviceRemoved;
00046 }else{
00047 this.hal = null;
00048 this.worker.Abort();
00049 this.worker = null;
00050 }
00051 }
00052
00053 #region HAL Signal handling
00054
00055 private DeviceEventArgs DeviceInfo(ObjectPath path){
00056 Device device = this.dbus.GetObject<Device>("org.freedesktop.Hal", path);
00057
00058 if(device.PropertyExists("info.capabilities")){
00059
00060 foreach(var cap in device.GetPropertyStringList("info.capabilities")){
00061 if(cap == "video4linux.video_capture"){
00062 string name = null;
00063 string devpath = device.GetPropertyString("video4linux.device");
00064
00065 if(device.PropertyExists("info.product"))
00066 name = device.GetPropertyString("info.product");
00067 else
00068 name = Path.GetFileName(devpath);
00069 return new DeviceEventArgs(name, devpath);
00070 }
00071 }
00072 }
00073 return null;
00074 }
00075
00079 void HandleDeviceAdded(ObjectPath path){
00080 var device = this.DeviceInfo(path);
00081 if(device != null){
00082 GLib.IdleHandler idle = delegate(){
00083 this.devices.Add(path.ToString(), device);
00084 if(this.DeviceAdded != null)
00085 this.DeviceAdded(this, device);
00086 return false;
00087 };
00088 GLib.Idle.Add(idle);
00089 }
00090 }
00091
00095 void HandleDeviceRemoved(ObjectPath path){
00096 GLib.IdleHandler idle = delegate(){
00097 IDeviceInfo device;
00098 if(this.devices.TryGetValue(path.ToString(), out device)){
00099 this.devices.Remove(path.ToString());
00100 if(this.DeviceRemoved != null)
00101 this.DeviceRemoved(this, (DeviceEventArgs)device);
00102 }
00103 return false;
00104 };
00105 GLib.Idle.Add(idle);
00106 }
00107
00108 #endregion
00109
00113 private void iterate(){
00114 while(true){
00115
00116 int fails = 0;
00117 try{
00118 this.dbus.Iterate();
00119 }
00120 catch(ThreadAbortException e){
00121 break;
00122 }
00123 catch(Exception e){
00124 fails++;
00125 Console.WriteLine("dbus misbehaved: " + e.Message);
00126 if(fails > 5)
00127 break;
00128 }
00129 }
00130 }
00131
00132 #region Singleton implementation
00133
00137 private static LinuxCaptureDeviceMonitor instance = null;
00138
00142 public static LinuxCaptureDeviceMonitor Instance{
00143 get{
00144 if(instance == null || instance.disposed){
00145 if(instance != null){
00146 instance.DeviceAdded = null;
00147 instance.DeviceRemoved = null;
00148 }
00149 instance = new LinuxCaptureDeviceMonitor();
00150 }
00151 return instance;
00152 }
00153 }
00154
00155 #endregion
00156
00161 private Dictionary<string, IDeviceInfo> devices = new Dictionary<string, IDeviceInfo>();
00162
00163 #region ICaptureDeviceMonitor implementation
00164
00169 public event EventHandler<DeviceEventArgs> DeviceAdded;
00170
00175 public event EventHandler<DeviceEventArgs> DeviceRemoved;
00176
00197 public ICollection<IDeviceInfo> Devices{
00198 get{
00199 if(this.hal != null)
00200 return this.devices.Values;
00201 else{
00202 var retval = new List<IDeviceInfo>();
00203 #if UDEV_RULES
00204 try{
00205
00206
00207 foreach(var device in Directory.GetFiles("/dev/v4l/by-id/")){
00208 var name = Path.GetFileName(device);
00209 name = name.Remove(0, name.IndexOf("_") + 1);
00210 name = name.Remove(name.IndexOf("-video-index"));
00211 if(name == string.Empty)
00212 throw new Exception("Hack that jumps aways from here :) ");
00213 retval.Add(new DeviceEventArgs(name, device));
00214 }
00215 }
00216 catch{
00217
00218 foreach(var device in Directory.GetFiles("/dev/", "video*")){
00219
00220 var name = "Video " + Path.GetFileName(device).Remove(0, 5);
00221 retval.Add(new DeviceEventArgs(name, device));
00222 }
00223 }
00224 #else
00225 foreach(var device in Directory.GetFiles("/dev/", "video*")){
00226 string name = null;
00227
00228 bool supports_capture = true;
00229
00230
00231
00232
00233
00234 try{
00235 using(Process v4l_id = new Process()){
00236 v4l_id.StartInfo.UseShellExecute = false;
00237 v4l_id.StartInfo.FileName = "/lib/udev/v4l_id";
00238 v4l_id.StartInfo.CreateNoWindow = true;
00239 v4l_id.StartInfo.Arguments = device;
00240 v4l_id.StartInfo.RedirectStandardOutput = true;
00241 v4l_id.Start();
00242 string input;
00243 while((input = v4l_id.StandardOutput.ReadLine()) != null){
00244 if(input.StartsWith("ID_V4L_PRODUCT="))
00245 name = input.Remove(0, 15);
00246 if(input.StartsWith("ID_V4L_CAPABILITIES="))
00247 supports_capture = input.Contains(":capture:");
00248 }
00249 v4l_id.WaitForExit();
00250 if(v4l_id.ExitCode != 0)
00251 throw new Exception("Don't use anything we've got here...");
00252 }
00253 }
00254 catch(Exception e){
00255
00256 name = "Video " + System.IO.Path.GetFileName(device).Remove(0, 5);
00257 }
00258
00259 if(supports_capture)
00260 retval.Add(new DeviceEventArgs(name, device));
00261 }
00262 #endif
00263 return retval;
00264 }
00265 }
00266 }
00267
00271 public bool SupportsAutoRefresh{
00272 get{
00273 return true;
00274 }
00275 }
00276
00277 #endregion
00278
00279 #region IDisposable implementation
00280
00284 public void Dispose(){
00285 if(this.worker != null)
00286 this.worker.Abort();
00287 this.worker = null;
00288 this.DeviceAdded = null;
00289 this.DeviceRemoved = null;
00290 this.disposed = true;
00291 }
00292
00293 #endregion
00294 }
00295
00299 public static class CaptureDeviceMonitor{
00300 public static ICaptureDeviceMonitor Instance{
00301 get{
00302 return LinuxCaptureDeviceMonitor.Instance;
00303 }
00304 }
00305 }
00306 }
00307
00314 namespace org.freedesktop.Hal{
00325 [Interface("org.freedesktop.Hal.Manager")]
00326 interface HalManager : Introspectable{
00327
00328 ObjectPath[] FindDeviceByCapability(string capability);
00329 event DeviceHandler DeviceAdded;
00330 event DeviceHandler DeviceRemoved;
00331 }
00332
00336 public delegate void DeviceHandler(ObjectPath path);
00337
00348 [Interface("org.freedesktop.Hal.Device")]
00349 interface Device : Introspectable{
00350 string GetPropertyString(string name);
00351 string[] GetPropertyStringList(string name);
00352 bool PropertyExists(string name);
00353 }
00354 }