I want to mux webcam capture with audio capture in an avi mux and write that file to disk.
This works in graphedit.
I try to recreate this in c# with directshowlib. This works so far but the only without the microphone capture. My microphone filter is created but has no pins. I tried this on two different laptops. My code for the microphone filter:
Guid microphonFilter = new Guid("{E30629D2-27E5-11CE-875D-00608CB78066}");
IBaseFilter pMikrofonRealtekHighDefinitionAudio = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(microphonFilter));
hr = pGraph.AddFilter(pMikrofonRealtekHighDefinitionAudio, "Mikrofon (Realtek High Definition Audio) ");
I also tried:
IBaseFilter microphonFilter = (IBaseFilter) new AudioRecord();
My code for finding pins:
static IPin GetPin(IBaseFilter filter, string pinname)
{
IEnumPins epins;
int hr = filter.EnumPins(out epins);
checkHR(hr, "Can't enumerate pins");
IntPtr fetched = Marshal.AllocCoTaskMem(4);
IPin[] pins = new IPin[1];
while (epins.Next(1, pins, fetched) == 0)
{
PinInfo pinfo;
pins[0].QueryPinInfo(out pinfo);
bool found = (pinfo.name == pinname);
DsUtils.FreePinInfo(pinfo);
if (found)
return pins[0];
}
checkHR(-1, "Pin not found");
return null;
}
Audio (and video) capture devices like this cannot be instantiated using CoCreateInstance (using CLSID - Activator.CreateInstance in C#). You have to create them using monikers, typically through enumeration, as described on MSDN (with source code snippet): Selecting a Capture Device.
Below is code snippet from DirectShow.NET samples for video capture, you need similar for audio device category.
// This version of FindCaptureDevice is provide for education only.
// A second version using the DsDevice helper class is define later.
public IBaseFilter FindCaptureDevice()
{
// ...
// Create the system device enumerator
ICreateDevEnum devEnum = (ICreateDevEnum) new CreateDevEnum();
// Create an enumerator for the video capture devices
hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, 0);
DsError.ThrowExceptionForHR(hr);
// The device enumerator is no more needed
Marshal.ReleaseComObject(devEnum);
// If there are no enumerators for the requested type, then
// CreateClassEnumerator will succeed, but classEnum will be NULL.
if (classEnum == null)
{
throw new ApplicationException("No video capture device was detected.\r\n\r\n" +
"This sample requires a video capture device, such as a USB WebCam,\r\n" +
"to be installed and working properly. The sample will now close.");
}
Related
I have a problem with the below code. I want to scan a document by clicking a button in a WinForms C# application.
I use WIA, Visual studio and the scanner Fujitsu N7100A working with Windows 8. I am following a tutorial online for using WIA.
But the program doesn't run as expected. It seems to break down at the Transfer method.
// Create a DeviceManager instance
var deviceManager = new DeviceManager();
// Create an empty variable to store the scanner instance
DeviceInfo firstScannerAvailable = null;
// Loop through the list of devices to choose the first available
AddLogs(deviceManager.DeviceInfos.Count.ToString(), filename);
foreach (DeviceInfo d in deviceManager.DeviceInfos)
{
if (d.Type == WiaDeviceType.ScannerDeviceType)
{
firstScannerAvailable = d;
}
}
// Connect to the first available scanner
var device = firstScannerAvailable.Connect();
// Select the scanner
var scannerItem = device.Items[0];
// Retrieve a image in JPEG format and store it into a variable
var imageFile = (ImageFile)scannerItem.Transfer(FormatID.wiaFormatPNG);
//Save the image in some path with filename
var path = #"C:\Documents\scan.png";
if (File.Exists(path))
{
File.Delete(path);
}
// Save image !
imageFile.SaveFile(path);
I just have to remove the addition of lines in the file of log.
This is much more of a workaround since i have no idea about your scanner.
I would assume that all scanners has a drive where they store their scanned documents, like mine, So i would suggest that you read all available drives loop through them check for DriveType and VolumeLabel and then read it's files and copy the document where you want
Something like this :
foreach (var item in DriveInfo.GetDrives())
{
//VolumeLabel differs from a scanner to another
if (item.VolumeLabel == "Photo scan" && item.DriveType == DriveType.Removable)
{
foreach (var obj in Directory.GetFiles(item.Name))
{
File.Copy(obj, "[YOUR NEW PATH]");
break;
}
break;
}
}
Finaly a TWAIN application work with this scanner. I will work with that. I don't said why do that work with TWAIN and not with WIA but that the reality. Sorry for this waste of time. Thank you for the answers. Have a nice day.
I am currently solving this very problem. It seems the N7100A driver sets the Pages property of the device to 0, which should mean continous scanning, but the transfer method is unable to handle this value. You must set that property to 1:
var pages = 1;
// Not all devices have this property, but Fujitsu N7100A has.
device.Properties["Pages"]?.set_Value(ref pages);
I think the problem is here
var scannerItem = device.Items[0];
as WIA indexes are NOT zero based so it should be 1 instead
var scannerItem = device.Items[1];
I am trying to communicate with a Nokia Lumia phone(RM-917), over USB using LIBUSING and C#. LIBUSB is able to see the device's information(pid,vid,etc). However, I am not able to successfully write to ANY endpoint, even sending the exact command as the Windows Device Recovery Tool.
According to WinUSB, the write endpoint is EP07, however, this endpoint just times out. I have tried every other endpoint, and all of these fail.
`
public void initDevice()
{
if(this.lumiaDevice == null)
{
throw new Exception("LumiaPhoneManager does not have a selected device");
}
UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(0x0421, 0x0661);
MyUsbDevice = UsbDevice.OpenUsbDevice(MyUsbFinder);
IUsbDevice wholeUsbDevice = MyUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// This is a "whole" USB device. Before it can be used,
// the desired configuration and interface must be selected.
// Select config #1
wholeUsbDevice.SetConfiguration(1);
// Claim interface #0.
wholeUsbDevice.ClaimInterface(1);
}
if (this.writer == null)
{
writer = MyUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep07);
}
}
public void readPCode()
{
currentID++;
var _x = new jsonPkt();
ErrorCode ec = ErrorCode.None;
int bytesWritten;
_x.id = this.currentID + 1;
_x.method = "ReadProductCode";
string value = #"{""jsonrpc"":""<JSONRPC>"",""id"":<ID>,""method"":""<METHOD>"",""params"":null}";
value = value.Replace("<JSONRPC>", "2.0");
value = value.Replace("<ID>", currentID.ToString());
value = value.Replace("<METHOD>", _x.method.ToString());
ec = writer.Write(Encoding.Default.GetBytes(value), 8000, out bytesWritten);
currentID++;
if (ec != ErrorCode.None) throw new Exception(UsbDevice.LastErrorString);
byte[] readBuffer = new byte[1024];
while (ec == ErrorCode.None)
{
int bytesRead;
// If the device hasn't sent data in the last 100 milliseconds,
// a timeout error (ec = IoTimedOut) will occur.
ec = reader.Read(readBuffer, 100, out bytesRead);
// if (bytesRead == 0) throw new Exception("No more bytes!");
// Write that output to the console.
this.rtb.Text += Encoding.Default.GetString(readBuffer, 0, bytesRead).ToString() + "\n";
}
}
Found the solution
Debugged the OEM software and found the program was using a different path to the USB device. After that I was getting access denied errors, which was solved by moving the project to a different drive. For reasons unknown, when the program runs on c drive, the CreateFile function fails with access denied
Its possible that to activate write, you need to send some class specific control request first. You mentioned that windows device recovery tool is able to write.
You can install USB packet sniffer software in your windows PC and then use the device manager to write some data to the device. Packet sniffer tool will be able to capture all the packets sent to the device.
This way you can see the exact device requests which are required to enable write operation.
Analyzer software - http://www.usblyzer.com/
Please try this way to resolve your problem.
PS- I am assuming you do not have a hardware USB packet analyzer like Lecroy advisor or Beagle. Software packet sniffer should be fine since the host is a PC.
I try to send the input from a microphone to the speaker and a connected headset simultaneously.
I know how to do this with a file and a device but I don't know how to connect the second device with an output node and attach it to the graph.
I can create nodes with CreateDeviceOutputNodeAsync but I have no clue hwo to associate the device with the node.
For one device it works when I give the device as the PrimaryRenderDevice in the settings when the graph is created.
This is the working code for one output device. I don't know how fix this to make it work for two output devices.
private async Task CreateAudioGraph()
{
AudioGraphSettings settings = new AudioGraphSettings(AudioRenderCategory.Speech);
settings.QuantumSizeSelectionMode = QuantumSizeSelectionMode.LowestLatency;
settings.DesiredRenderDeviceAudioProcessing = Windows.Media.AudioProcessing.Raw; // this is also set automatically when LowestLatency is set
settings.PrimaryRenderDevice = OutputDeviceForInput;
CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);
if (result.Status != AudioGraphCreationStatus.Success)
{
// Cannot create graph
ConnectionStatus = String.Format("AudioGraph Creation Error because {0}", result.Status.ToString());
return;
}
graphForInputDeviceNo1 = result.Graph;
ConnectionStatus = "Graph successfully created!";
// Create a device input node using the selectedt input device
CreateAudioDeviceInputNodeResult deviceInputNodeResult = await graphForInputDeviceNo1.CreateDeviceInputNodeAsync(MediaCategory.Speech, graphForInputDeviceNo1.EncodingProperties, FirstInputDevice);
if (deviceInputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
{
// Cannot create device input node
ConnectionStatus = String.Format("Audio Device Input unavailable because {0}", deviceInputNodeResult.Status.ToString());
return;
}
deviceInputNode = deviceInputNodeResult.DeviceInputNode;
// Create the first device output node
CreateAudioDeviceOutputNodeResult deviceOutputNodeNo1Result = await graphForInputDeviceNo1.CreateDeviceOutputNodeAsync();
if (deviceOutputNodeNo1Result.Status != AudioDeviceNodeCreationStatus.Success)
{
// Cannot create device output node
ConnectionStatus = String.Format("Audio Device Output unavailable because {0}", deviceOutputNodeNo1Result.Status.ToString());
return;
}
deviceOutputNodeNo1 = deviceOutputNodeNo1Result.DeviceOutputNode;
// Create the second device output node
CreateAudioDeviceOutputNodeResult deviceOutputNodeNo2Result = await graphForInputDeviceNo1.CreateDeviceOutputNodeAsync();
if (deviceOutputNodeNo2Result.Status != AudioDeviceNodeCreationStatus.Success)
{
// Cannot create device output node
ConnectionStatus = String.Format("Audio Device Output unavailable because {0}", deviceOutputNodeNo2Result.Status.ToString());
return;
}
deviceOutputNodeNo2 = deviceOutputNodeNo2Result.DeviceOutputNode;
ConnectionStatus = "Device Output connection successfully created";
deviceInputNode.AddOutgoingConnection(deviceOutputNodeNo1);
ConnectionStatus = "Device Input connection successfully created";
deviceInputNode.AddOutgoingConnection(deviceOutputNodeNo2);
// Because we are using lowest latency setting, we need to handle device disconnection errors
graphForInputDeviceNo1.UnrecoverableErrorOccurred += Graph_UnrecoverableErrorOccurred;
graphForInputDeviceNo1.Start(); // erst damit geht es los
}
The MSDN document is a bit confusing:
Creates a new AudioDeviceOutputNode that outputs audio data from the audio graph to the system's default output device, such as speakers or headphones.
In fact, the output device is not necessary the system's default output device. However, since you are not able to change the Device property of the node, and the constructor does not give you that option, it seems it must be from the system settings.
However, there are another place we can set this up: the AudioGraphSettings class allows you to set the PrimaryRenderDevice property, and this is actually the device will be used when creating the output node.
You can try the following:
var settings = new AudioGraphSettings(AudioRenderCategory.Other);
settings.PrimaryRenderDevice = device_other_than_system_output;
var ag_result = await AudioGraph.CreateAsync(settings);
var ag = ag_result.Graph;
var result = await ag.CreateDeviceOutputNodeAsync();
assert(result.DeviceOutputNode == device_other_than_system_output);
Technically, this didn't solve your problem since a AudioGraph is still not able to have two output nodes. However, you can try to create multiple AudioGraphs that consumes the same input(s), and output to different render devices, to have the same effect as you wish.
I have an issue with a UWP app that I am trying to write. I am connecting to a custom embedded USB Bulk device that I have programmed (it is actually an out of the box example from Cypress Semiconductor). I am using the WinUSB.sys driver using the embedded MS OS string in the device to allow the device to be used with out having to write a custom INF file to call the WinUSB.sys driver.
In my code, I am using the UsbDevice.GetDeviceSelector method to return an AQS that can then be passed into DeviceInformation.FindAllAsync to begin communicating with the device in my app. I have confirmed that the device shows up in the device manager without any issues, and I have checked in the registry to ensure that it has an Interface GUID. I have a screenshot from USBViewer to show the configuration of the device. This method for finding and connecting with USB devices is from this MSDN example found here.
When I use the UsbDevice.GetDeviceSelector method, it returns a GUID that is not associated with this device. The GUID that it returns is actually associated with Lumia Phones (DEE824EF-729B-4A0E-9C14-B7117D33A817). Because of this, it does not find my device connected to the system.
To troubleshoot, I have both called the DeviceInformation.FindAllAsync with out any arguments to see if my device is listed, and it does find the device (amongst over 1000 other devices that have been connected ever to my machine). I then wrote a custom AQS string without the help of the GetDeviceSelector method, starting with just the GUID. Doing this returned 27 devices, but when I tried to add the VID and PID to this AQS string, nothing returned.
I have also made sure that the device that I want to use is listed in the app manifest by its appropriate VID and PID as this is required for a device with a Custom Class of 0xFF. I have used the Custom USB UWP device example and it can find the device, though it uses a completely different method with a device picker, which I will go to if needed, but this is not my desire as it makes that part of the app not as clean of a solution.
I have posted this question over in the MSDN forums here with more information, but I have not gotten a lot of engagement there. Any help would be appreciated. I know that I must be missing something simple.
Adam
private async void button_Click(object sender, RoutedEventArgs e)
{
//UInt32 vid = 0x04B4;
//UInt32 pid = 0x00F0;
UInt32 vid = uint.Parse(textBox1.Text, System.Globalization.NumberStyles.HexNumber);
UInt32 pid = UInt32.Parse(textBox2.Text, System.Globalization.NumberStyles.HexNumber);
Guid winusbInterfaceGuid = new Guid("a5dcbf10-6530-11d2-901f-00c04fb951ed");
//string aqs = UsbDevice.GetDeviceSelector(vid, pid);
string aqs = UsbDevice.GetDeviceSelector(winusbInterfaceGuid);
var myDevices = await DeviceInformation.FindAllAsync(aqs, null);
//var myDevices = await DeviceInformation.FindAllAsync();
var myDevicesCount = myDevices.Count;
if (myDevicesCount >= 1)
{
textBlock2.Text = "Device Found";
} else
{
textBlock2.Text = "Searching";
await Task.Delay(1000);
textBlock2.Text = "looking for device";
}
}
just dropped you a mail asking about progress (I think, had to guess your mail address), but now it seems I found a solution myself. Please see my answer on UWP app cannot find/connect to USB device
In short, you have to create an inf for installing the winusb driver. I have no clue why, but that did the trick for me (and someone else, see Cannot create UsbDevice from DeviceInformation.Id)
The Guid DEE824EF-729B-4A0E-9C14-B7117D33A817 is actually the standard WinUSB Guid. I don't think it has anything to do with Lumia Phones. I don't know why it is not documented anywhere. I think that the Guid a5dcbf10-6530-11d2-901f-00c04fb951ed you specified is actually a red herring. I mistakenly used that as well, but it just led me down the garden path. It shows up USB interfaces, but I can't connect to them.
You might want to try this class https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net.UWP/UWPUsbDevice.cs .
Here is how it gets the device:
public async Task<IEnumerable<DeviceDefinition>> GetConnectedDeviceDefinitions(uint? vendorId, uint? productId)
{
var aqsFilter = "System.Devices.InterfaceClassGuid:=\"{DEE824EF-729B-4A0E-9C14-B7117D33A817}\" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True AND " + $" System.DeviceInterface.WinUsb.UsbVendorId:={vendorId.Value} AND System.DeviceInterface.WinUsb.UsbProductId:={productId.Value}";
var deviceInformationCollection = await wde.DeviceInformation.FindAllAsync(aqsFilter).AsTask();
//TODO: return the vid/pid if we can get it from the properties. Also read/write buffer size
var deviceIds = deviceInformationCollection.Select(d => new DeviceDefinition { DeviceId = d.Id, DeviceType = DeviceType.Usb }).ToList();
return deviceIds;
}
This sample connects to a device and I think you'll be able to connect to the device in the same way:
private static async Task InitializeTrezor()
{
//Register the factory for creating Usb devices. This only needs to be done once.
UWPUsbDeviceFactory.Register();
//Register the factory for creating Usb devices. This only needs to be done once.
UWPHidDeviceFactory.Register();
//Note: other custom device types could be added here
//Define the types of devices to search for. This particular device can be connected to via USB, or Hid
var deviceDefinitions = new List<DeviceDefinition>
{
new DeviceDefinition{ DeviceType= DeviceType.Hid, VendorId= 0x534C, ProductId=0x0001, Label="Trezor One Firmware 1.6.x" },
new DeviceDefinition{ DeviceType= DeviceType.Usb, VendorId= 0x1209, ProductId=0x53C1, ReadBufferSize=64, WriteBufferSize=64, Label="Trezor One Firmware 1.7.x" },
new DeviceDefinition{ DeviceType= DeviceType.Usb, VendorId= 0x1209, ProductId=0x53C0, ReadBufferSize=64, WriteBufferSize=64, Label="Model T" }
};
//Get the first available device and connect to it
var devices = await DeviceManager.Current.GetDevices(deviceDefinitions);
var trezorDevice = devices.FirstOrDefault();
await trezorDevice.InitializeAsync();
//Create a buffer with 3 bytes (initialize)
var buffer = new byte[64];
buffer[0] = 0x3f;
buffer[1] = 0x23;
buffer[2] = 0x23;
//Write the data to the device
await trezorDevice.WriteAsync(buffer);
//Read the response
var readBuffer = await trezorDevice.ReadAsync();
}
If you connect to the device in this way, you'll get Windows classic, and Android support for free with Device.Net (https://github.com/MelbourneDeveloper/Device.Net)
With Device.net's DeviceManager.Current.GetDevices(deviceDefinitions) using .NET 5 I can't find any device connected to my win10, which can be easily selected by ManagementObjectSearcher:
public List<ManagementBaseObject> GetLogicalDevices()
{
List<ManagementBaseObject> devices = new List<ManagementBaseObject>();
ManagementObjectCollection collection;
ManagementObjectSearcher seacher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM CIM_LogicalDevice");
collection = seacher.Get();
foreach (var device in collection)
{
devices.Add(device);
}
return devices;
}
I am trying to use WASAPI in C# but I could not even find which dll to reference in Visual Studio. Should I reference a dll in COM assemblies or download one from Microsoft website and reference it? Is there any documentation how to use the WASAPI in C#?
I want to use it to set microphone boost level. I have been using NAudio for that, but in Windows 8.1 it does not function properly, see this. It sets the boost level via winmm calls. I thought I could use WASAPI directly.
Edit
I have tried CSCore which has a wrapper for WASAPI calls for setting microphone boost. It successfully sets the value, but the program crashes everytime with Access Violation exception after setting the value. Here is the code for CSCore:
MMDeviceEnumerator deviceEnumerator = new MMDeviceEnumerator();
MMDeviceCollection deviceCollection = deviceEnumerator.EnumerateAudioEndPoints(EDataFlow.eCapture, DEVICE_STATE.DEVICE_STATE_ACTIVE);
MMDevice microphone = null;
for (int i = 0; i < deviceCollection.Count; i++)
{
MMDevice device = deviceCollection[i];
if (device.FriendlyName.Contains("Plantronics"))
{
microphone = device;
}
}
if (microphone != null && microphone.AudioSessionManager2.Sessions.Count < 1)
{
return;
}
AudioSessionControl2 activeSession = null;
for (int i = 0; i < microphone.AudioSessionManager2.Sessions.Count; i++)
{
if (microphone.AudioSessionManager2.Sessions[i].State == AudioSessionState.AudioSessionStateActive)
{
activeSession = microphone.AudioSessionManager2.Sessions[i];
}
}
if (activeSession == null)
{
return;
}
activeSession.SimpleAudioVolume.MasterVolume += 0.1f;
I have also found AudioSwitcher library which seems to have a wrapper too, however the microphone volume cannot be changed. It is always -1. Here is the code for AudioSwitcher:
CoreAudioController audioController = new CoreAudioController();
var devices = audioController.GetCaptureDevices(DeviceState.Active);
foreach (CoreAudioDevice device in devices)
{
if (device.FullName.Contains("Plantronics"))
{
device.Volume = 49;
}
}