locked
MP3 Memory Stream decoding RRS feed

  • Question

  • Hi. I've been playing with DirectShow and Media Format APIs. I'm trying to write a simple MP3 Player that decodes
    mp3 from memory stream. I've looked over Mr. Geraint Davies website and find it quite helpful. There are two useful examples there
    mp3 parser and wma parser. The wma parser is combined with Async Reader.

    OK, I've implemented  class that derives from IStream (own memory reader implementation)
    and I'm using it with Media Format IWMReader to read the input from the memory and play it.

    I would like now to use DirectShow instead.  So, I'm wondering how could I combine my implementation
    of IStream (which is async) together with examples of wma parser or mp3 parser in order to play mp3 via DirectShow?

    If I use my IStream with Mr. Davies' wma parser example; will it parse mp3? I guess I would have to change few things in the parser.
    The mp3 parser is given by Mr. Davies does not provide connection pin for IStream; what needs to be done to make it run with Async IStream reader?
    I think I read somewhere that all I need to do is put this mp3 parser into a graph and then put IStream Reader into the graph and things are all set; though I dont understand
    how would this Istream async reader communicate with Mr. Davies' mp3 parser.
    Any suggestions appreciated ;-)
    Thanx for your attention.
    Yaerek
    Tuesday, July 28, 2009 10:46 AM

Answers

