Search code examples
c#wpfaudioaudio-converter

Is there any way to convert .mp4 format audio to .wav format just in memory?


I'm developing a WPF application, where I have to play audio. I receive the audio data in .mp4 format (in a byte array) and the only restriction is that I can't write it out to the hard disk.

I found couple of solutions for playing the .mp4 format, for example with WMPLib.WindowsMediaPlayer, but I can't give a byte array, or stream to this library, to play the audio. It just accepts the file path.

Then I found the System.Media.SoundPlayer, which can play audio from a stream, but just in .wav format. I started to search for solutions to convert from mp4 to wav. I found the NAudio library and I could make the conversion the following way:

using (var data = new MediaFoundationReader(filePath)) {
  var stream = new MemoryStream();
  WaveFileWriter.WriteWavFileToStream(stream, data);
}

The problem with this is that I can instantiate the MediaFoundationReader just with a file path parameter. I didn't find any way to create it without using files. I think this was also a dead end.

So, any suggestion would be helpful about how can I convert audio in memory, or maybe how can I play directly the .mp4 file from a byte array or stream?


Solution

  • Finally I found a solution which converts to an .mp3 format, but it can also convert to .wav. I could use the uwp transcode API the following way:

    public static async void ConvertMp4ToMp3(byte[] mp4Data, Action<Stream> doneCallback) {
        MediaEncodingProfile profile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High);
        var inputStream = new MemoryRandomAccessStream(mp4Data);
        var outputStream = new InMemoryRandomAccessStream();
        MediaTranscoder transcoder = new MediaTranscoder();
    
        PrepareTranscodeResult prepareOperation = await transcoder.PrepareStreamTranscodeAsync(inputStream, outputStream, profile);
        if (prepareOperation.CanTranscode) {
            //start to convert
            var transcodeOperation = prepareOperation.TranscodeAsync();
    
            //registers completed event handler 
            transcodeOperation.Completed += (IAsyncActionWithProgress<double> asyncInfo, AsyncStatus status) => {
                asyncInfo.GetResults();
                var stream = outputStream.AsStream();
                stream.Position = 0;
                doneCallback(stream);
            };
        } else {
            doneCallback(null);
        }
    }
    

    The imports:

    using System;
    using System.IO;
    using Windows.Foundation;
    using Windows.Media.MediaProperties;
    using Windows.Media.Transcoding;
    using Windows.Storage.Streams;
    

    And the MemoryRandomAccessStream is just an implementation of the IRandomAccesStream interface and can be found here.