Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29f0c378 authored by John Grossman's avatar John Grossman
Browse files

LibAAH_RTP: Properly handle EOS conditions.

EOS was being treated as a flush operation which was causing problems.
In particular, the transmitter was delcaring that playback was
complete early (by the clock lead time of the system, which was 1
second in this case).  Also, the receiver was treating the EOS message
just like the flush message, immediately destroying the substreams
associated with the program without letting them play out first.

Change the transmitter to send the EOS message like it always does,
but have it wait until the media time of the last sample has arrived
before reporting playback complete to the app level of things.

On the receiver side of things, don't treat the EOS message like the
flush message.  Instead, have the EOS message simply put the substream
into EOS mode, allowing it to signal EOS to its decoder and shut off
the isAboutToUnderflow hack.

Change-Id: Ibe3ac01044373f83edb7a5f4b70478bd78c16d11
parent 6373153c
Loading
Loading
Loading
Loading
+30 −28
Original line number Diff line number Diff line
@@ -192,6 +192,7 @@ class AAH_RXPlayer : public MediaPlayerInterface {
        void processTSTransform(const LinearTransform& trans);

        bool     isAboutToUnderflow();
        void     signalEOS();
        uint32_t getSSRC()      const { return ssrc_; }
        uint8_t  getProgramID() const { return (ssrc_ >> 5) & 0x1F; }
        status_t getStatus() const { return status_; }
@@ -245,6 +246,7 @@ class AAH_RXPlayer : public MediaPlayerInterface {

        sp<AAH_DecoderPump>     decoder_;
        Timeout                 inactivity_timeout_;
        bool                    eos_reached_;

        static const int64_t    kAboutToUnderflowThreshold;
        static const int        kInactivityTimeoutMsec;
+16 −5
Original line number Diff line number Diff line
@@ -682,6 +682,7 @@ void AAH_RXPlayer::processCommandPacket(PacketBuffer* pb) {

    bool do_cleanup_pass = false;
    uint16_t command_id = U16_AT(data + offset);
    uint8_t  program_id = (U32_AT(data + 8) >> 5) & 0x1F;
    offset += 2;

    switch (command_id) {
@@ -692,13 +693,23 @@ void AAH_RXPlayer::processCommandPacket(PacketBuffer* pb) {
            break;

        case TRTPControlPacket::kCommandEOS:
            // TODO need to differentiate between flush and EOS.  Substreams
            // which have hit EOS need a chance to drain before being destroyed.
            // Flag the substreams which are a member of this program as having
            // hit EOS.  Once in the EOS state, it is not possible to get out.
            // It is possible to pause and unpause, but the only way out would
            // be to seek, or to stop completely.  Both of these operations
            // would involve a flush, which would destroy and (possibly)
            // recreate a new the substream, getting rid of the EOS flag in the
            // process.
            for (size_t i = 0; i < substreams_.size(); ++i) {
                const sp<Substream>& stream = substreams_.valueAt(i);
                if (stream->getProgramID() == program_id) {
                    stream->signalEOS();
                }
            }
            break;

        case TRTPControlPacket::kCommandFlush: {
            uint8_t program_id = (U32_AT(data + 8) >> 5) & 0x1F;
            LOGI("*** %s flushing program_id=%d",
                 __PRETTY_FUNCTION__, program_id);
            LOGI("Flushing program_id=%d", program_id);

            // Flag any programs with the given program ID for cleanup.
            for (size_t i = 0; i < substreams_.size(); ++i) {
+20 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) {
    buffer_in_progress_ = NULL;
    status_ = OK;
    codec_mime_type_ = "";
    eos_reached_ = false;

    decoder_ = new AAH_DecoderPump(omx);
    if (decoder_ == NULL) {
@@ -637,11 +638,30 @@ void AAH_RXPlayer::Substream::processTSTransform(const LinearTransform& trans) {
    }
}

void AAH_RXPlayer::Substream::signalEOS() {
    if (!eos_reached_) {
        LOGI("Substream with SSRC 0x%08x now at EOS", ssrc_);
        eos_reached_ = true;
    }

    // TODO: Be sure to signal EOS to our decoder so that it can flush out any
    // reordered samples.  Not supporting video right now, so its not super
    // important.
}

bool AAH_RXPlayer::Substream::isAboutToUnderflow() {
    // If we have no decoder, we cannot be about to underflow.
    if (decoder_ == NULL) {
        return false;
    }

    // If we have hit EOS, we will not be receiveing any new samples, so the
    // about-to-underflow hack/heuristic is no longer valid.  We should just
    // return false to be safe.
    if (eos_reached_) {
        return false;
    }

    return decoder_->isAboutToUnderflow(kAboutToUnderflowThreshold);
}

+91 −17
Original line number Diff line number Diff line
@@ -53,9 +53,11 @@ static const int64_t kAAHBufferTimeUs = 1000000LL;
const int64_t AAH_TXPlayer::kAAHRetryKeepAroundTimeNs =
    kAAHBufferTimeUs * 1100;

const int AAH_TXPlayer::kEOSResendTimeoutMsec = 100;
const int AAH_TXPlayer::kPauseTSUpdateResendTimeoutMsec = 250;
const int32_t AAH_TXPlayer::kInvokeGetCNCPort = 0xB33977;


sp<MediaPlayerBase> createAAH_TXPlayer() {
    sp<MediaPlayerBase> ret = new AAH_TXPlayer();
    return ret;
@@ -523,8 +525,9 @@ status_t AAH_TXPlayer::play_l() {

status_t AAH_TXPlayer::stop() {
    status_t ret = pause();
    mEOSResendTimeout.setTimeout(-1);
    mPauseTSUpdateResendTimeout.setTimeout(-1);
    sendEOS_l();
    sendFlush_l();
    return ret;
}

@@ -592,7 +595,9 @@ void AAH_TXPlayer::updateClockTransform_l(bool pause) {
    sendTSUpdateNop_l();

    // if we are paused, schedule a periodic resend of the TS update, JiC the
    // receiveing client misses it.
    // receiveing client misses it.  Don't bother setting the timer if we have
    // hit EOS; the EOS message will carry the update for us and serve the same
    // purpose as the pause updates.
    if (mPlayRateIsPaused) {
        mPauseTSUpdateResendTimeout.setTimeout(kPauseTSUpdateResendTimeoutMsec);
    } else {
@@ -604,12 +609,32 @@ void AAH_TXPlayer::sendEOS_l() {
    if (mAAH_TXGroup != NULL) {
        sp<TRTPControlPacket> packet = new TRTPControlPacket();
        if (packet != NULL) {
            if (mCurrentClockTransformValid) {
                packet->setClockTransform(mCurrentClockTransform);
            }
            packet->setCommandID(TRTPControlPacket::kCommandEOS);
            sendPacket_l(packet);
        } else {
            LOGD("Failed to allocate TRTP packet at %s:%d", __FILE__, __LINE__);
        }
    }

    // While we are waiting to reach the end of the actual presentation and have
    // the app clean us up, periodically resend the EOS message, just it case it
    // was dropped.
    mEOSResendTimeout.setTimeout(kEOSResendTimeoutMsec);
}

void AAH_TXPlayer::sendFlush_l() {
    if (mAAH_TXGroup != NULL) {
        sp<TRTPControlPacket> packet = new TRTPControlPacket();
        if (packet != NULL) {
            packet->setCommandID(TRTPControlPacket::kCommandFlush);
            sendPacket_l(packet);
        } else {
            LOGD("Failed to allocate TRTP packet at %s:%d", __FILE__, __LINE__);
        }
    }
}

void AAH_TXPlayer::sendTSUpdateNop_l() {
@@ -641,21 +666,13 @@ status_t AAH_TXPlayer::seekTo(int msec) {

status_t AAH_TXPlayer::seekTo_l(int64_t timeUs) {
    mIsSeeking = true;
    mEOSResendTimeout.setTimeout(-1);
    mSeekTimeUs = timeUs;

    mCurrentClockTransformValid = false;
    mLastQueuedMediaTimePTSValid = false;

    // send a flush command packet
    if (mAAH_TXGroup != NULL) {
        sp<TRTPControlPacket> packet = new TRTPControlPacket();
        if (packet != NULL) {
            packet->setCommandID(TRTPControlPacket::kCommandFlush);
            sendPacket_l(packet);
        } else {
            LOGD("Failed to allocate TRTP packet at %s:%d", __FILE__, __LINE__);
        }
    }
    sendFlush_l();

    return OK;
}
@@ -748,7 +765,7 @@ void AAH_TXPlayer::reset_l() {

    cancelPlayerEvents();

    sendEOS_l();
    sendFlush_l();

    mCachedSource.clear();

@@ -769,6 +786,7 @@ void AAH_TXPlayer::reset_l() {
    mIsSeeking = false;
    mSeekTimeUs = 0;

    mEOSResendTimeout.setTimeout(-1);
    mPauseTSUpdateResendTimeout.setTimeout(-1);

    mUri.setTo("");
@@ -1161,6 +1179,39 @@ void AAH_TXPlayer::onPumpAudio() {
            }
        }

        // If we have hit EOS, then we will have an EOS resend timeout set.
        int msecTillEOSResend = mEOSResendTimeout.msecTillTimeout();
        if (msecTillEOSResend >= 0) {
            // Resend the EOS message if its time.
            if (!msecTillEOSResend) {
                sendEOS_l();
            }

            // Declare playback complete to the app level if we have passed the
            // PTS of the last sample queued, then cancel the EOS resend timer.
            if (mediaTimeNowValid &&
                mLastQueuedMediaTimePTSValid &&
              ((mLastQueuedMediaTimePTS - mediaTimeNow) <= 0)) {
                LOGI("Sending playback complete");
                pause_l(false);
                notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
                mEOSResendTimeout.setTimeout(-1);

                // Return directly from here to avoid rescheduling ourselves.
                return;
            }

            // Once we have hit EOS, we are done until we seek or are reset.
            break;
        }

        // Stop if we have reached our buffer threshold.
        if (mediaTimeNowValid &&
            mLastQueuedMediaTimePTSValid &&
           (mediaTimeNow + kAAHBufferTimeUs - mLastQueuedMediaTimePTS) <= 0) {
            break;
        }

        // Stop if we have reached our buffer threshold.
        if (mediaTimeNowValid &&
            mLastQueuedMediaTimePTSValid &&
@@ -1177,11 +1228,34 @@ void AAH_TXPlayer::onPumpAudio() {
        status_t err = mAudioSource->read(&mediaBuffer, &options);
        if (err != NO_ERROR) {
            if (err == ERROR_END_OF_STREAM) {
                LOGI("*** %s reached end of stream", __PRETTY_FUNCTION__);
                LOGI("Demux reached reached end of stream.");

                // Send an EOS message to our receivers so that they know there
                // is no more data coming and can behave appropriately.
                sendEOS_l();

                // One way or the other, we are "completely buffered" at this
                // point since we have hit the end of stream.
                notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
                notifyListener_l(MEDIA_PLAYBACK_COMPLETE);

                // Do not send the playback complete message yet.  Instead, wait
                // until we pass the presentation time of the last sample we
                // queued to report playback complete up to the higher levels of
                // code.
                //
                // It would be very odd to not have a last PTS at this point in
                // time, but if we don't (for whatever reason), just go ahead
                // and send the playback complete right now so we don't end up
                // stuck.
                if (!mLastQueuedMediaTimePTSValid) {
                    LOGW("Sending playback complete (no valid last PTS)");
                    pause_l(false);
                sendEOS_l();
                    notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
                    mEOSResendTimeout.setTimeout(-1);
                } else {
                    // Break out of the loop to reschude ourselves.
                    break;
                }
            } else {
                LOGE("*** %s read failed err=%d", __PRETTY_FUNCTION__, err);
            }
+3 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ class AAH_TXPlayer : public MediaPlayerHWInterface {
    status_t seekTo_l(int64_t timeUs);
    void updateClockTransform_l(bool pause);
    void sendEOS_l();
    void sendFlush_l();
    void sendTSUpdateNop_l();
    void cancelPlayerEvents(bool keepBufferingGoing = false);
    void reset_l();
@@ -143,6 +144,7 @@ class AAH_TXPlayer : public MediaPlayerHWInterface {
    bool mIsSeeking;
    int64_t mSeekTimeUs;

    Timeout mEOSResendTimeout;
    Timeout mPauseTSUpdateResendTimeout;

    sp<TimedEventQueue::Event> mPumpAudioEvent;
@@ -171,6 +173,7 @@ class AAH_TXPlayer : public MediaPlayerHWInterface {
    uint8_t mProgramID;
    uint8_t mTRTPVolume;

    static const int kEOSResendTimeoutMsec;
    static const int kPauseTSUpdateResendTimeoutMsec;

    DISALLOW_EVIL_CONSTRUCTORS(AAH_TXPlayer);