0

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;
    }
}
Pavan Jakhu
  • 71
  • 1
  • 8
  • Is there any log from your switch case? Have you tried transcoding anyway? – Lou Garczynski Nov 05 '19 at 01:30
  • 1
    Hi, I tested some different types of audio files, only pcm can't be transcoded properly, and it can't provide valid error messages. `MediaTranscode`may can not transcode pcm audio file directly now. I am looking for help from an engineer to see if there are other solutions. – Richard Zhang Nov 05 '19 at 05:07
  • @LouisGarczynski Yes, it goes to default case and prints "unknown failure." – Pavan Jakhu Nov 05 '19 at 15:52
  • @RichardZhang-MSFT Okay, thank you! I really appreciate it! I was looking to use ffmpeg first but you can't launch an external process in UWPs. – Pavan Jakhu Nov 05 '19 at 15:53
  • If you have access to the file, why not just use any kind of online converter to just get the mp3? – Lou Garczynski Nov 05 '19 at 16:35
  • @LouisGarczynski I can't have the user manually do it since I need to automate it for them and I need to add that audio file to a screen recorded video file afterwards, then send it to our server for our users to view on our website at a later date. – Pavan Jakhu Nov 05 '19 at 16:44
  • @PavanJakhu Sorry, I have to fix my reply. PCM and WAV are similar, but WAV audio files have their own header files, PCM does not, just a simple data file. This may be why `Transcode` does not recognize it correctly, so you may need to add a header file to the PCM to convert it to WAV format and then convert it to MP3 format. – Richard Zhang Nov 06 '19 at 03:11
  • My question was more, "is it the same file every time", in which case you could do the conversion once. You could also manually prepend the header if the rest of the file is similar. – Lou Garczynski Nov 06 '19 at 17:41
  • @RichardZhang-MSFT Okay, thanks! I have gotten it working but it seems my PCM audio data is not how I expected it to be. So I will deal with that. – Pavan Jakhu Nov 07 '19 at 15:11
  • @LouisGarczynski Oh no, it's not the same audio. It's the recording of the remote user's voice. – Pavan Jakhu Nov 07 '19 at 15:12

1 Answers1

1

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);
}
Pavan Jakhu
  • 71
  • 1
  • 8