Handle Discontinuity and PAT/PMT change in Android Multicast Playback

In previous post it briefly introduces how to implement multicast playback in Android.
In real world, multicast may get discontinuities or PAT/PMT changes, which will cause the playback intermittent or failure if it’s not correctly handled.

In this post, it will introduces how to handle discontinuity and PAT/PMT changes.


In NuPlayer::Decoder::fetchInputData(), it handles three types of DISCONTINUITY:


Originally, they are used to handle adaptive cases in HLS, e.g. switch from low-resolution stream to high-resolution stream. They’re perfect for multicast stream’s discontinuity and PAT/PMT change as well.

Handle Discontinuity

In ATSParser::Stream::parse(), it already checks the ContinuityCounter in TS streams. We can just queue a time discontinuity unit, then NuPlayer::Decoder will handle the case, reset the playback and re-sync A/V.

if (mExpectedContinuityCounter >= 0
    && (unsigned)mExpectedContinuityCounter != continuity_counter) {
  ALOGI("discontinuity on stream pid 0x%04x", mElementaryPID);
  signalDiscontinuity(DISCONTINUITY_TIME, NULL);

In ATSParser::Program::convertPTSToTimestamp(), there is a special flag for calculating time stamp from PTS.

if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) {
  if (!mFirstPTSValid) {
    mFirstPTSValid = true;
    mFirstPTS = PTS;
    PTS = 0;
  } else if (PTS < mFirstPTS) {
    PTS = 0;
  } else {
    PTS -= mFirstPTS;

If TS_TIMESTAMPS_ARE_ABSOLUTE is not set, it uses the first valid PTS as the base time stamp, and assumes the PTS is increasing all the time. This is not the case for multicast live streaming. So we need to set this flag to let it calculate time stamp by absolute value of PTS.

  // In MulticastSession::onConnect()

With the changes, multicast discontinuity is correctly handled and it works fine when multicast stream is looped.

Handle PAT/PMT change

In ATSParser implementation, it just parses PAT/PMT and store the program table and the A/V streams. If PAT/PMT is changed, it just update the program table and streams, and requires the “user” of ATSParser to handle the change, which is quite complicated.
In the other hand, when PAT/PMT is changed in multicast, we know the stream is changed and thus can just throw away the previous program table. So why not delete the previous ATSParser and create a new one to handle the new stream? It makes things much easier.
The changes:

  • ATSParser
    1. Add an observer that should receive the event of PAT/PMT change
    2. Save PAT/PMT crc32 and detect the change; If it’s changed, call onPatPmtChange() and notify the observer
  • MulticastSession
    1. Receive parser’s event and call onPatPmtChanged() for PAT/PMT changed event
    2. Reset ATSParser by delete and create new one
    3. Queue FORMAT change DISCONTINUITY for both audio and video
    4. In dequeueAccessUnit(), check the discontinuity queue and return DISCONTINUITY for audio/video stream.

With the changes, ATSParser is reset when PAT/PMT changes, as if it plays a new stream, which is exactly the expected behavior.