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

Commit 1d8e458f authored by Wei Jia's avatar Wei Jia
Browse files

NuMediaExtractor: support track switch

Also use optimized readMultiple to reduce binder calls.
Test: MediaExtracotr cts tests passed
Bug: 66952371

Change-Id: I669a9613baa73c56c0077a09b0a4caba55a17e95
parent c56f27b2
Loading
Loading
Loading
Loading
+163 −76
Original line number Diff line number Diff line
@@ -40,13 +40,23 @@

namespace android {

NuMediaExtractor::Sample::Sample()
    : mBuffer(NULL),
      mSampleTimeUs(-1ll) {
}

NuMediaExtractor::Sample::Sample(MediaBuffer *buffer, int64_t timeUs)
    : mBuffer(buffer),
      mSampleTimeUs(timeUs) {
}

NuMediaExtractor::NuMediaExtractor()
    : mTotalBitrate(-1ll),
      mDurationUs(-1ll) {
}

NuMediaExtractor::~NuMediaExtractor() {
    releaseTrackSamples();
    releaseAllTrackSamples();

    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
        TrackInfo *info = &mSelectedTracks.editItemAt(i);
@@ -288,7 +298,8 @@ status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const {
    return OK;
}

status_t NuMediaExtractor::selectTrack(size_t index) {
status_t NuMediaExtractor::selectTrack(size_t index,
        int64_t startTimeUs, MediaSource::ReadOptions::SeekMode mode) {
    Mutex::Autolock autoLock(mLock);

    if (mImpl == NULL) {
@@ -311,31 +322,56 @@ status_t NuMediaExtractor::selectTrack(size_t index) {
    sp<IMediaSource> source = mImpl->getTrack(index);

    if (source == nullptr) {
        ALOGE("track %zu is empty", index);
        return ERROR_MALFORMED;
    }

    status_t ret = source->start();
    if (ret != OK) {
        ALOGE("track %zu failed to start", index);
        return ret;
    }

    sp<MetaData> meta = source->getFormat();
    if (meta == NULL) {
        ALOGE("track %zu has no meta data", index);
        return ERROR_MALFORMED;
    }

    const char *mime;
    if (!meta->findCString(kKeyMIMEType, &mime)) {
        ALOGE("track %zu has no mime type in meta data", index);
        return ERROR_MALFORMED;
    }
    ALOGV("selectTrack, track[%zu]: %s", index, mime);

    mSelectedTracks.push();
    TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);

    info->mSource = source;
    info->mTrackIndex = index;
    if (!strncasecmp(mime, "audio/", 6)) {
        info->mTrackType = MEDIA_TRACK_TYPE_AUDIO;
        info->mMaxFetchCount = 64;
    } else if (!strncasecmp(mime, "video/", 6)) {
        info->mTrackType = MEDIA_TRACK_TYPE_VIDEO;
        info->mMaxFetchCount = 8;
    } else {
        info->mTrackType = MEDIA_TRACK_TYPE_UNKNOWN;
        info->mMaxFetchCount = 1;
    }
    info->mFinalResult = OK;
    info->mSample = NULL;
    info->mSampleTimeUs = -1ll;
    releaseTrackSamples(info);
    info->mTrackFlags = 0;

    const char *mime;
    CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));

    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
        info->mTrackFlags |= kIsVorbis;
    }

    if (startTimeUs >= 0) {
        fetchTrackSamples(info, startTimeUs, mode);
    }

    return OK;
}

@@ -366,12 +402,7 @@ status_t NuMediaExtractor::unselectTrack(size_t index) {

    TrackInfo *info = &mSelectedTracks.editItemAt(i);

    if (info->mSample != NULL) {
        info->mSample->release();
        info->mSample = NULL;

        info->mSampleTimeUs = -1ll;
    }
    releaseTrackSamples(info);

    CHECK_EQ((status_t)OK, info->mSource->stop());

@@ -380,79 +411,136 @@ status_t NuMediaExtractor::unselectTrack(size_t index) {
    return OK;
}

void NuMediaExtractor::releaseTrackSamples() {
    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
        TrackInfo *info = &mSelectedTracks.editItemAt(i);
void NuMediaExtractor::releaseOneSample(TrackInfo *info) {
    if (info == NULL || info->mSamples.empty()) {
        return;
    }

    auto it = info->mSamples.begin();
    if (it->mBuffer != NULL) {
        it->mBuffer->release();
    }
    info->mSamples.erase(it);
}

        if (info->mSample != NULL) {
            info->mSample->release();
            info->mSample = NULL;
void NuMediaExtractor::releaseTrackSamples(TrackInfo *info) {
    if (info == NULL) {
        return;
    }

            info->mSampleTimeUs = -1ll;
    auto it = info->mSamples.begin();
    while (it != info->mSamples.end()) {
        if (it->mBuffer != NULL) {
            it->mBuffer->release();
        }
        it = info->mSamples.erase(it);
    }
}

void NuMediaExtractor::releaseAllTrackSamples() {
    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
        releaseTrackSamples(&mSelectedTracks.editItemAt(i));
    }
}

ssize_t NuMediaExtractor::fetchTrackSamples(
ssize_t NuMediaExtractor::fetchAllTrackSamples(
        int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
    TrackInfo *minInfo = NULL;
    ssize_t minIndex = -1;

    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
        TrackInfo *info = &mSelectedTracks.editItemAt(i);
        fetchTrackSamples(info, seekTimeUs, mode);

        if (seekTimeUs >= 0ll) {
            info->mFinalResult = OK;
        if (info->mSamples.empty()) {
            continue;
        }

            if (info->mSample != NULL) {
                info->mSample->release();
                info->mSample = NULL;
                info->mSampleTimeUs = -1ll;
        if (minInfo == NULL) {
            minInfo = info;
            minIndex = i;
        } else {
            auto it = info->mSamples.begin();
            auto itMin = minInfo->mSamples.begin();
            if (it->mSampleTimeUs < itMin->mSampleTimeUs) {
                minInfo = info;
                minIndex = i;
            }
        } else if (info->mFinalResult != OK) {
            continue;
        }
    }

    return minIndex;
}

void NuMediaExtractor::fetchTrackSamples(TrackInfo *info,
        int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
    if (info == NULL) {
        return;
    }

        if (info->mSample == NULL) {
    MediaSource::ReadOptions options;
    if (seekTimeUs >= 0ll) {
        options.setSeekTo(seekTimeUs, mode);
        info->mFinalResult = OK;
        releaseTrackSamples(info);
    } else if (info->mFinalResult != OK || !info->mSamples.empty()) {
        return;
    }
            status_t err = info->mSource->read(&info->mSample, &options);

            if (err != OK) {
                CHECK(info->mSample == NULL);
    status_t err = OK;
    Vector<MediaBuffer *> mediaBuffers;
    if (info->mSource->supportReadMultiple()) {
        options.setNonBlocking();
        err = info->mSource->readMultiple(&mediaBuffers, info->mMaxFetchCount, &options);
    } else {
        MediaBuffer *mbuf = NULL;
        err = info->mSource->read(&mbuf, &options);
        if (err == OK && mbuf != NULL) {
            mediaBuffers.push_back(mbuf);
        }
    }

    info->mFinalResult = err;

                if (info->mFinalResult != ERROR_END_OF_STREAM) {
                    ALOGW("read on track %zu failed with error %d",
                          info->mTrackIndex, err);
    if (err != OK && err != ERROR_END_OF_STREAM) {
        ALOGW("read on track %zu failed with error %d", info->mTrackIndex, err);
        size_t count = mediaBuffers.size();
        for (size_t id = 0; id < count; ++id) {
            MediaBuffer *mbuf = mediaBuffers[id];
            if (mbuf != NULL) {
                mbuf->release();
            }
        }
        return;
    }

                info->mSampleTimeUs = -1ll;
    size_t count = mediaBuffers.size();
    bool releaseRemaining = false;
    for (size_t id = 0; id < count; ++id) {
        int64_t timeUs;
        MediaBuffer *mbuf = mediaBuffers[id];
        if (mbuf == NULL) {
            continue;
            } else {
                CHECK(info->mSample != NULL);
                CHECK(info->mSample->meta_data()->findInt64(
                            kKeyTime, &info->mSampleTimeUs));
        }
        if (releaseRemaining) {
            mbuf->release();
            continue;
        }

        if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
            minInfo = info;
            minIndex = i;
        if (mbuf->meta_data()->findInt64(kKeyTime, &timeUs)) {
            info->mSamples.emplace_back(mbuf, timeUs);
        } else {
            mbuf->meta_data()->dumpToLog();
            info->mFinalResult = ERROR_MALFORMED;
            mbuf->release();
            releaseRemaining = true;
        }
    }

    return minIndex;
}

status_t NuMediaExtractor::seekTo(
        int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
    Mutex::Autolock autoLock(mLock);

    ssize_t minIndex = fetchTrackSamples(timeUs, mode);
    ssize_t minIndex = fetchAllTrackSamples(timeUs, mode);

    if (minIndex < 0) {
        return ERROR_END_OF_STREAM;
@@ -464,7 +552,7 @@ status_t NuMediaExtractor::seekTo(
status_t NuMediaExtractor::advance() {
    Mutex::Autolock autoLock(mLock);

    ssize_t minIndex = fetchTrackSamples();
    ssize_t minIndex = fetchAllTrackSamples();

    if (minIndex < 0) {
        return ERROR_END_OF_STREAM;
@@ -472,28 +560,26 @@ status_t NuMediaExtractor::advance() {

    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);

    info->mSample->release();
    info->mSample = NULL;
    info->mSampleTimeUs = -1ll;
    releaseOneSample(info);

    return OK;
}

status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer) {
status_t NuMediaExtractor::appendVorbisNumPageSamples(MediaBuffer *mbuf, const sp<ABuffer> &buffer) {
    int32_t numPageSamples;
    if (!info->mSample->meta_data()->findInt32(
    if (!mbuf->meta_data()->findInt32(
            kKeyValidSamples, &numPageSamples)) {
        numPageSamples = -1;
    }

    memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
    memcpy((uint8_t *)buffer->data() + mbuf->range_length(),
           &numPageSamples,
           sizeof(numPageSamples));

    uint32_t type;
    const void *data;
    size_t size, size2;
    if (info->mSample->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) {
    if (mbuf->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) {
        // Signal numPageSamples (a plain int32_t) is appended at the end,
        // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes
        if (SIZE_MAX - size < sizeof(int32_t)) {
@@ -511,9 +597,9 @@ status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<
        int32_t zero = 0;
        memcpy(adata, data, size);
        memcpy(adata + size, &zero, sizeof(zero));
        info->mSample->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);
        mbuf->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);

        if (info->mSample->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
        if (mbuf->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
            if (size2 != size) {
                return ERROR_MALFORMED;
            }
@@ -526,7 +612,7 @@ status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<
        // append sizeof(numPageSamples) to plain sizes.
        int32_t int32Size = sizeof(numPageSamples);
        memcpy(adata + size, &int32Size, sizeof(int32Size));
        info->mSample->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
        mbuf->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
    }

    return OK;
@@ -535,7 +621,7 @@ status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<
status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
    Mutex::Autolock autoLock(mLock);

    ssize_t minIndex = fetchTrackSamples();
    ssize_t minIndex = fetchAllTrackSamples();

    if (minIndex < 0) {
        return ERROR_END_OF_STREAM;
@@ -543,7 +629,8 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {

    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);

    size_t sampleSize = info->mSample->range_length();
    auto it = info->mSamples.begin();
    size_t sampleSize = it->mBuffer->range_length();

    if (info->mTrackFlags & kIsVorbis) {
        // Each sample's data is suffixed by the number of page samples
@@ -556,14 +643,14 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
    }

    const uint8_t *src =
        (const uint8_t *)info->mSample->data()
            + info->mSample->range_offset();
        (const uint8_t *)it->mBuffer->data()
            + it->mBuffer->range_offset();

    memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
    memcpy((uint8_t *)buffer->data(), src, it->mBuffer->range_length());

    status_t err = OK;
    if (info->mTrackFlags & kIsVorbis) {
        err = appendVorbisNumPageSamples(info, buffer);
        err = appendVorbisNumPageSamples(it->mBuffer, buffer);
    }

    if (err == OK) {
@@ -576,7 +663,7 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
    Mutex::Autolock autoLock(mLock);

    ssize_t minIndex = fetchTrackSamples();
    ssize_t minIndex = fetchAllTrackSamples();

    if (minIndex < 0) {
        return ERROR_END_OF_STREAM;
@@ -591,14 +678,14 @@ status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
    Mutex::Autolock autoLock(mLock);

    ssize_t minIndex = fetchTrackSamples();
    ssize_t minIndex = fetchAllTrackSamples();

    if (minIndex < 0) {
        return ERROR_END_OF_STREAM;
    }

    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    *sampleTimeUs = info->mSampleTimeUs;
    *sampleTimeUs = info->mSamples.begin()->mSampleTimeUs;

    return OK;
}
@@ -608,14 +695,14 @@ status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {

    *sampleMeta = NULL;

    ssize_t minIndex = fetchTrackSamples();
    ssize_t minIndex = fetchAllTrackSamples();

    if (minIndex < 0) {
        return ERROR_END_OF_STREAM;
    }

    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    *sampleMeta = info->mSample->meta_data();
    *sampleMeta = info->mSamples.begin()->mBuffer->meta_data();

    return OK;
}
+29 −6
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#ifndef NU_MEDIA_EXTRACTOR_H_
#define NU_MEDIA_EXTRACTOR_H_

#include <list>
#include <media/mediaplayer.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/IMediaExtractor.h>
#include <media/MediaSource.h>
@@ -67,7 +69,9 @@ struct NuMediaExtractor : public RefBase {

    status_t getFileFormat(sp<AMessage> *format) const;

    status_t selectTrack(size_t index);
    status_t selectTrack(size_t index, int64_t startTimeUs = -1ll,
            MediaSource::ReadOptions::SeekMode mode =
                MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
    status_t unselectTrack(size_t index);

    status_t seekTo(
@@ -75,8 +79,12 @@ struct NuMediaExtractor : public RefBase {
            MediaSource::ReadOptions::SeekMode mode =
                MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);

    // Each selected track has a read pointer.
    // advance() advances the read pointer with the lowest timestamp.
    status_t advance();
    // readSampleData() reads the sample with the lowest timestamp.
    status_t readSampleData(const sp<ABuffer> &buffer);

    status_t getSampleTrackIndex(size_t *trackIndex);
    status_t getSampleTime(int64_t *sampleTimeUs);
    status_t getSampleMeta(sp<MetaData> *sampleMeta);
@@ -96,12 +104,20 @@ private:
        kMaxTrackCount = 16384,
    };

    struct Sample {
        Sample();
        Sample(MediaBuffer *buffer, int64_t timeUs);
        MediaBuffer *mBuffer;
        int64_t mSampleTimeUs;
    };

    struct TrackInfo {
        sp<IMediaSource> mSource;
        size_t mTrackIndex;
        media_track_type mTrackType;
        size_t mMaxFetchCount;
        status_t mFinalResult;
        MediaBuffer *mSample;
        int64_t mSampleTimeUs;
        std::list<Sample> mSamples;

        uint32_t mTrackFlags;  // bitmask of "TrackFlags"
    };
@@ -117,16 +133,23 @@ private:
    int64_t mTotalBitrate;  // in bits/sec
    int64_t mDurationUs;

    ssize_t fetchTrackSamples(
    ssize_t fetchAllTrackSamples(
            int64_t seekTimeUs = -1ll,
            MediaSource::ReadOptions::SeekMode mode =
                MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
    void fetchTrackSamples(
            TrackInfo *info,
            int64_t seekTimeUs = -1ll,
            MediaSource::ReadOptions::SeekMode mode =
                MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);

    void releaseTrackSamples();
    void releaseOneSample(TrackInfo *info);
    void releaseTrackSamples(TrackInfo *info);
    void releaseAllTrackSamples();

    bool getTotalBitrate(int64_t *bitRate) const;
    status_t updateDurationAndBitrate();
    status_t appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer);
    status_t appendVorbisNumPageSamples(MediaBuffer *mbuf, const sp<ABuffer> &buffer);

    DISALLOW_EVIL_CONSTRUCTORS(NuMediaExtractor);
};