Pavan Jakhu
Pavan Jakhu

Reputation: 81

Transcode PCM Audio to MP3 using MediaTranscoder with C#

I am trying to transcode a PCM formatted audio file that I saved from a WebRTC call. The format of the audio stream, reported by WebRTC, is 16 bit depth, 1 channel and 48000 Hz sample rate. I want to convert the audio to MP3 so I can add the audio as a background track to a screen recording of my Unity UWP app afterwards (using MediaComposition). I'm having trouble with the first part: trying to transcode my PCM audio file to a MP3 file. preparedTranscodeResult.CanTranscode is returning false when I try to prepare the transcode. The following is my code.

StorageFile remoteAudioPCMFile = await StorageFile.GetFileFromPathAsync(Path.Combine(Application.temporaryCachePath, "remote.pcm").Replace("/", "\\"));
StorageFolder tempFolder = await StorageFolder.GetFolderFromPathAsync(Application.temporaryCachePath.Replace("/", "\\"));
StorageFile remoteAudioMP3File = await tempFolder.CreateFileAsync("remote.mp3", CreationCollisionOption.ReplaceExisting);

MediaEncodingProfile profile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.Auto);
profile.Audio.BitsPerSample = 16;
profile.Audio.ChannelCount = 1;
profile.Audio.SampleRate = 48000;

MediaTranscoder transcoder = new MediaTranscoder();
var preparedTranscodeResult = await transcoder.PrepareFileTranscodeAsync(remoteAudioPCMFile, remoteAudioMP3File, profile);

if (preparedTranscodeResult.CanTranscode)
{
    await preparedTranscodeResult.TranscodeAsync();
}
else
{
    if (remoteAudioPCMFile != null)
    {
        await remoteAudioPCMFile.DeleteAsync();
    }

    if (remoteAudioMP3File != null)
    {
        await remoteAudioMP3File.DeleteAsync();
    }

    switch (preparedTranscodeResult.FailureReason)
    {
        case TranscodeFailureReason.CodecNotFound:
            Debug.LogError("Codec not found.");
            break;
        case TranscodeFailureReason.InvalidProfile:
            Debug.LogError("Invalid profile.");
            break;
        default:
            Debug.LogError("Unknown failure.");
            break;
    }
}

Upvotes: 0

Views: 635

Answers (1)

Pavan Jakhu
Pavan Jakhu

Reputation: 81

So what I had to do is write the header into my FileStream before I started writing the data into the stream. I got it from this post.

private void WriteWavHeader(FileStream stream, bool isFloatingPoint, ushort channelCount, ushort bitDepth, int sampleRate, int totalSampleCount)
{
    stream.Position = 0;

    // RIFF header.
    // Chunk ID.
    stream.Write(Encoding.ASCII.GetBytes("RIFF"), 0, 4);

    // Chunk size.
    stream.Write(BitConverter.GetBytes((bitDepth / 8 * totalSampleCount) + 36), 0, 4);

    // Format.
    stream.Write(Encoding.ASCII.GetBytes("WAVE"), 0, 4);



    // Sub-chunk 1.
    // Sub-chunk 1 ID.
    stream.Write(Encoding.ASCII.GetBytes("fmt "), 0, 4);

    // Sub-chunk 1 size.
    stream.Write(BitConverter.GetBytes(16), 0, 4);

    // Audio format (floating point (3) or PCM (1)). Any other format indicates compression.
    stream.Write(BitConverter.GetBytes((ushort)(isFloatingPoint ? 3 : 1)), 0, 2);

    // Channels.
    stream.Write(BitConverter.GetBytes(channelCount), 0, 2);

    // Sample rate.
    stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);

    // Bytes rate.
    stream.Write(BitConverter.GetBytes(sampleRate * channelCount * (bitDepth / 8)), 0, 4);

    // Block align.
    stream.Write(BitConverter.GetBytes(channelCount * (bitDepth / 8)), 0, 2);

    // Bits per sample.
    stream.Write(BitConverter.GetBytes(bitDepth), 0, 2);



    // Sub-chunk 2.
    // Sub-chunk 2 ID.
    stream.Write(Encoding.ASCII.GetBytes("data"), 0, 4);

    // Sub-chunk 2 size.
    stream.Write(BitConverter.GetBytes(bitDepth / 8 * totalSampleCount), 0, 4);
}

Upvotes: 1

Related Questions