title: How to play audio using Sink Writer from Microsoft Media Foundation
body: <p>I'm trying to create an audio visualizer using Microsoft Media Foundation. For this I need to intercept the samples and simultaneously play them. Using a Media Session with Topology and a sample-grabber sink seems impractical and over-complicated, hence I'm trying to use a combination of a Sink Reader and Sink Writer for this (see the right half of the image on <a href="https://docs.microsoft.com/windows/desktop/medfound/overview-of-the-media-foundation-architecture" rel="nofollow noreferrer">Overview of the Media Foundation Architecture</a>). Unfortunately, <a href="https://docs.microsoft.com/windows/desktop/medfound/audio-video-playback" rel="nofollow noreferrer">Audio/Video Playback</a> does not really explain how to do this. The book <em>Developing Microsoft Media Foundation Applications</em> contains a source-to-sink loop on page 92, but that still does not really help me.</p>
  
  <p>Creating the Source Reader works fine and I'm reading nonzero samples. Writing them to the Sink Writer (which uses the Streaming Audio Renderer) does not give me any errors, but I don't hear anything. I tried multiple things like selecting other media types and explicitly selecting the rendering device (although I only have one, as it indicated), but to no avail. Note that playing audio using a Media Session works fine, though!</p>
  
  <p>I based my code on this question: <a href="https://stackoverflow.com/q/28235464/4454665">Play audio from file to speaker with Media Foundation</a>.</p>
  
  <p>This is my code at this moment:</p>
  
  <pre><code>#include &lt;iostream&gt;
  
  #include &lt;cassert&gt;
  
  #include &lt;mfidl.h&gt;
  #include &lt;mfapi.h&gt;
  #include &lt;mfreadwrite.h&gt;
  #include &lt;Mferror.h&gt;
  #pragma comment(lib, "mf")
  #pragma comment(lib, "mfplat")
  #pragma comment(lib, "mfreadwrite")
  
  #include &lt;winrt/base.h&gt;
  #pragma comment(lib, "windowsapp")
  
  void winHr(const HRESULT result) { winrt::check_hresult(result); }
  
  template&lt;class T&gt;
  struct ComPtr : winrt::com_ptr&lt;T&gt;
  {
      auto operator&amp;() noexcept { return this-&gt;put(); }
  
      operator T*() noexcept
      {
          assert(this-&gt;get());
          return this-&gt;get();
      }
  };
  
  int main() noexcept
  {
      winHr(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
      winHr(MFStartup(MF_VERSION));
  
      {
          ComPtr&lt;IMFSourceReader&gt; reader;
          winHr(MFCreateSourceReaderFromURL(
              LR"(test.wav)",
              nullptr, &amp;reader));
  
          constexpr auto inStreamIndex = MF_SOURCE_READER_FIRST_AUDIO_STREAM;
  
          // Select only the audio stream
          winHr(reader-&gt;SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, false));
          winHr(reader-&gt;SetStreamSelection(inStreamIndex, true));
  
          ComPtr&lt;IMFMediaSink&gt; mediaSink;
          winHr(MFCreateAudioRenderer(nullptr, &amp;mediaSink));
  
          ComPtr&lt;IMFSinkWriter&gt; writer;
  
          {
              ComPtr&lt;IMFStreamSink&gt; streamSink;
              winHr(mediaSink-&gt;GetStreamSinkByIndex(0, &amp;streamSink));
  
              ComPtr&lt;IMFMediaTypeHandler&gt; typeHandler;
              winHr(streamSink-&gt;GetMediaTypeHandler(&amp;typeHandler));
  
              ComPtr&lt;IMFMediaType&gt; inputType;
              winHr(reader-&gt;GetCurrentMediaType(inStreamIndex, &amp;inputType));
  
              ComPtr&lt;IMFMediaType&gt; closestSupportedType;
              const auto result = typeHandler-&gt;IsMediaTypeSupported(inputType, &amp;closestSupportedType);
              if (result == MF_E_INVALIDMEDIATYPE)
              {
                  if (!closestSupportedType)
                  {
                      std::cerr &lt;&lt; "Media type not supported" &lt;&lt; std::endl;
                      winHr(mediaSink-&gt;Shutdown());
                      goto end; //:o
                  }
                  winHr(reader-&gt;SetCurrentMediaType(inStreamIndex, nullptr, closestSupportedType));
                  winHr(typeHandler-&gt;SetCurrentMediaType(closestSupportedType));
                  winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &amp;writer));
                  winHr(writer-&gt;SetInputMediaType(0, closestSupportedType, nullptr));
              }
              else {
                  winHr(result);
                  winHr(reader-&gt;SetCurrentMediaType(inStreamIndex, nullptr, inputType));
                  winHr(typeHandler-&gt;SetCurrentMediaType(inputType));
                  winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &amp;writer));
                  winHr(writer-&gt;SetInputMediaType(0, inputType, nullptr));
              }
          }
  
          winHr(writer-&gt;BeginWriting());
          while (true)
          {
              ComPtr&lt;IMFSample&gt; sample;
              DWORD streamFlags;
              MFTIME timestamp;
              winHr(reader-&gt;ReadSample(inStreamIndex, 0, nullptr, &amp;streamFlags, &amp;timestamp, &amp;sample));
  
              if (streamFlags &amp; MF_SOURCE_READERF_ENDOFSTREAM)
              {
                  winHr(writer-&gt;NotifyEndOfSegment(0));
                  break;
              }
              if (streamFlags &amp; MF_SOURCE_READERF_STREAMTICK)
                  winHr(writer-&gt;SendStreamTick(0, timestamp));
  
              if (!sample) continue;
  
              winHr(sample-&gt;SetSampleTime(timestamp));
              winHr(writer-&gt;WriteSample(0, sample));
          }
          winHr(writer-&gt;Flush(0));
  
          std::cout &lt;&lt; "(Press enter to stop)" &lt;&lt; std::endl;
          std::cin.get();
  
          winHr(writer-&gt;Finalize());
          writer.attach(nullptr);
          winHr(mediaSink-&gt;Shutdown());
      }
  
  end:
      winHr(MFShutdown());
      CoUninitialize();
  }
  </code></pre>
  
  <p><s>Just to be clear: when I run this it prints <code>(Press enter to stop)</code> and I can hear from the noise (read: distortions from electronic signals) from my headphones that I can deduce that for a short moment an audio port was opened and then closed, but no actual audio is played. How can I get this to work?</s></p>
  
  <p><s>Edit 1: I just fixed that if <code>result != MF_E_INVALIDMEDIATYPE</code> I didn't set the media type, but now I often (but not always, for some reason) get <code>MF_E_TOPO_CODEC_NOT_FOUND</code> at the line <code>winHr(writer-&gt;SetInputMediaType(0, inputType, nullptr));</code>. Why would this be? (Still no audio is played in any case.)</s></p>
  
  <p>Edit 2: Apparently it matters when I create the writer, so now I do that only at the last moment, but now I get the "Media type not supported" error. Maybe I need to manually pick some media type but I will look in to this later -- unless someone knows the answer.</p>