Search code examples
c#xamarinstreamandroid-mediaplayer

Stream audio bytes to MediaPlayer


Is there a way to stream bytes directly to Android.Media.MediaPlayer? So when I receive a bunch of bytes I can throw them to this class and play them and repeat? I couldn't find any examples of feeding MediaPlayer with bytes and it seems like the most reasonable approach.

Currently I'm playing around with storing each packet as a temporary file, so I can play a small portion of music and dispose it right away, but I haven't gotten it to work yet and it feels like a terrible approach.

This is what I've tried so far. I receive a small portion of sound (bArray) and I append .wav header to it so that I can play it. I do this with each packet I receive. This header matches the data I receive (I use NAudio library to record sound):

public void PlayAudio(byte[] bArray)
{
    var player = new MediaPlayer();
    player.Prepared += (sender, args) =>
    {
        player.Start();
    };

    var header = new byte[]
    {
        0x52, 0x49, 0x46, 0x46, // b Chunk ID (RIFF)
        //0x24, 0xDC, 0x05, 0x00, // l Chunk size
        0x32, 0x4B, 0x00, 0x00,
        0x57, 0x41, 0x56, 0x45, // b Format WAVE
        0x66, 0x6d, 0x74, 0x20, // b Subchunk 1 id
        0x12, 0x00, 0x00, 0x00, // l Subchunk 1 size (size of the rest of the header) = 16
        0x03, 0x00,             // l Audio format, 1 = Linear Quantization, others = compression
        0x02, 0x00,             // l Number of channels, 1 = mono, 2 = stereo
        0x80, 0xBB, 0x00, 0x00, // l Sample rate
        0x00, 0xDC, 0x05, 0x00, // l Byte rate (SampleRate * NumChannels * BitsPerSample / 8)
        0x08, 0x00,             // l Block align (NumChannels * BitsPerSample / 8)
        0x20, 0x00,             // l Bits per sample 
        0x00, 0x00, 0x66, 0x61, // compression data
        0x63, 0x74, 0x04, 0x00, // compression data
        0x00, 0x00, 0x60, 0x09, // compression data
        0x00, 0x00,             // compression data
        0x64, 0x61, 0x74, 0x61, // b Subchunk 2 id (Contains the letters "data")
        0x00, 0x4B, 0x00, 0x00, // l Subchunk 2 Size
    };

        var file = File.CreateTempFile("example", ".wav");
        var fos = new FileOutputStream(file);
        fos.Write(header);
        fos.Write(bArray);
        fos.Close();

        player.Reset();

        var fis = new FileInputStream(file);
        player.SetDataSource(fis.FD);
        player.Prepare();
}

Obviously this code is not optimized, but I cannot even get it to work and I've spent a lot of time on it, so hopefully there is a different solution to this problem.


Solution

  • As far as I know, MediaPlayer cannot play continous stream (was not designed for that). However, there is more low-level class, AudioTrack, which is capable of doing that.

    Here is small sample from one of my projects:

    private int _bufferSize;
    private AudioTrack _output;
    
    // in constructor
    _bufferSize = AudioTrack.GetMinBufferSize(8000, ChannelOut.Mono, Encoding.Pcm16bit);
    
    // when starting to play audio
    _output = new AudioTrack(Stream.Music, 8000, ChannelOut.Mono, Encoding.Pcm16bit, _bufferSize, AudioTrackMode.Stream);
    _output.Play();
    
    // when data arrives via UDP socket
    byte[] decoded = _codec.Decode(decrypted, 0, decrypted.Length);                
    // just write to AudioTrack
    _output.Write(decoded, 0, decoded.Length);
    

    Of course you need to understand what all those parameters mean (like Pcm16bit or sample rate) to implement it correctly.