Search code examples
directshowmpeg2-ts

Microsoft's MPEG-2 demuxer filter - can I change an elementary stream pin's PID while the graph is running?


I'm working with multi-program UDP MPEG-2 TS streams that, -unfortunately- dynamically re-map their elementary stream PIDs at random intervals. The stream is being demuxed using Microsoft's MPEG-2 demultiplexer filter.

I'm using the PSI-Parser filter (an example filter included in the DirectShow base classes) in order to react to the PAT/PMT changes.

The code is properly reacting to the change, yet I am experiencing some odd crashes (heap memory corruption) right after I remap the Demuxer pins to their new ID's. (The re-mapping is performed inside the thread that is processing graph events, while the EC_PROGRAMCHANGED message is being processed).

The crash could be due to faulty code in my part, yet I have not found any reference that tells me if changing the pin PID mapping is safe while the graph is running.

Can anyone provide some info if this is operation is safe, and if it is not, what could I do to minimize capture disruption?


Solution

  • I managed to find the source code for a Windows CE version of the demuxer filter. Inspecting it, indeed, it seems that it is safe to remap a pin while the filter is running.

    I also managed to find the source of my problems with the PSI-Parser filter.

    When a new transport stream is detected, or the PAT version changes, the PAT is flushed, (all programs are removed, the table is re-parsed and repopulated). There is a subtle bug within the CPATProcessor::flush() method.

    //
    // flush
    //
    // flush an array of struct: m_mpeg2_program[];
    // and unmap all PMT_PIDs pids, except one: PAT
    BOOL CPATProcessor::flush()
    {
        BOOL bResult = TRUE;
        bResult = m_pPrograms->free_programs();   // CPrograms::free_programs() call
        if(bResult == FALSE)
            return bResult;
        bResult = UnmapPmtPid();
        return bResult;
    }// flush
    

    Here's the CPrograms::free_programs() implementation.

    _inline BOOL free_programs()
        {
            for(int i= 0; i<m_ProgramCount; i++){
                if(!HeapFree(GetProcessHeap(), 0, (LPVOID) m_programs[i] ))
                    return FALSE;
            }
            return TRUE;
        }
    

    The problem here is that the m_ProgramCount member is never cleared. So, -apart from reporting the wrong number of programs in the table after a flush (since it is updated incrementally for each program found in the table)-, the next time the table is flushed, it will try to release memory that was already released.

    Here's my updated version that fixes the heap corruption errors:

    _inline BOOL free_programs()
        {
            for(int i= 0; i<m_ProgramCount; i++){
                if(!HeapFree(GetProcessHeap(), 0, (LPVOID) m_programs[i] ))
                    return FALSE;
            }
            m_ProgramCount = 0;  // This was missing,  next call will try to free memory twice
            return TRUE;
        }