I am developing a virtual reality windows based game. I will be using an accelerometer sensor to get the hand movements of the player and use it in the game, where as the player will use it to fight an enemy in the game. I managed to get the accelerometer readings from the sensor.
I just need an idea on how I can now integrate it with my game in Unity. I used .Net to get the readings from the sensor. This is a TI simple link sensor tag (CC2650STK). This will connect via Bluetooth to my Windows phone.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Storage.Streams;
using Buffer = Windows.Storage.Streams.Buffer;
namespace SensorTag
{
/// This class provides access to the SensorTag Accelerometer BLE data
public class BleAccelerometerService : BleGenericGattService
{
public BleAccelerometerService()
{
}
/// The version of the SensorTag device. 1=CC2541, 2=CC2650.
public int Version { get; set; }
static Guid AccelerometerServiceUuid = Guid.Parse("f000aa10-0451-4000-b000-000000000000");
static Guid AccelerometerCharacteristicUuid = Guid.Parse("f000aa11-0451-4000-b000-000000000000");
static Guid AccelerometerCharacteristicConfigUuid = Guid.Parse("f000aa12-0451-4000-b000-000000000000");
static Guid AccelerometerCharacteristicPeriodUuid = Guid.Parse("f000aa13-0451-4000-b000-000000000000");
Delegate _accelerometerValueChanged;
public event EventHandler<AccelerometerMeasurementEventArgs> AccelerometerMeasurementValueChanged
{
add
{
if (_accelerometerValueChanged != null)
{
_accelerometerValueChanged = Delegate.Combine(_accelerometerValueChanged, value);
}
else
{
_accelerometerValueChanged = value;
RegisterForValueChangeEvents(AccelerometerCharacteristicUuid);
}
}
remove
{
if (_accelerometerValueChanged != null)
{
_accelerometerValueChanged = Delegate.Remove(_accelerometerValueChanged, value);
if (_accelerometerValueChanged == null)
{
UnregisterForValueChangeEvents(AccelerometerCharacteristicUuid);
}
}
}
}
private async Task<int> GetConfig()
{
var ch = GetCharacteristic(AccelerometerCharacteristicConfigUuid);
if (ch != null)
{
var properties = ch.CharacteristicProperties;
if ((properties & GattCharacteristicProperties.Read) != 0)
{
var result = await ch.ReadValueAsync();
IBuffer buffer = result.Value;
DataReader reader = DataReader.FromBuffer(buffer);
var value = reader.ReadByte();
Debug.WriteLine("Acceleration config = " + value);
return (int)value;
}
}
return -1;
}
bool isReading;
public async Task StartReading()
{
if (!isReading)
{
await WriteCharacteristicByte(AccelerometerCharacteristicConfigUuid, 1);
isReading = true;
}
}
public async Task StopReading()
{
if (isReading)
{
isReading = false;
await WriteCharacteristicByte(AccelerometerCharacteristicConfigUuid, 0);
}
}
/// Get the rate at which accelerometer is being polled, in milliseconds.
/// </summary>
/// <returns>Returns the value read from the sensor or -1 if something goes wrong.</returns>
public async Task<int> GetPeriod()
{
byte v = await ReadCharacteristicByte(AccelerometerCharacteristicPeriodUuid, Windows.Devices.Bluetooth.BluetoothCacheMode.Uncached);
return (int)(v * 10);
}
/// <summary>
/// Set the rate at which accelerometer is being polled, in milliseconds.
/// </summary>
/// <param name="milliseconds">The delay between updates, accurate only to 10ms intervals. Maximum value is 2550.</param>
public async Task SetPeriod(int milliseconds)
{
int delay = milliseconds / 10;
byte p = (byte)delay;
if (p < 1)
{
p = 1;
}
await WriteCharacteristicByte(AccelerometerCharacteristicPeriodUuid, p);
}
private void OnAccelerationMeasurementValueChanged(AccelerometerMeasurementEventArgs args)
{
if (_accelerometerValueChanged != null)
{
((EventHandler<AccelerometerMeasurementEventArgs>)_accelerometerValueChanged)(this, args);
}
}
public async Task<bool> ConnectAsync(string deviceContainerId)
{
return await this.ConnectAsync(AccelerometerServiceUuid, deviceContainerId);
}
protected override void OnCharacteristicValueChanged(GattCharacteristic sender, GattValueChangedEventArgs eventArgs)
{
if (sender.Uuid == AccelerometerCharacteristicUuid)
{
if (_accelerometerValueChanged != null)
{
uint dataLength = eventArgs.CharacteristicValue.Length;
using (DataReader reader = DataReader.FromBuffer(eventArgs.CharacteristicValue))
{
if (dataLength == 3)
{
var data = new byte[dataLength];
reader.ReadBytes(data);
AccelerometerMeasurement measurement = new AccelerometerMeasurement();
sbyte x = (sbyte)data[0];
sbyte y = (sbyte)data[1];
sbyte z = (sbyte)data[2];
measurement.X = (double)x / 64.0;
measurement.Y = (double)y / 64.0;
measurement.Z = (double)z / 64.0;
OnAccelerationMeasurementValueChanged(new AccelerometerMeasurementEventArgs(measurement, eventArgs.Timestamp));
}
}
}
}
}
}
public class AccelerometerMeasurement
{
/// <summary>
/// Get/Set X accelerometer in units of 1 g (9.81 m/s^2).
/// </summary>
public double X { get; set;}
/// <summary>
/// Get/Set Y accelerometer in units of 1 g (9.81 m/s^2).
/// </summary>
public double Y { get; set;}
/// <summary>
/// Get/Set Z accelerometer in units of 1 g (9.81 m/s^2).
/// </summary>
public double Z { get; set;}
public AccelerometerMeasurement()
{
}
}
public class AccelerometerMeasurementEventArgs : EventArgs
{
public AccelerometerMeasurementEventArgs(AccelerometerMeasurement measurement, DateTimeOffset timestamp)
{
Measurement = measurement;
Timestamp = timestamp;
}
public AccelerometerMeasurement Measurement
{
get;
private set;
}
public DateTimeOffset Timestamp
{
get;
private set;
}
}
}
One requirement would be to convert the acceleration values into quaternion:
private void ReadAcceleration(AccelerometerMeasurement measure){
Vector3 accel = new Vector3((float)measure.X,
(float)measure.Y, (float)measure.Z);
Quaternion rotation = Quaternion.LookRotation(accel,Vector3.forward);
}
The rotation quaternion can be used to set an object to the device rotation.
Related
I am connecting a RFIDReader using USB and trying to communicate with it. It works well on windows. But this program should run on my raspberry pi whose operating system is raspbian and it tells me that a dll called hid.dll cannot be found when the program tried using the API of the RFIDReader. I guess its because the hid.dll is specific to windows. Since Linux doesn't have hid.dll, is there anything I can do to solve the problem to still get my program run on Linux? Is there anything similar to Hid.dll in linux?
My code dealing with the reader is as follows. When it executes the method InitReader() the problem will appear. Hope there's anyone can help me, thanks a lot!
using SkyeTek.Devices;
using SkyeTek.Tags;
using SkyeTek.STPv3;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Threading;
namespace RFIDReader
{
/// <summary>
/// this class represents the RFID reader, used to set the reader, send commond to the reader and ask response from the reader
/// </summary>
public class RFIDReader : EventArgs
{
public Device rfidReader { get; set; }
public String tagID;
public String buffer;
public static int count;
Thread timer; //the thread responsible for counting the timeout
public static bool tagRead = false; // determine if a person can pay for a product when a tag is detected
public event EventHandler<RFIDReader> TagDetected; //Tag detected event, raised once a tag is detected by the reader
/// <summary>
/// initiate the reader, set it as auto scanning tags
/// </summary>
public void InitReader()
{
if (FindLinkedDevices())
{
Console.WriteLine("RFIDReader: Opening device: " + rfidReader.ToString());
rfidReader.Open();
AutoScanTags();
rfidReader.Close();
}
}
/// <summary>
/// scanning tags automatically
/// </summary>
public void AutoScanTags()
{
STPv3Request request;
STPv3Response response;
SkyeTek.Tags.Tag tag = new SkyeTek.Tags.Tag();
tag.Type = TagType.AUTO_DETECT;
// Build the SelectTag request
request = new STPv3Request();
request.Command = STPv3Commands.SELECT_TAG;
request.Tag = tag;
//set the reader's flag as loop mode and inventory mode. So The reader reads all the tags in the field and then continuously
//scans for any tags that enter the field and reports back their tag IDs.
request.Loop = true;
request.Inventory = true;
// Issue the request to the output device
while (true)
{
request.Issue(rfidReader);
response = request.GetResponse();
if (response.ResponseCode == STPv3ResponseCode.SELECT_TAG_PASS)
{
//paymentValid = false;
buffer = BitConverter.ToString(response.TID);
if (buffer.Length > 0 && !buffer.Equals(tagID)) //if the tagID is a new ID, rasie tagdetected event, send data to RFIDData instance and start the timer
{
if (timer != null && timer.IsAlive)
{
timer.Abort();
}
tagID = buffer;
OnTagDetected(tagID);
timer = new Thread(Timing);
timer.Start();
}
else
{
if (buffer.Length > 0 && count >= 5) // if the tagID is not a new ID, check the timeout. If time is over, rasie tagdetected event, send data to RFIDData instance and start the timer
{
tagID = buffer;
OnTagDetected(tagID);
timer = new Thread(Timing);
timer.Start();
}
/*else
{
if(buffer.Length >0 && count >= 3 && request.GetResponse() != null)
{
paymentValid = true;
}
}*/
}
}
}
}
/// <summary>
/// Find the readers connected to the computer
/// </summary>
/// <returns></returns>
public bool FindLinkedDevices()
{
Console.WriteLine("RFIDReader: Enumerating linked devices");
Device[] LinkedDevices = USBDeviceFactory.Enumerate();
//Identify the RFID reader
if (LinkedDevices.Length == 0)
{
Console.WriteLine("RFIDReader: No USB devices found");
return false;
}
else
{
foreach (Device i in LinkedDevices)
{
Console.WriteLine("RFIDReader: USB devices found ---> " + i.ToString());
if (i.ToString().Equals("SkyeTek.Devices.USBDevice"))
{
rfidReader = i;
}
}
return true;
}
}
/// <summary>
/// Rasie an tagDetected event once the reader detects a tag
/// </summary>
/// <param name="TID"></param>
protected virtual void OnTagDetected(String TID)
{
if (TagDetected != null)
{
TagDetected(this, new RFIDReader() { tagID = TID });
}
}
/// <summary>
/// timing method running on the timer thread
/// </summary>
public void Timing()
{
for (count = 0; count < 5; count++)
{
System.Threading.Thread.Sleep(1000);
}
}
}
}
To debug a firewall delay issue I need an application that will produce a beep on server side when it detects an HTTP GET request.
This code (test.ashx):
<%# WebHandler Language="C#" Class="TestHandler" %>
using System;
using System.Web;
public class TestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
HttpResponse Response = context.Response;
try
{
Response.Write("Before beep");
Console.Beep();
Response.Write("After beep");
}
catch (Exception ex)
{
Response.Write(ex.Message + "<br />\n" + ex.InnerException.Message);
}
}
public bool IsReusable { get { return false; } }
}
produces sound only when debugging in IIS Express. After moving the web app to IIS, the sound disappears.
The three easy ways of producing a sound are System.Console.Beep(), System.Media.SoundPlayer, and System.Media.SystemSounds.Beep().
Unfortunately, these methods only work in desktop applications, and won't work in service applications. When ASP.Net apps are run under IIS Express (a desktop app), these sound methods work. However, when ASP.Net apps are run under the IIS service, the sound methods don't work.
System.Console.Beep() ultimately calls the kernel32.dll Beep() function. It's restricted to desktop apps only (scroll down to the Requirements section).
Same for System.Media.SoundPlayer and System.Media.SystemSounds.Beep(). They call the kernel32.dll MessageBeep() and the winmm.dll PlaySound() functions, respectively. They, too, are restricted to desktop apps.
One way to get sounds to play in a service is use NAudio. It's easy to install via NuGet.
This chunk of code is the only way I could get the sound to play. It has to be played on a separate worker thread, and the execution of the worker thread needs to be paused to let the .wav file finish playing.
using System;
using System.Diagnostics;
using System.Threading;
using NAudio.Dsp;
using NAudio.Wave;
...
protected void Button1_Click(object sender, EventArgs e)
{
var waveFilename = #"c:\Windows\Media\tada.wav";
/* Trying to play the .wav file on the main thread
doesn't seem to work. */
ThreadPool.QueueUserWorkItem(
(state) =>
{
using (var audioPlayback = new AudioPlayback())
{
audioPlayback.Load(waveFilename);
audioPlayback.Play(); // Asynchronous.
/* Need to sleep for the approximate length of .wav file,
otherwise no sound is produced because of the
asynchronous Play() call. */
Thread.Sleep(2000);
}
});
}
Here's the supporting code taken from code in NAudio's NAudioWPFDemo project:
public class MaxSampleEventArgs : EventArgs
{
[DebuggerStepThrough]
public MaxSampleEventArgs(float minValue, float maxValue)
{
this.MaxSample = maxValue;
this.MinSample = minValue;
}
public float MaxSample { get; private set; }
public float MinSample { get; private set; }
}
public class FftEventArgs : EventArgs
{
[DebuggerStepThrough]
public FftEventArgs(Complex[] result)
{
this.Result = result;
}
public Complex[] Result { get; private set; }
}
public class SampleAggregator : ISampleProvider
{
// volume
public event EventHandler<MaxSampleEventArgs> MaximumCalculated;
private float maxValue;
private float minValue;
public int NotificationCount { get; set; }
int count;
// FFT
public event EventHandler<FftEventArgs> FftCalculated;
public bool PerformFFT { get; set; }
private readonly Complex[] fftBuffer;
private readonly FftEventArgs fftArgs;
private int fftPos;
private readonly int fftLength;
private int m;
private readonly ISampleProvider source;
private readonly int channels;
public SampleAggregator(ISampleProvider source, int fftLength = 1024)
{
channels = source.WaveFormat.Channels;
if (!IsPowerOfTwo(fftLength))
throw new ArgumentException("FFT Length must be a power of two");
this.m = (int) Math.Log(fftLength, 2.0);
this.fftLength = fftLength;
this.fftBuffer = new Complex[fftLength];
this.fftArgs = new FftEventArgs(fftBuffer);
this.source = source;
}
private bool IsPowerOfTwo(int x)
{
return (x & (x - 1)) == 0;
}
public void Reset()
{
count = 0;
maxValue = minValue = 0;
}
private void Add(float value)
{
if (PerformFFT && FftCalculated != null)
{
fftBuffer[fftPos].X = (float) (value * FastFourierTransform.HammingWindow(fftPos, fftLength));
fftBuffer[fftPos].Y = 0;
fftPos++;
if (fftPos >= fftBuffer.Length)
{
fftPos = 0;
// 1024 = 2^10
FastFourierTransform.FFT(true, m, fftBuffer);
FftCalculated(this, fftArgs);
}
}
maxValue = Math.Max(maxValue, value);
minValue = Math.Min(minValue, value);
count++;
if (count >= NotificationCount && NotificationCount > 0)
{
if (MaximumCalculated != null)
MaximumCalculated(this, new MaxSampleEventArgs(minValue, maxValue));
Reset();
}
}
public WaveFormat WaveFormat { get { return source.WaveFormat; } }
public int Read(float[] buffer, int offset, int count)
{
var samplesRead = source.Read(buffer, offset, count);
for (int n = 0; n < samplesRead; n += channels)
Add(buffer[n + offset]);
return samplesRead;
}
}
public class AudioPlayback : IDisposable
{
private IWavePlayer _playbackDevice;
private WaveStream _fileStream;
public void Load(string fileName)
{
Stop();
CloseFile();
EnsureDeviceCreated();
OpenFile(fileName);
}
private void CloseFile()
{
if (_fileStream != null)
{
_fileStream.Dispose();
_fileStream = null;
}
}
private void OpenFile(string fileName)
{
try
{
var inputStream = new AudioFileReader(fileName);
_fileStream = inputStream;
var aggregator = new SampleAggregator(inputStream);
aggregator.NotificationCount = inputStream.WaveFormat.SampleRate / 100;
aggregator.PerformFFT = true;
_playbackDevice.Init(aggregator);
}
catch
{
CloseFile();
throw;
}
}
private void EnsureDeviceCreated()
{
if (_playbackDevice == null)
CreateDevice();
}
private void CreateDevice()
{
_playbackDevice = new WaveOut { DesiredLatency = 200 };
}
public void Play()
{
if (_playbackDevice != null && _fileStream != null && _playbackDevice.PlaybackState != PlaybackState.Playing)
_playbackDevice.Play();
}
public void Pause()
{
if (_playbackDevice != null)
_playbackDevice.Pause();
}
public void Stop()
{
if (_playbackDevice != null)
_playbackDevice.Stop();
if (_fileStream != null)
_fileStream.Position = 0;
}
public void Dispose()
{
Stop();
CloseFile();
if (_playbackDevice != null)
_playbackDevice.Dispose();
}
}
Try this System.Media.SystemSounds.Beep.Play();
I'm currently working on SFML.Net to expand with mp3 support. Therefore I wrote a Stream class which uses NLayer MpegFile to decode the mp3.
public class Mp3StreamSFML : SoundStream
{
private MpegFile mp3file;
private int currentBufferSize;
private short[] currentBuffer;
public Mp3StreamSFML(String _filename)
{
mp3file = new MpegFile(_filename);
Initialize((uint)mp3file.Channels, (uint)mp3file.SampleRate);
currentBufferSize = 0;
currentBuffer = new short[currentBufferSize];
}
#region implemented abstract members of SoundStream
protected override bool OnGetData(out short[] samples)
{
if (currentBufferSize <= mp3file.Position)
{
byte[] buffer = new byte[512];
if (mp3file.ReadSamples(buffer, 0, buffer.Length) > 0)
{
Array.Resize(ref currentBuffer, currentBuffer.Length + (buffer.Length / 2));
Buffer.BlockCopy(buffer, 0, currentBuffer, currentBufferSize, buffer.Length);
currentBufferSize = currentBuffer.Length;
}
samples = currentBuffer;
return true;
}
else
{
samples = currentBuffer;
return false;
}
}
protected override void OnSeek(TimeSpan timeOffset)
{
mp3file.Position = (long)timeOffset.TotalSeconds;
}
#endregion
}
I use it this way:
try
{
stream = new Mp3StreamSFML(this.objProgram.getObjCuesheet().getAudiofilePath(true));
stream.Play();
log.debug("samplerate = " + stream.SampleRate);
}
catch(Exception ex)
{
log.fatal(ex.ToString());
}
Unfortunately, there is not the correct sound played, its just "juttering" and sound really weird. What I'm doing wrong? Seems like a problem between the 2 Frameworks.
Thanks for your help.
Sven
Solved the problem this way:
using System;
using SFML.Audio;
using NLayer;
using System.Threading;
namespace AudioCuesheetEditor.AudioBackend.SFML
{
/// <summary>
/// Class for mp3 decoded audio files to use in SFML as Soundstream, since SFML doesn't support mp3 decoding (for legal reasons).
/// </summary>
public class Mp3StreamSFML : SoundStream
{
private static readonly Logfile log = Logfile.getLogfile(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private MpegFile mp3file;
private Mutex mutex;
/// <summary>
/// Initializes a new instance of the <see cref="AudioCuesheetEditor.AudioBackend.SFML.Mp3StreamSFML"/> class.
/// </summary>
/// <param name="_filename">Full path to the file</param>
public Mp3StreamSFML(String _filename)
{
log.debug("Constructor called with " + _filename);
this.mp3file = new MpegFile(_filename);
this.Initialize((uint)this.mp3file.Channels, (uint)this.mp3file.SampleRate);
this.mutex = new Mutex();
}
public TimeSpan Duration
{
get
{
log.debug("Duration = " + this.mp3file.Duration);
return this.mp3file.Duration;
}
}
#region implemented abstract members of SoundStream
protected override bool OnGetData(out short[] samples)
{
log.debug("OnGetData called");
this.mutex.WaitOne();
//Buffer data for about 1 second
float[] normalizedaudiodata = new float[48000];
int readSamples = this.mp3file.ReadSamples(normalizedaudiodata, 0, normalizedaudiodata.Length);
short[] pcmaudiodata;
if (readSamples > 0)
{
pcmaudiodata = new short[readSamples]; // converted data
for (int i = 0; i < readSamples; i++)
{
// clip the data
if (normalizedaudiodata[i] > 1.0f)
{
normalizedaudiodata[i] = 1.0f;
}
else
{
if (normalizedaudiodata[i] < -1.0f)
{
normalizedaudiodata[i] = -1.0f;
}
}
// convert to pcm data
pcmaudiodata[i] = (short)(normalizedaudiodata[i] * short.MaxValue);
}
samples = pcmaudiodata;
this.mutex.ReleaseMutex();
return true;
}
else
{
samples = null;
this.mutex.ReleaseMutex();
return false;
}
}
protected override void OnSeek(TimeSpan timeOffset)
{
log.debug("OnSeek called with " + timeOffset);
this.mutex.WaitOne();
if ((timeOffset <= this.mp3file.Duration) && (timeOffset >= TimeSpan.Zero))
{
this.mp3file.Time = timeOffset;
}
this.mutex.ReleaseMutex();
}
#endregion
}
}
I have a really weird problem. I tried everything to fix it and so far nothing works.
As you can see in the first image image, when I try to reference the static class "SharedConstants" from a namespace outside "UnnamedGameServer" the compiler returns the following error:
The type or namespace name 'ServerInfo' does not exist in the
namespace 'SharedConstants'
I found a turnaround to this problem referencing this class using UnnamedGameServer.SharedConstants instead of SharedConstants and using UnnamedGameServer; on the top of the .cs file. But I prefer avoiding referencing it on every line I use that class.
Here are some screenshots of my code:
First screenshot:
Second screenshot:
Edit: Screenshot of using statement:
Edit 2: Code without screenshots:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections;
using ProtoBuf;
using UnnamedGameServer;
using System.Threading;
namespace Unnamed_game
{
class Connection
{
public struct UserInformation
{
public string Username;
public int UserID;
}
static private Connection instance;
private Connection()
{
client = new TcpClient();
header = new PacketHeader();
_isLoggedCharacter = false;
_isLoggedUser = false;
magicByte = UnnamedGameServer.SharedConstants.ServerInfo.MagicByte;
}
// properties
TcpClient client;
PacketHeader header;
Thread serverCommThread;
byte[] magicByte;
bool _isLoggedCharacter;
CharacterInformation chInformation
{
get
{
return Player.Instance().Information;
}
}
bool _isLoggedUser;
public UserInformation Information;
// methods
static public Connection Instance()
{
if(instance == null)
{
instance = new Connection();
return instance;
}
else
{
return instance;
}
}
/// <summary>
/// Should never be used. Use the parameterless one to get the server address and information. It doesn't use try-catch, the exception handling should be done on Game1.Initialize()
/// </summary>
public void ConnectServer(IPEndPoint endPoint)
{
if (client.Connected)
{
return;
}
else
{
client.Connect(endPoint);
serverCommThread = new Thread(HandleServerCommunication);
serverCommThread.Start();
}
}
public void ConnectServer()
{
this.ConnectServer(new IPEndPoint(UnnamedGameServer.SharedConstants.ServerInfo.ServerAddress, UnnamedGameServer.SharedConstants.ServerInfo.Port));
}
private void HandleServerCommunication()
{
if (client == null)
{
throw new Exception("The TcpClient is null");
}
else
{
// this doesn't work
byte[] magicByte = SharedConstants.ServerInfo.MagicByte;
// this works
magicByte = UnnamedGameServer.SharedConstants.ServerInfo.MagicByte;
}
}
private void SendPacket(ActionType actionType, object packetStruct)
{
try
{
header.ActionTypeNumber = (short)actionType;
using (NetworkStream stream = client.GetStream())
{
stream.Write(magicByte, 0, magicByte.Length);
Serializer.SerializeWithLengthPrefix<PacketHeader>(stream, header, PrefixStyle.Base128);
switch (actionType)
{
case ActionType.Connect:
Serializer.SerializeWithLengthPrefix<PacketConnect>(stream, (PacketConnect)packetStruct, PrefixStyle.Base128);
break;
}
stream.Write(magicByte, 0, magicByte.Length);
}
}
catch (Exception)
{
// error
return;
}
}
public void CreateNewCharacter(string characterName, CharacterClass chClass, CharacterRace chRace)
{
var info = new NewCharacterInfo();
info.Name = characterName;
info.OwnerUsername = Information.Username;
info.Class = chClass;
info.Race = chRace;
CreateNewCharacter(info);
}
public void CreateNewCharacter(NewCharacterInfo info)
{
var packet = new PacketCreateNewCharacter();
packet.chInfo = info;
SendPacket(ActionType.CreateNewCharacter, packet);
}
public void ConnectUser(string username, string unhashedPassword)
{
var packet = new PacketConnect();
packet.Username = username;
packet.UnhashedPassword = unhashedPassword;
ConnectUser(packet);
}
public void ConnectUser(PacketConnect packet)
{
if (_isLoggedCharacter || _isLoggedUser)
{
return;
}
else
{
SendPacket(ActionType.Connect, packet);
}
}
public void ConnectCharacter(string characterName, short serverID)
{
var packet = new PacketLoginCharacter();
packet.CharacterName = characterName;
packet.ServerID = serverID;
ConnectCharacter(packet);
}
public void ConnectCharacter(PacketLoginCharacter packet)
{
if (_isLoggedCharacter || !_isLoggedUser)
{
return;
}
else
{
SendPacket(ActionType.LoginCharacter, packet);
}
}
public void Disconnect(PacketDisconnect packet)
{
if (!_isLoggedUser)
{
return;
}
else
{
SendPacket(ActionType.Disconnect, packet);
}
}
}
}
edit 3:: Code where SharedConstants is stored.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace UnnamedGameServer
{
/// <summary>
/// ALL CONSTANTS THAT ARE SHARED BY THE CLIENT AND THE SERVER ARE STORED HERE. DONT ADD MORE CLASSES THAT STORE CONSTANTS. Use GameConstants for game-only constants.
/// </summary>
public static class SharedConstants
{
/// <summary>
/// Server information with port, IP and MagicByte
/// </summary>
public static class ServerInfo
{
public const short Port = 6483;
public static readonly IPAddress ServerAddress = IPAddress.Loopback;
public static byte[] MagicByte
{
get
{
return new byte[4] { 0xF1, 0x64, 0x83, 0xC4 };
}
}
}
/// <summary>
/// Character constants shared by client/server
/// </summary>
public static class CharacterConstants
{
public static class Movement
{
/// <summary>
/// Coordinates per update
/// </summary>
public const float DefaultCoordinatePerUpdate = 8;
public const float ModifierNormal = 1f;
public const float ModifierFrozen = 0.8f;
public const float ModifierSpeedBuff = 1.2f;
public const float ModifierStop = 0f;
}
}
/// <summary>
/// Networking constants
/// </summary>
public static class Networking
{
public const int MilisecondsPerMovementUpdate = 100;
public const ushort ActionTypeNonMetaActionStart = 0x0FFF + 1;
/// <summary>
/// What is the number of actions a non-logged user can perform
/// </summary>
public const ushort ActionTypeNonLoggedUser = 0x000F;
}
}
enum CharacterDirection
{
NoMovement = 0x00,
Top = 0x01,
TopRight = 0x02,
TopLeft = 0x03,
Right = 0x04,
BottomRight = 0x05,
Bottom = 0x06,
BottomLeft = 0x07,
Left = 0x08
}
enum CharacterStatus
{
Alive = 0x01,
Dead = 0x02
}
enum CharacterClass
{
Mage = 0x01,
Knight = 0x02
}
enum CharacterRace
{
Human = 0x01
}
enum CharacterType
{
Player = 0x01,
GameMaster = 0x02
}
enum CharacterFaction
{
Newbie = 0x01,
Army = 0x02,
Chaos = 0x03
}
enum CharacterMovementStatus
{
Normal = 0x01,
Frozen = 0x02,
SpeedBuff = 0x03,
Stop = 0x04
}
struct CharacterExperience
{
public CharacterExperience(short level, int experience)
{
Level = level;
Experience = experience;
}
public short Level;
public int Experience;
}
}
Having a using statement should work. If there is any code before the using statements then this may cause a problem.
you could try putting the using statement after the namespace e.g:
namespace Unnamed_game
{
using UnamedGameServer;
class Connection
This should work:
using UnnamedGameServer;
class MyClass {
byte[] b = SharedConstants.ServerInfo.MagicByte;
}
If this doesn't solve your problem, please post the code (in text, not a screenshot) of a short but complete example showing the problem.
I have an IP Camera that receives a char buffer containing an image over the network. I cant access it until i setup the connection to it in a program. I am trying to dissect windows source filter code and im not going very fast so i thought i'd ask if it was possible to just take a buffer like that and cast it to something that could then connect a pin to AVISplitter or such in Directshow/.net
(video buffer from IP Cam) -> (???) -> (AVI Splitter) -> (Profit)
Update
I have my program capturing video in a namespace, and i have this code from the GSSF in its own namespace. I pass a ptr with an image from the cam namespace to the GSSF namespace. This only occurs once, but the graph streams from this one image, and the camera streams from the network. is there a way to continually pass the buffer from cam to GSSF or should i combine the namespaces somehow? I tried sending the main camera pointer to the GSSF but it crashed because its accessing the pointer and its being written. maybe if i grabbed an image, passed the pointer, waited to grab a new one?
*Update*
I shrunk my code and I don't believe im doing the namespace correctly either now that i look at it.
namespace Cam_Controller
{
static byte[] mainbyte = new byte[1280*720*2];
static IntPtr main_ptr = new IntPtr();
//(this function is threaded)
static void Trial(NPvBuffer mBuffer, NPvDisplayWnd mDisplayWnd, VideoCompression compressor)
{
Functions function = new Functions();
Defines define = new Defines();
NPvResult operationalResult = new NPvResult();
VideoCompression mcompressor = new VideoCompression();
int framecount = 0;
while (!Stopping && AcquiringImages)
{
Mutex lock_video = new Mutex();
NPvResult result = mDevice.RetrieveNextBuffer(mBuffer, operationalResult);
if(result.isOK())
{
framecount++;
wer = (int)mDisplayWnd.Display(mBuffer, wer);
main_ptr = (IntPtr)mBuffer.GetMarshalledBuffer();
Marshal.Copy(main_ptr, mainbyte, 0, 720 * 2560);
}
}
}
private void button7_Click(object sender, EventArgs e)
{
IntPtr dd = (IntPtr)mBuffer.GetMarshalledBuffer();
Marshal.Copy(dd, main_byte1, 0, 720 * 2560);
play = new VisiCam_Controller.DxPlay.DxPlay("", panel9, main_byte1);
play.Start();
}
namespace DxPlay
{
public class DxPlay
{
public DxPlay(string sPath, Control hWin, byte[] color)
{
try
{
// pick one of our image providers
//m_ImageHandler = new ImageFromFiles(sPath, 24);
m_ImageHandler = new ImageFromPixels(20, color);
//m_ImageHandler = new ImageFromMpg(#"c:\c1.mpg");
//m_ImageHandler = new ImageFromMpg(sPath);
//m_ImageHandler = new ImageFromMP3(#"c:\vss\media\track3.mp3");
// Set up the graph
SetupGraph(hWin);
}
catch
{
Dispose();
throw;
}
}
}
abstract internal class imagehandler
internal class imagefrompixels
{
private int[] mainint = new int[720 * 1280];
unsafe public ImageFromPixels(long FPS, byte[] x)
{
long fff = 720 * 1280 * 3;
mainptr = new IntPtr(fff);
for (int p = 0; p < 720 * 640; p++)
{
U = (x[ p * 4 + 0]);
Y = (x[p * 4 + 1]);
V = (x[p * 4 + 2]);
Y2 = (x[p * 4 + 3]);
int one = V << 16 | Y << 8 | U;
int two = V << 16 | Y2 << 8 | U;
mainint[p * 2 + 0] = one;
mainint[p * 2 + 1] = two;
}
m_FPS = UNIT / FPS;
m_b = 211;
m_g = 197;
}
}
}
}
Theres also GetImage but thats relatively the same, copy the buffer into the pointer. What happens is i grab a buffer of the image and send it to the DxPlay class. it is able to process it and put it on the directshow line no problems; but it never updates nor gets updated because its just a single buffer. If i instead send DxPlay a IntPtr holding the address of the image buffer, the code crashes for accessing memory because i assume ImageFromPixels code ( which isn't there now ( change
(x[p * 4 + #])
to
(IntPtr)((x-passed as an IntPtr).toInt64()+p*4 + #)
))
is accessing the memory of the pointer as the Cam_Controller class is editing it. I make and pass copies of the IntPtrs, and new IntPtrs but they fail halfway through the conversion.
If you want to do this in .NET, the following steps are needed:
Use the DirectShow.NET Generic Sample Source Filter (GSSF.AX) from the Misc/GSSF directory within the sample package. A source filter is always a COM module, so you need to register it too using "RegSvr32 GSSF.ax".
Implement a bitmap provider in .NET
Setup a graph, and connect the pin from the GSSF to the implementation of the bitmap provider.
Pray.
I am using the following within a project, and made it reusable for future usage.
The code (not the best, and not finished, but a working start) (this takes a IVideoSource, which is bellow):
public class VideoSourceToVideo : IDisposable
{
object locker = new object();
public event EventHandler<EventArgs> Starting;
public event EventHandler<EventArgs> Stopping;
public event EventHandler<EventArgs> Completed;
/// <summary> graph builder interface. </summary>
private DirectShowLib.ICaptureGraphBuilder2 captureGraphBuilder = null;
DirectShowLib.IMediaControl mediaCtrl = null;
IMediaEvent mediaEvent = null;
bool stopMediaEventLoop = false;
Thread mediaEventThread;
/// <summary> Dimensions of the image, calculated once in constructor. </summary>
private readonly VideoInfoHeader videoInfoHeader;
IVideoSource source;
public VideoSourceToVideo(IVideoSource source, string destFilename, string encoderName)
{
try
{
this.source = source;
// Set up the capture graph
SetupGraph(destFilename, encoderName);
}
catch
{
Dispose();
throw;
}
}
/// <summary> release everything. </summary>
public void Dispose()
{
StopMediaEventLoop();
CloseInterfaces();
}
/// <summary> build the capture graph for grabber. </summary>
private void SetupGraph(string destFilename, string encoderName)
{
int hr;
// Get the graphbuilder object
captureGraphBuilder = new DirectShowLib.CaptureGraphBuilder2() as DirectShowLib.ICaptureGraphBuilder2;
IFilterGraph2 filterGraph = new DirectShowLib.FilterGraph() as DirectShowLib.IFilterGraph2;
mediaCtrl = filterGraph as DirectShowLib.IMediaControl;
IMediaFilter mediaFilt = filterGraph as IMediaFilter;
mediaEvent = filterGraph as IMediaEvent;
captureGraphBuilder.SetFiltergraph(filterGraph);
IBaseFilter aviMux;
IFileSinkFilter fileSink = null;
hr = captureGraphBuilder.SetOutputFileName(MediaSubType.Avi, destFilename, out aviMux, out fileSink);
DsError.ThrowExceptionForHR(hr);
DirectShowLib.IBaseFilter compressor = DirectShowUtils.GetVideoCompressor(encoderName);
if (compressor == null)
{
throw new InvalidCodecException(encoderName);
}
hr = filterGraph.AddFilter(compressor, "compressor");
DsError.ThrowExceptionForHR(hr);
// Our data source
IBaseFilter source = (IBaseFilter)new GenericSampleSourceFilter();
// Get the pin from the filter so we can configure it
IPin ipin = DsFindPin.ByDirection(source, PinDirection.Output, 0);
try
{
// Configure the pin using the provided BitmapInfo
ConfigurePusher((IGenericSampleConfig)ipin);
}
finally
{
Marshal.ReleaseComObject(ipin);
}
// Add the filter to the graph
hr = filterGraph.AddFilter(source, "GenericSampleSourceFilter");
Marshal.ThrowExceptionForHR(hr);
hr = filterGraph.AddFilter(source, "source");
DsError.ThrowExceptionForHR(hr);
hr = captureGraphBuilder.RenderStream(null, null, source, compressor, aviMux);
DsError.ThrowExceptionForHR(hr);
IMediaPosition mediaPos = filterGraph as IMediaPosition;
hr = mediaCtrl.Run();
DsError.ThrowExceptionForHR(hr);
}
private void ConfigurePusher(IGenericSampleConfig ips)
{
int hr;
source.SetMediaType(ips);
// Specify the callback routine to call with each sample
hr = ips.SetBitmapCB(source);
DsError.ThrowExceptionForHR(hr);
}
private void StartMediaEventLoop()
{
mediaEventThread = new Thread(MediaEventLoop)
{
Name = "Offscreen Vid Player Medialoop",
IsBackground = false
};
mediaEventThread.Start();
}
private void StopMediaEventLoop()
{
stopMediaEventLoop = true;
if (mediaEventThread != null)
{
mediaEventThread.Join();
}
}
public void MediaEventLoop()
{
MediaEventLoop(x => PercentageCompleted = x);
}
public double PercentageCompleted
{
get;
private set;
}
// FIXME this needs some work, to be completely in-tune with needs.
public void MediaEventLoop(Action<double> UpdateProgress)
{
mediaEvent.CancelDefaultHandling(EventCode.StateChange);
//mediaEvent.CancelDefaultHandling(EventCode.Starvation);
while (stopMediaEventLoop == false)
{
try
{
EventCode ev;
IntPtr p1, p2;
if (mediaEvent.GetEvent(out ev, out p1, out p2, 0) == 0)
{
switch (ev)
{
case EventCode.Complete:
Stopping.Fire(this, null);
if (UpdateProgress != null)
{
UpdateProgress(source.PercentageCompleted);
}
return;
case EventCode.StateChange:
FilterState state = (FilterState)p1.ToInt32();
if (state == FilterState.Stopped || state == FilterState.Paused)
{
Stopping.Fire(this, null);
}
else if (state == FilterState.Running)
{
Starting.Fire(this, null);
}
break;
// FIXME add abort and stuff, and propagate this.
}
// Trace.WriteLine(ev.ToString() + " " + p1.ToInt32());
mediaEvent.FreeEventParams(ev, p1, p2);
}
else
{
if (UpdateProgress != null)
{
UpdateProgress(source.PercentageCompleted);
}
// FiXME use AutoResetEvent
Thread.Sleep(100);
}
}
catch (Exception e)
{
Trace.WriteLine("MediaEventLoop: " + e);
}
}
}
/// <summary> Shut down capture </summary>
private void CloseInterfaces()
{
int hr;
try
{
if (mediaCtrl != null)
{
// Stop the graph
hr = mediaCtrl.Stop();
mediaCtrl = null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
if (captureGraphBuilder != null)
{
Marshal.ReleaseComObject(captureGraphBuilder);
captureGraphBuilder = null;
}
GC.Collect();
}
public void Start()
{
StartMediaEventLoop();
}
}
IVideoSource:
public interface IVideoSource : IGenericSampleCB
{
double PercentageCompleted { get; }
int GetImage(int iFrameNumber, IntPtr ip, int iSize, out int iRead);
void SetMediaType(global::IPerform.Video.Conversion.Interops.IGenericSampleConfig psc);
int SetTimeStamps(global::DirectShowLib.IMediaSample pSample, int iFrameNumber);
}
ImageVideoSource (mostly taken from DirectShow.NET examples):
// A generic class to support easily changing between my different sources of data.
// Note: You DON'T have to use this class, or anything like it. The key is the SampleCallback
// routine. How/where you get your bitmaps is ENTIRELY up to you. Having SampleCallback call
// members of this class was just the approach I used to isolate the data handling.
public abstract class ImageVideoSource : IDisposable, IVideoSource
{
#region Definitions
/// <summary>
/// 100 ns - used by a number of DS methods
/// </summary>
private const long UNIT = 10000000;
#endregion
/// <summary>
/// Number of callbacks that returned a positive result
/// </summary>
private int m_iFrameNumber = 0;
virtual public void Dispose()
{
}
public abstract double PercentageCompleted { get; protected set; }
abstract public void SetMediaType(IGenericSampleConfig psc);
abstract public int GetImage(int iFrameNumber, IntPtr ip, int iSize, out int iRead);
virtual public int SetTimeStamps(IMediaSample pSample, int iFrameNumber)
{
return 0;
}
/// <summary>
/// Called by the GenericSampleSourceFilter. This routine populates the MediaSample.
/// </summary>
/// <param name="pSample">Pointer to a sample</param>
/// <returns>0 = success, 1 = end of stream, negative values for errors</returns>
virtual public int SampleCallback(IMediaSample pSample)
{
int hr;
IntPtr pData;
try
{
// Get the buffer into which we will copy the data
hr = pSample.GetPointer(out pData);
if (hr >= 0)
{
// Set TRUE on every sample for uncompressed frames
hr = pSample.SetSyncPoint(true);
if (hr >= 0)
{
// Find out the amount of space in the buffer
int cbData = pSample.GetSize();
hr = SetTimeStamps(pSample, m_iFrameNumber);
if (hr >= 0)
{
int iRead;
// Get copy the data into the sample
hr = GetImage(m_iFrameNumber, pData, cbData, out iRead);
if (hr == 0) // 1 == End of stream
{
pSample.SetActualDataLength(iRead);
// increment the frame number for next time
m_iFrameNumber++;
}
}
}
}
}
finally
{
// Release our pointer the the media sample. THIS IS ESSENTIAL! If
// you don't do this, the graph will stop after about 2 samples.
Marshal.ReleaseComObject(pSample);
}
return hr;
}
}
RawVideoSource (an example of a concrete managed source generator for a DirectShow pipeline):
internal class RawVideoSource : ImageVideoSource
{
private byte[] buffer;
private byte[] demosaicBuffer;
private RawVideoReader reader;
public override double PercentageCompleted
{
get;
protected set;
}
public RawVideoSource(string sourceFile)
{
reader = new RawVideoReader(sourceFile);
}
override public void SetMediaType(IGenericSampleConfig psc)
{
BitmapInfoHeader bmi = new BitmapInfoHeader();
bmi.Size = Marshal.SizeOf(typeof(BitmapInfoHeader));
bmi.Width = reader.Header.VideoSize.Width;
bmi.Height = reader.Header.VideoSize.Height;
bmi.Planes = 1;
bmi.BitCount = 24;
bmi.Compression = 0;
bmi.ImageSize = (bmi.BitCount / 8) * bmi.Width * bmi.Height;
bmi.XPelsPerMeter = 0;
bmi.YPelsPerMeter = 0;
bmi.ClrUsed = 0;
bmi.ClrImportant = 0;
int hr = psc.SetMediaTypeFromBitmap(bmi, 0);
buffer = new byte[reader.Header.FrameSize];
demosaicBuffer = new byte[reader.Header.FrameSize * 3];
DsError.ThrowExceptionForHR(hr);
}
long startFrameTime;
long endFrameTime;
unsafe override public int GetImage(int iFrameNumber, IntPtr ip, int iSize, out int iRead)
{
int hr = 0;
if (iFrameNumber < reader.Header.NumberOfFrames)
{
reader.ReadFrame(buffer, iFrameNumber, out startFrameTime, out endFrameTime);
Demosaic.DemosaicGBGR24Bilinear(buffer, demosaicBuffer, reader.Header.VideoSize);
Marshal.Copy(demosaicBuffer, 0, ip, reader.Header.FrameSize * 3);
PercentageCompleted = ((double)iFrameNumber / reader.Header.NumberOfFrames) * 100.0;
}
else
{
PercentageCompleted = 100;
hr = 1; // End of stream
}
iRead = iSize;
return hr;
}
override public int SetTimeStamps(IMediaSample pSample, int iFrameNumber)
{
reader.ReadTimeStamps(iFrameNumber, out startFrameTime, out endFrameTime);
DsLong rtStart = new DsLong(startFrameTime);
DsLong rtStop = new DsLong(endFrameTime);
int hr = pSample.SetTime(rtStart, rtStop);
return hr;
}
}
And the interops to the GSSF.AX COM:
namespace IPerform.Video.Conversion.Interops
{
[ComImport, Guid("6F7BCF72-D0C2-4449-BE0E-B12F580D056D")]
public class GenericSampleSourceFilter
{
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("33B9EE57-1067-45fa-B12D-C37517F09FC0")]
public interface IGenericSampleCB
{
[PreserveSig]
int SampleCallback(IMediaSample pSample);
}
[Guid("CE50FFF9-1BA8-4788-8131-BDE7D4FFC27F"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IGenericSampleConfig
{
[PreserveSig]
int SetMediaTypeFromBitmap(BitmapInfoHeader bmi, long lFPS);
[PreserveSig]
int SetMediaType([MarshalAs(UnmanagedType.LPStruct)] AMMediaType amt);
[PreserveSig]
int SetMediaTypeEx([MarshalAs(UnmanagedType.LPStruct)] AMMediaType amt, int lBufferSize);
[PreserveSig]
int SetBitmapCB(IGenericSampleCB pfn);
}
}
Good luck, try to get it working using this. Or comment with further questions so we can iron out other issues.