Search code examples
c++audiocodecms-media-foundation

How to create sink writer from buffer?


my implementation of encoder gets as input .wav file bytes and gives as output .mp3 bytes written in out file.

Like this

void co_AudioEncoderMF::start_encoding()
{
...
    IMFSinkWriter * sink_writer = nullptr;
    
    //Create sink writer
    HRESULT hr = MFCreateSinkWriterFromURL(
        L"D:\\buffer\\del\\out\\test_out.mp3",
        NULL,
        NULL,
        &sink_writer
    );
...

    // Add the stream
    hr = sink_writer->AddStream(
        ouput_media_type,
        &dwWriterStreamIndex
    );

    // Set input media type
    hr = sink_writer->SetInputMediaType(
        dwWriterStreamIndex,
        input_type,
        NULL
    );

    // Tell the sink writer to accept data
    hr = sink_writer->BeginWriting();

...

    hr = sink_writer->WriteSample(
            dwWriterStreamIndex,
            pSample
        );
...
}

So, I create a IMFSinkWriter then add it to the straem + set mediatype and then I can write encoded bytes to out file L"D:\\buffer\\del\\out\\test_out.mp3".

Problem here is that I don't want to write encoded data to out file I need to get it as a buffer.

I see there is another possible method

STDAPI MFCreateSinkWriterFromMediaSink(
    _In_ IMFMediaSink *pMediaSink,
    _In_opt_ IMFAttributes *pAttributes,
    _Out_ IMFSinkWriter **ppSinkWriter );

So, as far as I understood it takes pointer on IMFMediaSink and out on IMFSinkWriter from this point I got that I can make an implementation of IMFMediaSink interface which would create IMFSinkWriter and set it up when MFCreateSinkWriterFromMediaSink method would be called. (but I am not sure about that this is a right way to write encoded data in buffer).

So, I made an implementation of IMFMediaSink

#include "co_MediaSink.h"
#include <stdio.h>
#include "co_SinkWriter.h"

co_MediaSink::co_MediaSink()
{
}

co_MediaSink::~co_MediaSink()
{
}

HRESULT co_MediaSink::QueryInterface(REFIID riid, void ** ppvObject)
{
    printf("HERE");
    *ppvObject = new co_SinkWriter();
    return S_OK;
}

ULONG co_MediaSink::AddRef(void)
{
    printf("HERE");
    return 1;
}

ULONG co_MediaSink::Release(void)
{
    printf("HERE");
    return 1;
}

HRESULT co_MediaSink::GetCharacteristics(DWORD * pdwCharacteristics)
{
    printf("HERE");
    return S_OK;
}

HRESULT co_MediaSink::AddStreamSink(DWORD dwStreamSinkIdentifier, IMFMediaType * pMediaType, IMFStreamSink ** ppStreamSink)
{
    printf("HERE");
    return S_OK;
}

HRESULT co_MediaSink::RemoveStreamSink(DWORD dwStreamSinkIdentifier)
{
    printf("HERE");
    return S_OK;
}

HRESULT co_MediaSink::GetStreamSinkCount(DWORD * pcStreamSinkCount)
{
    printf("HERE");
    return S_OK;
}

HRESULT co_MediaSink::GetStreamSinkByIndex(DWORD dwIndex, IMFStreamSink ** ppStreamSink)
{
    printf("HERE");
    return S_OK;
}

HRESULT co_MediaSink::GetStreamSinkById(DWORD dwStreamSinkIdentifier, IMFStreamSink ** ppStreamSink)
{
    printf("HERE");
    return S_OK;
}

HRESULT co_MediaSink::SetPresentationClock(IMFPresentationClock * pPresentationClock)
{
    printf("HERE");
    return S_OK;
}

HRESULT co_MediaSink::GetPresentationClock(IMFPresentationClock ** ppPresentationClock)
{
    printf("HERE");
    return S_OK;
}

HRESULT co_MediaSink::Shutdown(void)
{
    printf("HERE");
    return S_OK;
}

Usage ::

    IMFSinkWriter * sink_writer = nullptr;
    co_MediaSink media_sink;
    HRESULT hr = MFCreateSinkWriterFromMediaSink(&media_sink, nullptr, &sink_writer);

So, here with a debug I see that after this MFCreateSinkWriterFromMediaSink method was called, next debug break point a get in co_MediaSink::QueryInterface(REFIID riid, void ** ppvObject) method where I need to set (I think) my IMFSinkWriter implementation to this out pointer ppvObject like this *ppvObject = new co_SinkWriter();, but then I get an error

Exception thrown at 0x00007FF95B4F8317 (mfreadwrite.dll) in co_Convert.exe: 0xC0000005: Access violation reading location 0x0000000000000000.

So, there are actually two questions, first is a right way was chosen in order to write in buffer and second - if yes, what is a problem with Access violation?


Solution

    1. Have byte stream implemented to accept the data (you are already familiar with IMFByteStream concept from your development on the source end)
    2. Have MFCreateMP3MediaSink (this is the key API for the mentioned task) create an implementation of IMFMediaSink for MP3 stream
    3. MFCreateSinkWriterFromMediaSink will create an IMFsinkWriter on top of this media sink, and you are already aware how to work with sink writers

    This way you could write to sink writer and accept parts of MP3 byte stream on the other end.