Add mp3 support to SFML.NET - c#

I'm currently working on an audiobackend for my application, which runs on Mono/.NET. Therefore I found, that SFML is plattformindependent, so it suits. But there is no mp3 support because of licensing.
So I wanted to add up the mp3 capability for myself in C#. I use SFML.Net 2.1 with Mp3Sharp (http://www.robburke.net/mle/mp3sharp/). At first I tried this:
Mp3Stream stream = new Mp3Stream("D:\\tmp\\test.mp3");
Music music = new Music(stream);
music.play();
But that didn't work. Exception:
AudioCuesheetEditor.AudioBackend.AudioManagerSFML: FATAL | SFML.LoadingFailedException: Failed to load music from memory
at SFML.Audio.Music..ctor(Stream stream)
at AudioCuesheetEditor.AudioBackend.AudioManagerSFML.setMusic() in d:\tmp\AudioCuesheetEditor\src\AudioCuesheetEditor\AudioBackend\AudioManagerSFML.cs
So I thought about doing as it is done in the examples (https://github.com/LaurentGomila/SFML/wiki/Source:-MP3-Player):
I made a class SFMLMp3Stream which extends SoundStream:
public class SFMLMp3Stream : SoundStream
{
private Mp3Stream stream;
public SFMLMp3Stream()
{
stream = null;
}
public Boolean openFromFile(String _filename)
{
//TODO:Check file existence
stream = new Mp3Stream(_filename);
Initialize((uint)stream.ChannelCount,(uint) stream.Frequency);
return true;
}
#region implemented abstract members of SoundStream
protected override bool OnGetData(out short[] samples)
{
if (stream != null)
{
byte[] buffer = new byte[512];
short[] retVal = new short[512];
stream.Read(buffer,0,buffer.Length);
Buffer.BlockCopy(buffer,0,retVal,0,buffer.Length);
samples = retVal;
return true;
}
else
{
samples = null;
return false;
}
}
protected override void OnSeek(TimeSpan timeOffset)
{
if (stream != null)
{
stream.Seek((long)timeOffset.TotalMilliseconds, SeekOrigin.Current);
}
}
#endregion
}
When I try now this:
musicStream = new SFMLMp3Stream();
musicStream.openFromFile(this.objProgram.getObjCuesheet().getAudiofilePath(true));
music = new Music(musicStream);
I get the compiler error, that Music can not be initiatet from SFMLMp3Stream (because it is no string and no System.IO.Stream. How can I extend SFML for Mp3 usage? Has anybody done this so far?
Thanks for your help
Sven

Related

c# how to capture audio from nvlc and raise Accord.Audio.NewFrameEventArgs

I'm working on the application in c# that record video stream from IP cameras.
I'm using Accord.Video.FFMPEG.VideoFileWriter and nVLC C# wrapper.
I have a class that captures audio from the stream using nVLC, which should implement the IAudioSource interface, so I've used CustomAudioRendere to capture sound data, then raised the event NewFrame that contains the signal object.
The problem is when saving the signal to video file, the sound is terrifying(discrete) when the record from RTSP stream, but in good quality when the record from the local mic(from the laptop).
Here is the code that raises the event:
public void Start()
{
_mFactory = new MediaPlayerFactory();
_mPlayer = _mFactory.CreatePlayer<IAudioPlayer>();
_mMedia = _mFactory.CreateMedia<IMedia>(Source);
_mPlayer.Open(_mMedia);
var fc = new Func<SoundFormat, SoundFormat>(SoundFormatCallback);
_mPlayer.CustomAudioRenderer.SetFormatCallback(fc);
var ac = new AudioCallbacks { SoundCallback = SoundCallback };
_mPlayer.CustomAudioRenderer.SetCallbacks(ac);
_mPlayer.Play();
}
private void SoundCallback(Sound newSound)
{
var data = new byte[newSound.SamplesSize];
Marshal.Copy(newSound.SamplesData, data, 0, (int)newSound.SamplesSize);
NewFrame(this, new Accord.Audio.NewFrameEventArgs(new Signal(data,Channels, data.Length, SampleRate, Format)));
}
private SoundFormat SoundFormatCallback(SoundFormat arg)
{
Channels = arg.Channels;
SampleRate = arg.Rate;
BitPerSample = arg.BitsPerSample;
return arg;
}
And here is the code that captures the event:
private void source_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
Signal sig = eventArgs.Signal;
duration += eventArgs.Signal.Duration;
if (videoFileWrite == null)
{
videoFileWrite = new VideoFileWriter();
videoFileWrite.AudioBitRate = sig.NumberOfSamples*sig.NumberOfChannels*sig.SampleSize;
videoFileWrite.SampleRate = sig.SampleRate;
videoFileWrite.FrameSize = sig.NumberOfSamples/sig.NumberOfFrames;
videoFileWrite.Open("d:\\output.mp4");
}
if (isStartRecord)
{
DoneWriting = false;
using (MemoryStream ms = new MemoryStream())
{
encoder = new WaveEncoder(ms);
encoder.Encode(eventArgs.Signal);
ms.Seek(0, SeekOrigin.Begin);
decoder = new WaveDecoder(ms);
Signal s = decoder.Decode();
videoFileWrite.WriteAudioFrame(s);
encoder.Close();
decoder.Close();
}
DoneWriting = true;
}
}
I've solved this problem by taking only one channel from Sound object "newSound" in SoundCallback void, then create a signal from that array of bytes and raise the event "NewFrame".
The main idea behind this solution is that when the audio stream contains more than one channel, the SampleData property in Sound object in SoundCallback void contains array of bytes for all channels by the following formation:
let's assume that sound data for one channel for samples of two bytes are A1A2B1B2C1C2...etc, so the SampleData would be for two channels as:
A1A2A1A2B1B2B1B2C1C2C1C2.....etc.
hope that could help you out.

Convert onvif stream to rtsp address in c#

I am developing a project using c#.in my project i should get the camera stream .So i use onvif to get the stream but i faced a problem .some of my camera can't support onvif and they support RTSP,and i have to use rtsp in my project to :
Here is my onvif code to get the camera stream :
async void StartCameraAfterLogin()
{
//Dooooorbin
await ProcessConnect("http://172.16.0.52/onvif/device_service"
, Properties.Settings.Default.CameraUserName, Properties.Settings.Default.CameraPassword);
}
async Task ProcessConnect(string addr, string name, string pass)
{
//Release();
//ctrlInfo.Content = new ProgressControl();
var account = new System.Net.NetworkCredential(name, pass);
NvtSessionFactory factory = new NvtSessionFactory(account);
////This is syncronouse ui blokking call
//var session = factory.CreateSession(new Uri(addr);
try
{
var session = factory.CreateSession(new Uri(addr));
ss = session;
await FillInfoArea(session);
}
catch (Exception err)
{
Algoa.Utilities.Logger.ToDebug(err);
//ctrlInfo.Content = new ErrorControl(err);
}
}
string profie;
private async Task FillInfoArea(INvtSession session)
{
var profInfo = new ProfieInfoControl();
try
{
var streamData = await profInfo.Init(session);
//sp.Children.Add(profInfo);
profie = profInfo.valueToken.Text;
InitVideoControl(streamData.Item1, streamData.Item4, session.credentials);
//InitPtzControl(session, streamData.Item2, streamData.Item3);
}
catch (Exception err)
{
Algoa.Utilities.Logger.ToDebug(err);
}
//ctrlInfo.Content = sp;
}
private void InitVideoControl(string suri, System.Windows.Size size, System.Net.NetworkCredential networkCredential)
{
InitPlayer(suri, networkCredential, size);
}
public void InitPlayer(string videoUri, NetworkCredential account, System.Windows.Size sz = default(System.Windows.Size))
{
player = new HostedPlayer();
playerDisposable.Add(player);
if (sz != default(System.Windows.Size))
videoBuff = new VideoBuffer((int)sz.Width, (int)sz.Height);
else
{
videoBuff = new VideoBuffer(720, 576);
}
player.SetVideoBuffer(videoBuff);
MediaStreamInfo.Transport transp = MediaStreamInfo.Transport.Udp;
MediaStreamInfo mstrInfo = null;
if (account != null)
{
mstrInfo = new MediaStreamInfo(videoUri, transp, new UserNameToken(account.UserName, account.Password));//, transp, null);
}
else
{
mstrInfo = new MediaStreamInfo(videoUri, transp);
}
playerDisposable.Add(
player.Play(mstrInfo, this)
);
InitPlayback(videoBuff, true);
//{ // playing controls
// btnPause.Click += play;
// btnResume.Click += play;
//}
}
How can i convert my onvif url to rtsp url ?Is there any solution or i should change all part of my code?
I am new in camera stream coding .
I downloaded the OZEKI-SDK
private IIPCamera _camera;
private DrawingImageProvider _imageProvider = new DrawingImageProvider();
private MediaConnector _connector = new MediaConnector();
private VideoViewerWF _videoViewerWF1;
public Form1()
{
InitializeComponent();
// Create video viewer UI control
_videoViewerWF1 = new VideoViewerWF();
_videoViewerWF1.Name = "videoViewerWF1";
_videoViewerWF1.Size = panel1.Size;
panel1.Controls.Add(_videoViewerWF1);
// Bind the camera image to the UI control
_videoViewerWF1.SetImageProvider(_imageProvider);
}
// Connect camera video channel to image provider and start
private void connectBtn_Click(object sender, EventArgs e)
{
_camera = IPCameraFactory.GetCamera("rtsp://192.168.113.150:554/ufirststream", "root", "pass");
_connector.Connect(_camera.VideoChannel, _imageProvider);
_camera.Start();
_videoViewerWF1.Start();
}
As a another solution i finally used Accord.net library to capture rtsp stream.

Xamarin Android view pdf in layout using Mupdf

I want to use MuPDF reader for my Xamarin Android project .
I am attempting to view a PDF in my relative layout
Here is my Relative layout code
<RelativeLayout
android:id="#+id/mupdf_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</RelativeLayout>
and here is main acitivity
SetContentView(Resource.Layout.Main);
RelativeLayout mupdfWrapper = FindViewById<RelativeLayout>(Resource.Id.mupdf_wrapper);
MuPDFCore core = new MuPDFCore(this, "test.pdf");
MuPDFReaderView reader = new MuPDFReaderView(this);
reader.Adapter = new MuPDFPageAdapter(this, new FilePicker.IFilePickerSupport() , core);
mupdfWrapper.AddView(reader);
mupdfWrapper.AddView(reader);
But i am getting Error here
"cannot create an istance of the abstract class or interface 'File picker .iflepickersupport"
Can anyone help me resolve this issue please.
Thanks in advance.
If you don't have use of FilePicker.IFilePickerSupport() then set it null
like
reader.Adapter = new MuPDFPageAdapter(this, null , core);
and second thing is that your code is very helpful for me there you are facing problem but your problem is my solution in my project so thank you.
and try it it will work and i am using it in my code it's work for me.
and last this thing is that sorry for my english.
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
File fileToDisplay = (File)fileFromAsset(this, "test.pdf");
fileToDisplay.SetWritable(true);
RelativeLayout mupdfWrapper = FindViewById<RelativeLayout>(Resource.Id.mupdf_wrapper);
MuPDFCore core = new MuPDFCore(this, fileToDisplay.AbsolutePath);
MuPDFReaderView reader = new MuPDFReaderView(this);
MuPDFPageAdapter adapter = new MuPDFPageAdapter(this, null, core);
reader.SetAdapter(adapter);
mupdfWrapper.AddView(reader);
}
private object fileFromAsset(Context context, string assetName)
{
File outFile = new File(context.CacheDir, assetName);
copy(context.Assets.Open(assetName), outFile);
return outFile;
}
private void copy(Stream inputStream, File output)
{
OutputStream outputStream = null;
var bufferedInputStream = new BufferedInputStream(inputStream);
try
{
outputStream = new FileOutputStream(output);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = bufferedInputStream.Read(bytes)) != -1)
{
outputStream.Write(bytes, 0, read);
}
}
finally
{
try
{
if (inputStream != null)
{
inputStream.Close();
inputStream.Dispose();
inputStream = null;
}
}
finally
{
if (outputStream != null)
{
outputStream.Close();
outputStream.Dispose();
outputStream = null;
}
}
}
}

