Skip to content

Commit

Permalink
Add Stream support to MediaFoundationEncoder
Browse files Browse the repository at this point in the history
  • Loading branch information
aianlinb committed May 23, 2021
1 parent fb35ce8 commit 66d567f
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 3 deletions.
10 changes: 8 additions & 2 deletions NAudio.Wasapi/MediaFoundation/MediaFoundationAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ public static class MediaFoundationAttributes
/// Contains the class identifier (CLSID) of an MFT.
/// </summary>
[FieldDescription("Class identifier")]
public static readonly Guid MFT_TRANSFORM_CLSID_Attribute = new Guid("6821c42b-65a4-4e82-99bc-9a88205ecd0c");

public static readonly Guid MFT_TRANSFORM_CLSID_Attribute = new Guid("6821c42b-65a4-4e82-99bc-9a88205ecd0c");

/// <summary>
/// Specifies the container type of an encoded file. The container types are supported by Media Foundation.
/// </summary>
[FieldDescription("Container type")]
public static readonly Guid MF_TRANSCODE_CONTAINERTYPE = new Guid(0x150ff23f, 0x4abc, 0x478b, 0xac, 0x4f, 0xe1, 0x91, 0x6f, 0xba, 0x1c, 0xca);

/// <summary>
/// Contains the registered input types for a Media Foundation transform (MFT).
/// </summary>
Expand Down
20 changes: 20 additions & 0 deletions NAudio.Wasapi/MediaFoundation/TranscodeContainerTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace NAudio.MediaFoundation {
/// <summary>
/// https://docs.microsoft.com/en-us/windows/win32/medfound/mf-transcode-containertype
/// </summary>
public static class TranscodeContainerTypes {
public static readonly Guid MFTranscodeContainerType_ASF = new Guid(0x430f6f6e, 0xb6bf, 0x4fc1, 0xa0, 0xbd, 0x9e, 0xe4, 0x6e, 0xee, 0x2a, 0xfb);
public static readonly Guid MFTranscodeContainerType_MPEG4 = new Guid(0xdc6cd05d, 0xb9d0, 0x40ef, 0xbd, 0x35, 0xfa, 0x62, 0x2c, 0x1a, 0xb2, 0x8a);
public static readonly Guid MFTranscodeContainerType_MP3 = new Guid(0xe438b912, 0x83f1, 0x4de6, 0x9e, 0x3a, 0x9f, 0xfb, 0xc6, 0xdd, 0x24, 0xd1);
public static readonly Guid MFTranscodeContainerType_3GP = new Guid(0x34c50167, 0x4472, 0x4f34, 0x9e, 0xa0, 0xc4, 0x9f, 0xba, 0xcf, 0x03, 0x7d);
public static readonly Guid MFTranscodeContainerType_AC3 = new Guid(0x6d8d91c3, 0x8c91, 0x4ed1, 0x87, 0x42, 0x8c, 0x34, 0x7d, 0x5b, 0x44, 0xd0);
public static readonly Guid MFTranscodeContainerType_ADTS = new Guid(0x132fd27d, 0x0f02, 0x43de, 0xa3, 0x01, 0x38, 0xfb, 0xbb, 0xb3, 0x83, 0x4e);
public static readonly Guid MFTranscodeContainerType_MPEG2 = new Guid(0xbfc2dbf9, 0x7bb4, 0x4f8f, 0xaf, 0xde, 0xe1, 0x12, 0xc4, 0x4b, 0xa8, 0x82);
public static readonly Guid MFTranscodeContainerType_FMPEG4 = new Guid(0x9ba876f1, 0x419f, 0x4b77, 0xa1, 0xe0, 0x35, 0x95, 0x9d, 0x9d, 0x40, 0x4);
public static readonly Guid MFTranscodeContainerType_WAVE = new Guid(0x64c3453c, 0x0f26, 0x4741, 0xbe, 0x63, 0x87, 0xbd, 0xf8, 0xbb, 0x93, 0x5b);
public static readonly Guid MFTranscodeContainerType_AVI = new Guid(0x7edfe8af, 0x402f, 0x4d76, 0xa3, 0x3c, 0x61, 0x9f, 0xd1, 0x57, 0xd0, 0xf1);
public static readonly Guid MFTranscodeContainerType_AMR = new Guid(0x025d5ad3, 0x621a, 0x475b, 0x96, 0x4d, 0x66, 0xb1, 0xc8, 0x24, 0xf0, 0x79);
}
}
97 changes: 96 additions & 1 deletion NAudio.Wasapi/MediaFoundationEncoder.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using NAudio.MediaFoundation;
using NAudio.Utils;

