locked
Audio Pass-Through IMFTransform

    Question

  • I created 2 MFTs; one for a graphic EQ and another for a Fast Fourier Transform. The equalizer MFT runs well, but I cannot get the FFT MFT working. Basically, it just needs to be a "pass-through" MFT. I set it up almost identical to the EQ MFT, but cannot get the ProcessOutput to output anything but exceptions.

    How do I set this up as a "pass-through" MFT? I can't seem to get it right.

    Now, for the ProcessOutput code (Yes, I know the code is a mess):

      HRESULT FFT::ProcessOutput(
        DWORD                   dwFlags,
        DWORD                   cOutputBufferCount,
        MFT_OUTPUT_DATA_BUFFER  *pOutputSamples, // one per stream
        DWORD                   *pdwStatus
        )
      {
        HRESULT hr = S_OK;
    
        AutoLock lock(m_critSec);
    
        try
        {
          if (dwFlags != 0)
          {
            throw ref new InvalidArgumentException();
          }
    
          if (pOutputSamples == nullptr || pdwStatus == nullptr)
          {
            throw ref new InvalidArgumentException();
          }
    
          if (cOutputBufferCount != 1)
          {
            throw ref new InvalidArgumentException();
          }
    
          if (pOutputSamples[0].pSample == nullptr)
          {
            throw ref new InvalidArgumentException();
          }
    
          ComPtr<IMFMediaBuffer> spInput;
          ComPtr<IMFMediaBuffer> spOutput;
    
          if (m_pSample == nullptr)
          {
            return MF_E_TRANSFORM_NEED_MORE_INPUT;
          }
    
          BeginStreaming();
    
          ThrowIfError(m_pSample->ConvertToContiguousBuffer(&spInput));
    
          ThrowIfError(pOutputSamples[0].pSample->ConvertToContiguousBuffer(&spOutput));
          ThrowIfError(spInput->GetCurrentLength(&_currentLength));
    
          OnProcessOutput(spInput.Get(), spOutput.Get());
    
          pOutputSamples[0].dwStatus = 0;
          *pdwStatus = 0;
    
          LONGLONG hnsDuration = 0;
          LONGLONG hnsTime = 0;
    
          if (SUCCEEDED(m_pSample->GetSampleDuration(&hnsDuration)))
          {
            ThrowIfError(pOutputSamples[0].pSample->SetSampleDuration(hnsDuration));
          }
    
          if (SUCCEEDED(m_pSample->GetSampleTime(&hnsTime)))
          {
            ThrowIfError(pOutputSamples[0].pSample->SetSampleTime(hnsTime));
          }
    
          ThrowIfError(spOutput->SetCurrentLength(_currentLength));
        }
        catch (Exception ^exc)
        {
          hr = exc->HResult;
        }
    
        m_pSample.Reset(); // Release our input sample.
    
        return hr;
      };
    
      void FFT::OnProcessOutput(IMFMediaBuffer *pIn, IMFMediaBuffer *pOut)
      {
        BYTE *pSrc = NULL; // Source buffer.
        BYTE *pDest = NULL;
        DWORD length = NULL;
    
        ThrowIfError(pIn->Lock(&pSrc, &_maxLength, &length));
        ThrowIfError(pOut->Lock(&pDest, NULL, NULL));
    
        ThrowIfError(memcpy_s(pDest, length, pSrc, length));
    
        if (_isEnabled)
        {
          double* inputFrames = reinterpret_cast<double*>(*pSrc);
    
          UINT32 frameCount = _currentLength / _bytesPerSample / _numChannels;
    
          bool inputSilent = true;
    
          for (auto i = 0U; i < frameCount * _numChannels; i++)
          {
            if (inputFrames[i] != 0)
            {
              inputSilent = false;
              break;
            }
          }
    
          if (inputSilent)
          {
            if (_lastInputWasSilent)
            {
              return;
            }
            else
            {
              _lastInputWasSilent = true;
            }
          }
          else
          {
            _lastInputWasSilent = false;
          }
    
          ThrowIfError(ProcessFrames(inputFrames, 0, frameCount));
        }
    
        ThrowIfError(pIn->Unlock());
        ThrowIfError(pOut->Unlock());
    
        return;
      }
    

    This is what I'm trying to do: It is supposed to skip over the FFT processing if _isEnabled == false. It is always supposed to copy the input data (m_pSample) to the output buffer for playback. I have tried many different methods of copying the IMFMediaBuffer from pIn to pOut and I thought I had it working with memcpy_s, but I ended up with a corrupted heap.


    Sunday, August 03, 2014 7:12 AM

Answers

  • In my effort to correct the issue, I made it worse. Also, the frame calculation was way off.

    To copy an audio IMFMediaBuffer, frame by frame, all you need to do is (if the block align is 1):

      HRESULT FFT::CopyBuffer(BYTE *pSrc, BYTE *pDest)
      {
        HRESULT hr = S_OK;
    
        for (unsigned f = 0; f < _currentLength; f++)
        {
          pDest[f] = pSrc[f];
        }
    
        return hr;
      }
    

    I still have to handle cases where the block align is > 1, but it is working (for now) and I am receiving data from the FFT.

    Also, spInput->GetCurrentLength(&_currentLength) should be in ProcessInput; not ProcessOutput. GetOutputStatus() is called after ProcessInput, but before ProcessOutput. It requires the correct buffer length before ProcessOutput is called.

    Monday, August 04, 2014 3:00 AM

All replies

  • In my effort to correct the issue, I made it worse. Also, the frame calculation was way off.

    To copy an audio IMFMediaBuffer, frame by frame, all you need to do is (if the block align is 1):

      HRESULT FFT::CopyBuffer(BYTE *pSrc, BYTE *pDest)
      {
        HRESULT hr = S_OK;
    
        for (unsigned f = 0; f < _currentLength; f++)
        {
          pDest[f] = pSrc[f];
        }
    
        return hr;
      }
    

    I still have to handle cases where the block align is > 1, but it is working (for now) and I am receiving data from the FFT.

    Also, spInput->GetCurrentLength(&_currentLength) should be in ProcessInput; not ProcessOutput. GetOutputStatus() is called after ProcessInput, but before ProcessOutput. It requires the correct buffer length before ProcessOutput is called.

    Monday, August 04, 2014 3:00 AM
  • Good to know you have solve the issue, and your solution should help other communities who has the same problem.

    --James


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    Tuesday, August 05, 2014 6:11 AM
    Moderator