All replies

    • Marked as answer by Yaerek Monday, August 3, 2009 2:19 PM
    Tuesday, July 28, 2009 1:25 PM
  • Hi. I did search, found what I was looking for; the mp3 player that reads the stream from memory buffer is running.
    I do have a few questions though.
    We have a class that receives the mp3 stream; this stream is buffered and I'm using the example CAsync stream (modified to deal with random Seek) to read the mp3 and buffer to the renderer.

    Here's the deal; when I play the audio for the first time with the following code, it works and plays. After playing the audio, I release all DirectShow object and the initialize them again; then I open another audio stream and in this moment, I have the follwing error returned:

    -2147220968   0x80040218    The version of ACL format in the stream is not supported by this implementation of IAccessControl

    Here's the code:

    class StreamPlayerDS
    {

    PlaybackState    m_state;


        HWND            m_hwndEvent;    // Window to receive events
        UINT            m_EventMsg;        // Windows message for graph events

        // Audio
        long            m_lVolume;        // Current volume (unless muted)
        BOOL            m_bMute;        // Volume muted?       
        bool            m_bFirstPlay;

        IFilterGraph    *m_pFilterGraph;
        IGraphBuilder    *m_pGraphBuilder;
        IMediaControl    *m_pControl;
        IMediaEventEx    *m_pEvent;
        IBasicAudio        *m_pAudio;
        IBaseFilter        *m_pSource;
        IMediaSeeking    *m_pSeek;

        // NOWE JM 20090729
         CMediaType m_mt;
         CMemStream *m_MemStreamPtr;
         CMemReader *m_MemReaderPtr;
         static CAsyncStream *m_AsyncStreamPtr;
    };


    HRESULT StreamPlayerDS::OpenSTMstream()
    {
        HRESULT hr = NULL;

        this->InitializeGraph();
        m_AsyncStreamPtr=stm::net::ptrCAsyncStream();
        m_MemStreamPtr = reinterpret_cast<CMemStream*>(m_AsyncStreamPtr);
       
        this->m_MemReaderPtr = new CMemReader(m_MemStreamPtr, &m_mt, &hr);
         
       
        hr = this->m_pFilterGraph->AddFilter(this->m_MemReaderPtr, NULL);
       
        if(FAILED(hr))
        {
            this->_pLogger->error(this->_pLogger->name() + " FilterGraph Failed to Render");   
            return hr;
        }

        hr = this->m_pGraphBuilder->Render(this->m_MemReaderPtr->GetPin(0)); // THIS IS WHERE IT FAILS ON 2nd Play AND RETURNS -2147220968
       
        if(FAILED(hr))
        {
            return hr;
        }
        m_state = STATE_STOPPED;

        return hr;
    }


    void StreamPlayerDS::TearDownGraph()
    {
        // Stop sending event messages
        if (m_pEvent)
        {
            m_pEvent->SetNotifyWindow((OAHWND)NULL, NULL, NULL);
        }

        SAFE_RELEASE(m_pFilterGraph);
        SAFE_RELEASE(m_pGraphBuilder);
        SAFE_RELEASE(m_pControl);
        SAFE_RELEASE(m_pEvent);
        SAFE_RELEASE(m_pSeek);
        SAFE_RELEASE(m_pAudio);
        SAFE_RELEASE(m_pSource);

        m_state = STATE_CLOSED;

    }

    HRESULT StreamPlayerDS::InitializeGraph()
    {
        HRESULT hr = S_OK;

        TearDownGraph();

        // Create the Filter Graph Manager.
        m_mt.majortype = MEDIATYPE_Stream;
        m_mt.subtype = MEDIASUBTYPE_MPEG1Audio;

         
         if (SUCCEEDED(hr))
        {
            try
            {
                hr = CoCreateInstance(
                                        CLSID_FilterGraph,
                                        NULL,
                                        CLSCTX_INPROC,
                                        IID_IGraphBuilder,
                                        (void**)&m_pFilterGraph
                                    );
            } catch(std::exception& e)
                {
                        this->_pLogger->error(this->_pLogger->name() + " CoCreateInstance m_pFilterGraph Exception");   
                }
        }

        if (SUCCEEDED(hr))
        {
            hr = m_pFilterGraph->QueryInterface(IID_IGraphBuilder, (void**)&m_pGraphBuilder);
        }

        // Query for graph interfaces.
        if (SUCCEEDED(hr))
        {
            hr = m_pGraphBuilder->QueryInterface(IID_IMediaControl, (void**)&m_pControl);
        }

        if (SUCCEEDED(hr))
        {
            hr = m_pGraphBuilder->QueryInterface(IID_IMediaEventEx, (void**)&m_pEvent);
        }

        if (SUCCEEDED(hr))
        {
            hr = m_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void**)&m_pSeek);
        }

        if (SUCCEEDED(hr))
        {
            hr = m_pGraphBuilder->QueryInterface(IID_IBasicAudio, (void**)&m_pAudio);
        }


        // Set up event notification.
        if (SUCCEEDED(hr))
        {
       
            hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwndEvent, m_EventMsg, NULL);
        }
       
        return hr;
    }

    My guess is, I am doing something too fast.

    The 2nd question is; is there a way to tell DirectShow what mp3 parser to use? To B more specific, to make it use default MS Windows parser. The reason I'm asking is, I've bumped into XP installs that had some additional mp3 codecs installed and the code above failed even on the first play. The problem doesnt appear when I'm using it on XP without any additional codec packs. I'm wondering what's the CLSID (if there is one) of the standard Windows mp3 codec.

    Best Regards and Thanx
    Yaerek
    Monday, August 3, 2009 2:19 PM
  • Here's the deal; when I play the audio for the first time with the following code, it works and plays. After playing the audio, I release all DirectShow object and the initialize them again; then I open another audio stream and in this moment, I have the follwing error returned:

    -2147220968   0x80040218    The version of ACL format in the stream is not supported by this implementation of IAccessControl

    FYI:

    HRESULT = 0x80040218 :
    VFW_E_CANNOT_RENDER
    Error Message:
    No combination of filters could be found to render the stream.




    http://alax.info/blog/tag/directshow
    Monday, August 3, 2009 4:42 PM
  • Hi. Thanx for the reply. Yes, I know this error means "No Combination of filters could be found to render the stream." What exactly does it mean? Problem with the stream or problem with the renderer (not initialized, yet)

    Here's the problem. I have two mp3s to play. When I play them each separately (via buffering in memory, stopping after each play), it works.
    When I play one, wait for it to end (event indicating the end is invoked) and then reinitialize the player (by running InitializeGraph (which runs TearDownGraph)), then try to open the new stream, I get the error:

        this->m_MemReaderPtr = new CMemReader(m_MemStreamPtr, &m_mt, &hr);
         
       
        hr = this->m_pFilterGraph->AddFilter(this->m_MemReaderPtr, NULL);
       
        if(FAILED(hr))
        {
            this->_pLogger->error(this->_pLogger->name() + " FilterGraph Failed to Render");   
            return hr;
        }

        hr = this->m_pGraphBuilder->Render(this->m_MemReaderPtr->GetPin(0)); // THIS IS WHERE IT FAILS ON 2nd Play AND RETURNS -2147220968

    If I play any of the two mp3s separately, then the Render function works. If try to play them in sequence, the second one fails. I just double checked the contents of the buffer to be read in the 2nd attempt, they are correct (contents can be the same as previous). I'm wondering if perhaps something is wrong with the CMemReader (which derives from CAsyncReader) or if the graph is not completely initialized (after running deinit and init on it).
    Any suggestions?

    Yaerek

    Monday, August 3, 2009 9:56 PM
  • I doubt it is the CMemReader as that is recreated each time.  Are you creating a clean new graph builder each time?

    Something is going wrong with the connection between the source and the parser, which to me indicates that it's possible that the old graph didn't get completely cleaned up.

    Note that you may also get faster graph builds if you modify the CMemReader to set the subtype to MEDIASUBTYPE_MPEG1Audio on it's output pin when it see's .mp3 in the filename.

    Parsers are tried in order of merit, but if you wish to insert an explicit one then you can do that too by inserting it into the graph before calling Render.

    www.chrisnet.net
    Monday, August 3, 2009 10:20 PM
  • The problem was in CMReader; incorrect initialization. Got the player running. Thanks a lot!!!
    Yaerek
    Thursday, August 6, 2009 12:27 PM