Expand Down Expand Up @@ -86,6 +88,21 @@ public static void EncodeToWma(IWaveProvider inputProvider, string outputFile, i
}
}

/// <summary>
/// Helper function to simplify encoding Window Media Audio
/// Should be supported on Vista and above (not tested)
/// </summary>
/// <param name="inputProvider">Input provider, must be PCM</param>
/// <param name="outputStream">Output stream</param>
/// <param name="desiredBitRate">Desired bitrate. Use GetEncodeBitrates to find the possibilities for your input type</param>
public static void EncodeToWma(IWaveProvider inputProvider, Stream outputStream, int desiredBitRate = 192000) {
var mediaType = SelectMediaType(AudioSubtypes.MFAudioFormat_WMAudioV8, inputProvider.WaveFormat, desiredBitRate);
if (mediaType == null) throw new InvalidOperationException("No suitable WMA encoders available");
using (var encoder = new MediaFoundationEncoder(mediaType)) {
encoder.Encode(outputStream, inputProvider, TranscodeContainerTypes.MFTranscodeContainerType_ASF);
}
}

/// <summary>
/// Helper function to simplify encoding to MP3
/// By default, will only be available on Windows 8 and above
Expand All @@ -103,6 +120,21 @@ public static void EncodeToMp3(IWaveProvider inputProvider, string outputFile, i
}
}

/// <summary>
/// Helper function to simplify encoding to MP3
/// By default, will only be available on Windows 8 and above
/// </summary>
/// <param name="inputProvider">Input provider, must be PCM</param>
/// <param name="outputStream">Output stream</param>
/// <param name="desiredBitRate">Desired bitrate. Use GetEncodeBitrates to find the possibilities for your input type</param>
public static void EncodeToMp3(IWaveProvider inputProvider, Stream outputStream, int desiredBitRate = 192000) {
var mediaType = SelectMediaType(AudioSubtypes.MFAudioFormat_MP3, inputProvider.WaveFormat, desiredBitRate);
if (mediaType == null) throw new InvalidOperationException("No suitable MP3 encoders available");
using (var encoder = new MediaFoundationEncoder(mediaType)) {
encoder.Encode(outputStream, inputProvider, TranscodeContainerTypes.MFTranscodeContainerType_MP3);
}
}

/// <summary>
/// Helper function to simplify encoding to AAC
/// By default, will only be available on Windows 7 and above
Expand All @@ -124,6 +156,25 @@ public static void EncodeToAac(IWaveProvider inputProvider, string outputFile, i
}
}

