using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Windows; using System.Windows.Input; using NAudio.MediaFoundation; using NAudio.Wave; using NAudioWpfDemo.ViewModel; using OpenFileDialog = Microsoft.Win32.OpenFileDialog; using SaveFileDialog = Microsoft.Win32.SaveFileDialog; namespace NAudioWpfDemo.MediaFoundationEncode { internal class MediaFoundationEncodeViewModel : ViewModelBase, IDisposable { private readonly Dictionary> allMediaTypes; private EncoderViewModel selectedOutputFormat; private MediaTypeViewModel selectedMediaType; private string inputFile; private string inputFormat; private WaveFormat inputWaveFormat; public List OutputFormats { get; } public List SupportedMediaTypes { get; private set; } public ICommand EncodeCommand { get; } public ICommand SelectInputFileCommand { get; } public MediaFoundationEncodeViewModel() { MediaFoundationApi.Startup(); allMediaTypes = new Dictionary>(); SupportedMediaTypes = new List(); EncodeCommand = new DelegateCommand(Encode); SelectInputFileCommand = new DelegateCommand(SelectInputFile); // TODO: fill this by asking the encoders what they can do OutputFormats = new List { new EncoderViewModel() { Name = "AAC", Guid = AudioSubtypes.MFAudioFormat_AAC, Extension = ".mp4" }, // Windows 8 can do a .aac extension as well new EncoderViewModel() { Name = "Windows Media Audio", Guid = AudioSubtypes.MFAudioFormat_WMAudioV8, Extension = ".wma" }, new EncoderViewModel() { Name = "Windows Media Audio Professional", Guid = AudioSubtypes.MFAudioFormat_WMAudioV9, Extension = ".wma" }, new EncoderViewModel() { Name = "MP3", Guid = AudioSubtypes.MFAudioFormat_MP3, Extension = ".mp3" }, new EncoderViewModel() { Name = "Windows Media Audio Voice", Guid = AudioSubtypes.MFAudioFormat_MSP1, Extension = ".wma" }, new EncoderViewModel() { Name = "Windows Media Audio Lossless", Guid = AudioSubtypes.MFAudioFormat_WMAudio_Lossless, Extension = ".wma" }, new EncoderViewModel() { Name = "FLAC", Guid = AudioSubtypes.MFAudioFormat_FLAC, Extension = ".flac" }, new EncoderViewModel() { Name = "Apple Lossless (ALAC)", Guid = AudioSubtypes.MFAudioFormat_ALAC, Extension = ".m4a" }, new EncoderViewModel() { Name = "Fake for testing", Guid = Guid.NewGuid(), Extension = ".xyz" } }; SelectedOutputFormat = OutputFormats[0]; } private void SelectInputFile() { var ofd = new OpenFileDialog(); ofd.Filter = "Audio files|*.mp3;*.wav;*.wma;*.aiff;*.aac"; if (ofd.ShowDialog() == true) { if (TryOpenInputFile(ofd.FileName)) { InputFile = ofd.FileName; SetMediaTypes(); } } } private bool TryOpenInputFile(string file) { bool isValid = false; try { using (var reader = new MediaFoundationReader(file)) { InputFormat = reader.WaveFormat.ToString(); inputWaveFormat = reader.WaveFormat; } isValid = true; } catch (Exception e) { MessageBox.Show($"Not a supported input file ({e.Message})"); } return isValid; } public string InputFile { get => inputFile; set { if (inputFile != value) { inputFile = value; OnPropertyChanged(nameof(InputFile)); } } } public string InputFormat { get => inputFormat; set { if (inputFormat != value) { inputFormat = value; OnPropertyChanged(nameof(InputFormat)); } } } public EncoderViewModel SelectedOutputFormat { get => selectedOutputFormat; set { if (selectedOutputFormat != value) { selectedOutputFormat = value; SetMediaTypes(); OnPropertyChanged("SelectedOutputFormat"); } } } private void SetMediaTypes() { if (!allMediaTypes.ContainsKey(SelectedOutputFormat.Guid)) { TryGetSupportedMediaTypes(); } FilterSupportedMediaTypes(); OnPropertyChanged(nameof(SupportedMediaTypes)); SelectedMediaType = SupportedMediaTypes.FirstOrDefault(); } private void FilterSupportedMediaTypes() { //SupportedMediaTypes.Clear(); SupportedMediaTypes = new List(); if (inputWaveFormat == null) { SupportedMediaTypes.Add(new MediaTypeViewModel() {Name="Select an input file"}); return; } SupportedMediaTypes = allMediaTypes[SelectedOutputFormat.Guid] .Where(m => m.MediaType != null) .Where(m => m.MediaType.SampleRate == inputWaveFormat.SampleRate) .Where(m => m.MediaType.ChannelCount == inputWaveFormat.Channels) .ToList(); } private void TryGetSupportedMediaTypes() { var list = MediaFoundationEncoder.GetOutputMediaTypes(SelectedOutputFormat.Guid) .Select(mf => new MediaTypeViewModel(mf)) .ToList(); if (list.Count == 0) { list.Add(new MediaTypeViewModel() {Name = "Not Supported", Description = "No encoder found for this output type"}); } allMediaTypes[SelectedOutputFormat.Guid] = list; } public MediaTypeViewModel SelectedMediaType { get => selectedMediaType; set { if (selectedMediaType != value) { selectedMediaType = value; OnPropertyChanged(nameof(SelectedMediaType)); } } } private void Encode() { if (String.IsNullOrEmpty(InputFile) || !File.Exists(InputFile)) { MessageBox.Show("Please select a valid input file to convert"); return; } if (SelectedMediaType == null || SelectedMediaType.MediaType == null) { MessageBox.Show("Please select a valid output format"); return; } using (var reader = new MediaFoundationReader(InputFile)) { string outputUrl = SelectSaveFile(SelectedOutputFormat.Name, SelectedOutputFormat.Extension); if (outputUrl == null) return; using (var encoder = new MediaFoundationEncoder(SelectedMediaType.MediaType)) { try { encoder.Encode(outputUrl, reader); } catch (Exception e) { MessageBox.Show(e.Message, "Failed to encode"); } } } } private string SelectSaveFile(string formatName, string extension) { var sfd = new SaveFileDialog(); sfd.FileName = Path.GetFileNameWithoutExtension(InputFile) + " converted" + extension; sfd.Filter = formatName + "|*" + extension; //return (sfd.ShowDialog() == true) ? new Uri(sfd.FileName).AbsoluteUri : null; return (sfd.ShowDialog() == true) ? sfd.FileName : null; } public void Dispose() { MediaFoundationApi.Shutdown(); } } enum AacPayloadType { /// /// The stream contains raw_data_block elements only. /// RawData = 0, /// /// Audio Data Transport Stream (ADTS). The stream contains an adts_sequence, as defined by MPEG-2. /// Adts = 1, /// /// Audio Data Interchange Format (ADIF). The stream contains an adif_sequence, as defined by MPEG-2. /// Adif = 2, /// /// The stream contains an MPEG-4 audio transport stream with a synchronization layer (LOAS) and a multiplex layer (LATM). /// LoasLatm = 3 } internal class EncoderViewModel : ViewModelBase { public string Name { get; set; } public Guid Guid { get; set; } public string Extension { get; set; } } }