xscDevBlog – LastSharp & Co.

Der xscheme-DevelopmentBlog

Audiostreaming mit irrKlang (C#)

with one comment



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);
        }
    }
}

Written by xsc

August 14th, 2008 at 7:59 am

Posted in C#

Tagged with , , , , ,

One Response to 'Audiostreaming mit irrKlang (C#)'

Subscribe to comments with RSS or TrackBack to 'Audiostreaming mit irrKlang (C#)'.

  1. [...] 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). [...]

Leave a Reply