none
How to play a stream of AAC with NAudio and Drive API? RRS feed

  • Question

  • Instead of download the whole uploaded audio from Google Drive (extension can be m4a) I would like to play as buffered content (eg on YouTube, Spotify, etc).

    That's why I tried to write two functions that first download a part of stream and then play it with NAudio until it get to the end of content (or something interrupts).

    First of all, I have no idea what would be the size of the buffer. In the other hand, when should I load the next chunk of stream (so how to get the remaining stream of currently playing chuck)?

    Unfortunately, I can't get there because StreamMediaFoundationReader throws an exception: System.Runtime.InteropServices.COMException: 'The byte stream type of the specified URL is not supported. (Exception HRESULT value: 0xC00D36C4) '

    
    
    public Stream GetStreamChunk(string fileId, RangeHeaderValue range)
    {
    	var getrequest = driveService.Files.Get(fileId);
    	var stream = new MemoryStream();
    	getrequest.DownloadRange(stream, range);
    	return stream;
    }
    
    public void PlayStream(AudioObject file)
    {
            //size of chuck
    	long from = 0;
    	long to = file.size / 5;
    
    	RangeHeaderValue range = new RangeHeaderValue(from, to);
    	Stream stream = GetStreamChunk(file.Id, range);
    
    	while (stream.Length <= file.size - 1)
    	{
    		using (var memoryStream = new MemoryStream())
    		{
    			stream.CopyTo(memoryStream);
    			memoryStream.Position = 0;
    			
    			using (WaveStream blockAlignedStream = new BlockAlignReductionStream(
    				WaveFormatConversionStream.CreatePcmStream(
    					new StreamMediaFoundationReader(memoryStream))))
    			{
    			   using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
    			   {
    					waveOut.Init(blockAlignedStream);
    					waveOut.Play();
    					while (waveOut.PlaybackState == PlaybackState.Playing)
    					{
    						System.Threading.Thread.Sleep(100);
    					}
    			   }
    			}
    		}
    		stream = GetStreamChunk(file.Id, range);
    	}
    }
    
    


    • Edited by AdamDlt Thursday, January 16, 2020 12:46 PM more readable code
    Thursday, January 16, 2020 12:43 PM

All replies

  • You will need two separate threads.  One of them is reading from the web site and copying into your own circular buffer, and the other is handling GetStreamChunk by pulling from your circular buffer.  There is clearly not enough time to do a network exchange during GetStreamChunk.  NAudio doesn't call that until its own buffers (which are VERY small) are nearly dry.  You will need to have your own buffer.

    You'll want to start the downlaoding first, to start filling the buffer.  Once you reach some threshold, you can start PlayStream.  The downloader thread will then monitor the circular buffer.  If it falls below a threshold, you'll fetch more data, thereby keeping the buffer mostly full.

    As an example (you'll need to play with these numbers to see how they work with your network latency), if you have a 500kB buffer, you might have the "start PlayStream" threshhold at 80kB, or whatever represents a second of data (again, adjustable).  Then, you can decide to reload the buffer when it falls below 80% full, or something like that.


    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    Thursday, January 16, 2020 7:01 PM
  • Hi AdamDlt,
    NAudio's Media Foundation support is fairly new, so not all features are yet supported. I am afaid that stream-based playback is one of the features that is not yet supported.
    If you want to get started and implement IMFByteStream yourself, you need to create a wrapper class that implements this interface, and then adapt the NAudio MediaFoundationReader class to use the stream.
    More detalis you can refer these links.
    [wma audio stream to mp3 stream using NAudio c#]
    [NAudio MediaFoundationRecorder EncodeToAAC Byte array instead of file]
    Hope these are helpful for you.
    Best Regards,
    Daniel Zhang


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, January 17, 2020 7:26 AM
  • Thank you for your reply!

    If I understand it correctly, I have to pay attention on the current contents of the buffer and if it drop under X% (eg 80%) then I can start uploading.

    You mentioned that the NAudio's buffer is quite small. In this case, how much data is worth to storing in it?
    Furthermore, what do I use to determine the range of an audio file to download to puffer?
    I have no idea, how do I determine how much data of stream is equivalent to 1 second?

    Thanks in advance,
    Adam

    Friday, January 17, 2020 1:00 PM
  • Hi Daniel!

    Thanks for the help!

    I tried this solution, but I have no idea how to define the members of wrapper class. Can you show me an example of these?

    public class InteropStream : IMFByteStream
    {
           ...
    }
    Thanks in advance,
    Adam


    Friday, January 17, 2020 1:05 PM