/// <summary>
/// Helper function to simplify encoding to AAC
/// By default, will only be available on Windows 7 and above
/// </summary>
/// <param name="inputProvider">Input provider, must be PCM</param>
/// <param name="outputStream">Output stream</param>
/// <param name="desiredBitRate">Desired bitrate. Use GetEncodeBitrates to find the possibilities for your input type</param>
public static void EncodeToAac(IWaveProvider inputProvider, Stream outputStream, int desiredBitRate = 192000) {
// Information on configuring an AAC media type can be found here:
// http://msdn.microsoft.com/en-gb/library/windows/desktop/dd742785%28v=vs.85%29.aspx
var mediaType = SelectMediaType(AudioSubtypes.MFAudioFormat_AAC, inputProvider.WaveFormat, desiredBitRate);
if (mediaType == null) throw new InvalidOperationException("No suitable AAC encoders available");
using (var encoder = new MediaFoundationEncoder(mediaType)) {
// should AAC container have ADTS, or is that just for ADTS?
// http://www.hydrogenaudio.org/forums/index.php?showtopic=97442
encoder.Encode(outputStream, inputProvider, TranscodeContainerTypes.MFTranscodeContainerType_MPEG4);
}
}

/// <summary>
/// Tries to find the encoding media type with the closest bitrate to that specified
/// </summary>
Expand Down Expand Up @@ -186,6 +237,33 @@ public void Encode(string outputFile, IWaveProvider inputProvider)
}
}

/// <summary>
/// Encodes a file
/// </summary>
/// <param name="outputStream">Output stream</param>
/// <param name="inputProvider">Input provider (should be PCM, some encoders will also allow IEEE float)</param>
/// <param name="transcodeContainerType">One of <see cref="TranscodeContainerTypes"/></param>
public void Encode(Stream outputStream, IWaveProvider inputProvider, Guid transcodeContainerType) {
if (inputProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm && inputProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat) {
throw new ArgumentException("Encode input format must be PCM or IEEE float");
}

var inputMediaType = new MediaType(inputProvider.WaveFormat);

var writer = CreateSinkWriter(new ComStream(outputStream), transcodeContainerType);
try {
writer.AddStream(outputMediaType.MediaFoundationObject, out int streamIndex);

// n.b. can get 0xC00D36B4 - MF_E_INVALIDMEDIATYPE here
writer.SetInputMediaType(streamIndex, inputMediaType.MediaFoundationObject, null);

PerformEncode(writer, streamIndex, inputProvider);
} finally {
Marshal.ReleaseComObject(writer);
Marshal.ReleaseComObject(inputMediaType.MediaFoundationObject);
}
}

private static IMFSinkWriter CreateSinkWriter(string outputFile)
{
// n.b. could try specifying the container type using attributes, but I think
Expand Down Expand Up @@ -214,6 +292,23 @@ private static IMFSinkWriter CreateSinkWriter(string outputFile)
return writer;
}

private static IMFSinkWriter CreateSinkWriter(IStream outputStream, Guid TranscodeContainerType) {
// n.b. could try specifying the container type using attributes, but I think
// it does a decent job of working it out from the file extension
// n.b. AAC encode on Win 8 can have AAC extension, but use MP4 in win 7
// http://msdn.microsoft.com/en-gb/library/windows/desktop/dd389284%28v=vs.85%29.aspx
IMFSinkWriter writer;
var attributes = MediaFoundationApi.CreateAttributes(1);
attributes.SetGUID(MediaFoundationAttributes.MF_TRANSCODE_CONTAINERTYPE, TranscodeContainerType);
try {
MediaFoundationInterop.MFCreateMFByteStreamOnStream(outputStream, out var ppByteStream);
MediaFoundationInterop.MFCreateSinkWriterFromURL(null, ppByteStream, attributes, out writer);
} finally {
Marshal.ReleaseComObject(attributes);
}
return writer;
}

private void PerformEncode(IMFSinkWriter writer, int streamIndex, IWaveProvider inputProvider)
{
int maxLength = inputProvider.WaveFormat.AverageBytesPerSecond * 4;
Expand All @@ -222,7 +317,7 @@ private void PerformEncode(IMFSinkWriter writer, int streamIndex, IWaveProvider
writer.BeginWriting();

long position = 0;
long duration = 0;
long duration;
do
{
duration = ConvertOneBuffer(writer, streamIndex, inputProvider, position, managedBuffer);
Expand Down

0 comments on commit 66d567f

Please sign in to comment.