MediaElement - set Source from Stream

I'm developing WPF application that stores some sound files as array of bytes in database. I want play these files via MediaElement control. MediaElement has Source property but its type is Uri. Does anyone know if it is possible to convert array of bytes to Uri?
Thanks
Here is a workaround using a self hosted media server
let's start
MainWindow.xaml
<MediaElement x:Name="media" />
MainWindow.cs
public MainWindow()
{
InitializeComponent();
//host a media server on some port
MediaServer ws = new MediaServer(RenderVideo, "http://localhost:8080/");
ws.Run();
//set the media server's url as the source of media element
media.Source = new Uri("http://localhost:8080/");
}
private byte[] RenderVideo(HttpListenerRequest r)
{
//get the video bytes from the server etc. and return the same
return File.ReadAllBytes("e:\\vids\\Wildlife.wmv");
}
MediaServer class
class MediaServer
{
private readonly HttpListener _listener = new HttpListener();
private readonly Func<HttpListenerRequest, byte[]> _responderMethod;
public MediaServer(Func<HttpListenerRequest, byte[]> method, string prefix)
{
if (!HttpListener.IsSupported)
throw new NotSupportedException(
"Needs Windows XP SP2, Server 2003 or later.");
if (prefix == null)
throw new ArgumentException("prefix");
if (method == null)
throw new ArgumentException("method");
_listener.Prefixes.Add(prefix);
_responderMethod = method;
_listener.Start();
}
public void Run()
{
ThreadPool.QueueUserWorkItem((o) =>
{
try
{
while (_listener.IsListening)
{
ThreadPool.QueueUserWorkItem((c) =>
{
var ctx = c as HttpListenerContext;
try
{
byte[] buf = _responderMethod(ctx.Request);
ctx.Response.ContentLength64 = buf.Length;
ctx.Response.ContentType = "application/octet-stream";
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
}
catch { }
finally
{
ctx.Response.OutputStream.Close();
}
}, _listener.GetContext());
}
}
catch { }
});
}
public void Stop()
{
_listener.Stop();
_listener.Close();
}
}
give it a try, I am successful able to play the video, I hope same for you too
For the MediaServer I have used Simple C# Web Server with some modifications.
above could be made shorter using Reactive Extensions. I'll give a try on the same if this works for you.
Also we can make the media server generic to pass the id of the video in url and in return it will stream back the desired video from the DB

