Audiostreaming mit irrKlang (C#)

Auf der Suche nach einem Ersatz für die DirectX-Wiedergabe in LastSharp bin ich gestern über irrKlang gestolpert. Soweit so gut: plattformunabhängig, einfach zu bedienen, eigentlich alles in Ordnung. Aber dann gab es da doch noch ein größeres Manko, das ich nicht ignorieren konnte: das Streaming von Dateien, die im Internet liegen, ist nicht möglich – und genau das bräuchte ich aber für LastSharp. Was also machen?
Über dieses Tutorial bin ich der Sache näher gekommen: Man muss eine eigene Implementierung des Interfaces IFileFactory erstellen, und – falls nötig – eine eigene Stream-Klasse. Und letzteres war durchaus nötig, da mir keine bereitgestellte Stream-Klasse die entsprechenden Möglichkeit der Pufferung eines Audiostreams geboten hat. So ist die Klasse InternetStream geboren worden, die eine Verbindung zu einer Datei im Internet herstellt (mittels HttpWebRequest), und die empfangenen Daten in einer Datei auf der Festplatte speichern kann, auf die ebenso die Möglichkeit gleichzeitigen Zugriffs besteht:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;
using System.Collections;
using IrrKlang;
public class InternetStream : Stream
{
// =======================================================
// Buffer-Daten
private Stream resp = null;
private string BufferFile = "streaming.mp3";
private FileStream OutputStream = null;
private FileStream BufferStream = null;
private Thread BufferThread = null;
// =======================================================
// Konstruktor
public InternetStream(string uri, string bfile)
{
// Dateiname des lokalen Files
this.BufferFile = bfile;
// FileStream zum Schreiben öffnen
if (File.Exists(this.BufferFile)) { try { File.Delete(this.BufferFile); } catch { } }
this.BufferStream = new FileStream(this.BufferFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
// ResponseStream zum File im Internet
this.resp = ((HttpWebRequest)WebRequest.Create(uri)).GetResponse().GetResponseStream();
// Pufferung starten
this.BufferThread = new Thread(new ThreadStart(this.FillBuffer));
this.BufferThread.Start();
}
// =======================================================
// Read: wenn zehn mal 0 zurückgegeben wird, werden alle
// Streams geschlossen und der Thread beendet
private int ReadTries = 0;
public override int Read(byte[] array, int offset, int count)
{
// Ausgabe-Stream öffnen
if (this.OutputStream == null) this.OutputStream = new FileStream(this.BufferFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
try
{
// Dem Puffer Zeit geben, Daten zu holen
while (this.OutputStream.Length == 0) Thread.Sleep(100);
// Daten lesen
int s = this.OutputStream.Read(array, offset, count);
// Wenn 0: ein erfolgloser Versuch mehr
if (s == 0) this.ReadTries++;
if (this.ReadTries > 10)
{
this.Close();
}
//
return s;
}
catch { return 0; }
}
// =======================================================
// Seek
public override long Seek(long offset, SeekOrigin loc)
{
return this.OutputStream.Seek(offset, loc);
}
// =======================================================
// Puffer-Thread
private void FillBuffer()
{
byte[] buffer = new byte[20 * 1024];
int rd = 0;
while ((rd = this.resp.Read(buffer, 0, buffer.Length)) > 0)
{
this.BufferStream.Write(buffer, 0, rd);
}
}
// =======================================================
// Schließen
public override void Close()
{
this.OutputStream.Close();
this.BufferStream.Close();
this.BufferThread.Abort();
if (File.Exists(this.BufferFile))
try { File.Delete(this.BufferFile); }
catch { }
base.Close();
}
// =======================================================
// Length
public override long Length
{
get
{
if (this.OutputStream == null) return 0;
else return this.OutputStream.Length;
}
}
// =======================================================
// Properties: CanRead, CanSeek, CanWrite
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
// =======================================================
// restliche Methoden, damit die Stream-Implementierung
// komplett ist
public override void Flush()
{
this.OutputStream.Flush();
}
public override long Position
{
get
{
return this.OutputStream.Position;
}
set
{
this.OutputStream.Position = value;
}
}
public override void SetLength(long value)
{
this.OutputStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
return;
}
}
Die entsprechende IFileFactory-Implementation:
public class StreamFileFactory : IFileFactory
{
// =====================================
// Öffne ein File (von Irrklang verwendete Methode)
public Stream openFile(string filename)
{
if (filename.StartsWith("http://"))
{
// neuen Starten
string fn = Application.StartupPath + "/streaming.mp3";
try
{
InternetStream istream = new InternetStream(filename, fn);
return istream;
}
catch { return null; }
}
else
{
// lokale Datei
return new FileStream(filename, FileMode.Open, FileAccess.Read);
}
}
}
[...] Mit LastSharp 0.3 kommt endlich die Abkehr von DirectX, denn ab sofort sorgt irrKlang für Audio-Wiedergabe und -Streaming (letzteres nicht ohne intensiven Arbeitsaufwand). [...]
[Release] LastSharp 0.3 at xscDevBlog - LastSharp & Co.
16 Aug 08 at 03:16