BinaryReader throws NullReferenceException when being disposed

I have a class MyClass that needs data from a file that will be used throughout the run of the program. To read in the data I have another class OpenFileService that derives from IDisposable and uses a BinaryReader to read in all the data:
internal class OpenFileService : IDisposable
{
#region disposing data
bool disposed = false;
public void Dispose()
{
if (!disposed)
{
Dispose(true);
GC.SuppressFinalize(this);
disposed = true;
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
br.Dispose();
}
}
~OpenFileService()
{
Dispose(false);
}
#endregion
internal event EventHandler ErrorInFileReadEventHandler;
internal event EventHandler SuccessfulFileReadEventHandler;
private BinaryReader br;
internal void OpenFile(object obj)
{
MyClass sender = obj as MyClass;
bool isWrongFormat = false;
try
{
br = new BinaryReader(File.Open((sender).fileName, FileMode.Open));
//read in header from file.
if (//header shows wrong file format)
{
isWrongFormat = true;
throw new System.Exception();
}
//read rest of file.
SuccessfulFileReadEventHandler(this,null);
}
catch
{
if (isWrongFormat)
MessageBox.Show("Wrong format.");
else
MessageBox.Show("Couldn't access.");
ErrorInFileReadEventHandler(this, null);
return;
}
finally
{
this.Dispose();
}
}
}
And it is used as such:
class MyClass
{
internal filePath; //assuming it has already been determined
internal ImageData(string filePath)
{
this.filePath = filePath;
OpenFileService ofs = new OpenFileService();
ofs.ErrorInFileReadEventHandler += new EventHandler(UnsuccessfulFileRead);
ofs.SuccessfulFileReadEventHandler += new EventHandler(SuccessfulFileRead);
Thread thread = new Thread(new ParameterizedThreadStart(ofs.OpenFile));
thread.IsBackground = true;
thread.Start(this);
}
}
Now when the file format is wrong and I create the exception myself in the try block, everything works without any problems, but when the file could actually not be accessed (e.g. write-protected) br.Dispose(); creates a NullReferenceException and I cannot figure out why.
I really stripped down the code to its bare essentials, I hope it's not too long.
Edit: Possible answer can be found from the accepted answer here as a recommended answer by Microsoft.
The issue might become clearer if you split your file-open logic across two lines:
try
{
var fs = File.Open((sender).fileName, FileMode.Open);
br = new BinaryReader(fs);
}
finally
{
br.Dispose();
}
When the File.Open call fails, the exception is thrown without anything being assigned to the br field. When you attempt to dispose it in the finally block, it would still be null, thus your exception.
Edit: The way I would suggest fixing this:
try
{
using (FileStream fs = File.Open(sender.fileName, FileMode.Open))
using (BinaryReader br = new BinaryReader(fs))
{
//read in header from file.
if ( /*header shows wrong file format*/ )
{
isWrongFormat = true;
MessageBox.Show("Wrong format.");
ErrorInFileReadEventHandler(this, null);
}
else
{
//read rest of file.
SuccessfulFileReadEventHandler(this, null);
}
}
}
catch
{
MessageBox.Show("Couldn't access.");
ErrorInFileReadEventHandler(this, null);
}
In the process, I've demoted your BinaryReader from a field to a local variable. Since you're only accessing it within the OpenFile method (and disposing it before returning), there's no need for its reference to persist outside the method.

Categories