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

Commit 85a9ab32 authored by Amy Zhang's avatar Amy Zhang
Browse files

Implement ES data process in Tuner default impl to support Sample TIS

This CL also provides a new VTS case to test ES input stream.
Please see the test.es https://drive.google.com/file/d/13ZDT9uhEO1LXDT2GcOhB91iIK6m_KEER/view?usp=sharing

ES Format(all the numbers are in decimal):
1. First line is a general meta data to describe the whole file

m:meta data size in bytes, l:ES frame line count X, V:video raw data size
int bytes, A:audio raw data size in bytes, pv:video pid, pa:audio pid

2. The following X lines(equals to the ES frame line count) are the size/pts information
of the video or audio ES frames. Starting with v means video, a means
audio. They are printed in the same order as how they presented in the
original ts.

v, Len:current ES frame size in bytes, PTS: current ES frame PTS

3. After the X lines of ES frame descriptions, there are the video ES raw
data connected with the audio ES raw data.

Test: atest VtsHalTvTunerV1_0TargetTest
Bug: 159027928
Change-Id: I56bd799fd6eda867df54d593235510a5e4758257
parent c152a361
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -294,6 +294,11 @@ void Demux::updateFilterOutput(uint16_t filterId, vector<uint8_t> data) {
    mFilters[filterId]->updateFilterOutput(data);
}

void Demux::updateMediaFilterOutput(uint16_t filterId, vector<uint8_t> data, uint64_t pts) {
    updateFilterOutput(filterId, data);
    mFilters[filterId]->updatePts(pts);
}

uint16_t Demux::getFilterTpid(uint32_t filterId) {
    return mFilters[filterId]->getTpid();
}
@@ -322,6 +327,12 @@ void Demux::frontendInputThreadLoop() {
            ALOGD("[Demux] wait for data ready on the playback FMQ");
            continue;
        }
        if (mDvrPlayback->getSettings().playback().dataFormat == DataFormat::ES) {
            if (!mDvrPlayback->processEsDataOnPlayback(true /*isVirtualFrontend*/, mIsRecording)) {
                ALOGE("[Demux] playback es data failed to be filtered. Ending thread");
                break;
            }
        }
        // Our current implementation filter the data and write it into the filter FMQ immediately
        // after the DATA_READY from the VTS/framework
        if (!mDvrPlayback->readPlaybackFMQ(true /*isVirtualFrontend*/, mIsRecording) ||
+1 −0
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ class Demux : public IDemux {
    bool detachRecordFilter(int filterId);
    Result startFilterHandler(uint32_t filterId);
    void updateFilterOutput(uint16_t filterId, vector<uint8_t> data);
    void updateMediaFilterOutput(uint16_t filterId, vector<uint8_t> data, uint64_t pts);
    uint16_t getFilterTpid(uint32_t filterId);
    void setIsRecording(bool isRecording);
    void startFrontendInputLoop();
+138 −11
Original line number Diff line number Diff line
@@ -129,7 +129,7 @@ Return<Result> Dvr::stop() {

    mDvrThreadRunning = false;

    std::lock_guard<std::mutex> lock(mDvrThreadLock);
    lock_guard<mutex> lock(mDvrThreadLock);

    mIsRecordStarted = false;
    mDemux->setIsRecording(false);
@@ -155,14 +155,13 @@ bool Dvr::createDvrMQ() {
    ALOGV("%s", __FUNCTION__);

    // Create a synchronized FMQ that supports blocking read/write
    std::unique_ptr<DvrMQ> tmpDvrMQ =
            std::unique_ptr<DvrMQ>(new (std::nothrow) DvrMQ(mBufferSize, true));
    unique_ptr<DvrMQ> tmpDvrMQ = unique_ptr<DvrMQ>(new (nothrow) DvrMQ(mBufferSize, true));
    if (!tmpDvrMQ->isValid()) {
        ALOGW("[Dvr] Failed to create FMQ of DVR");
        return false;
    }

    mDvrMQ = std::move(tmpDvrMQ);
    mDvrMQ = move(tmpDvrMQ);

    if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) {
        return false;
@@ -183,7 +182,7 @@ void* Dvr::__threadLoopPlayback(void* user) {

void Dvr::playbackThreadLoop() {
    ALOGD("[Dvr] playback threadLoop start.");
    std::lock_guard<std::mutex> lock(mDvrThreadLock);
    lock_guard<mutex> lock(mDvrThreadLock);
    mDvrThreadRunning = true;

    while (mDvrThreadRunning) {
@@ -195,6 +194,14 @@ void Dvr::playbackThreadLoop() {
            ALOGD("[Dvr] wait for data ready on the playback FMQ");
            continue;
        }

        if (mDvrSettings.playback().dataFormat == DataFormat::ES) {
            if (!processEsDataOnPlayback(false /*isVirtualFrontend*/, false /*isRecording*/)) {
                ALOGE("[Dvr] playback es data failed to be filtered. Ending thread");
                break;
            }
            maySendPlaybackStatusCallback();
        }
        // Our current implementation filter the data and write it into the filter FMQ immediately
        // after the DATA_READY from the VTS/framework
        if (!readPlaybackFMQ(false /*isVirtualFrontend*/, false /*isRecording*/) ||
@@ -211,7 +218,7 @@ void Dvr::playbackThreadLoop() {
}

void Dvr::maySendPlaybackStatusCallback() {
    std::lock_guard<std::mutex> lock(mPlaybackStatusLock);
    lock_guard<mutex> lock(mPlaybackStatusLock);
    int availableToRead = mDvrMQ->availableToRead();
    int availableToWrite = mDvrMQ->availableToWrite();

@@ -263,8 +270,128 @@ bool Dvr::readPlaybackFMQ(bool isVirtualFrontend, bool isRecording) {
    return true;
}

bool Dvr::processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording) {
    // Read ES from the DVR FMQ
    // Note that currently we only provides ES with metaData in a specific format to be parsed.
    // The ES size should be smaller than the Playback FMQ size to avoid reading truncated data.
    int size = mDvrMQ->availableToRead();
    vector<uint8_t> dataOutputBuffer;
    dataOutputBuffer.resize(size);
    if (!mDvrMQ->read(dataOutputBuffer.data(), size)) {
        return false;
    }

    int metaDataSize = size;
    int totalFrames = 0;
    int videoEsDataSize = 0;
    int audioEsDataSize = 0;
    int audioPid = 0;
    int videoPid = 0;

    vector<MediaEsMetaData> esMeta;
    int videoReadPointer = 0;
    int audioReadPointer = 0;
    int frameCount = 0;
    // Get meta data from the es
    for (int i = 0; i < metaDataSize; i++) {
        switch (dataOutputBuffer[i]) {
            case 'm':
                metaDataSize = 0;
                getMetaDataValue(i, dataOutputBuffer.data(), metaDataSize);
                videoReadPointer = metaDataSize;
                continue;
            case 'l':
                getMetaDataValue(i, dataOutputBuffer.data(), totalFrames);
                esMeta.resize(totalFrames);
                continue;
            case 'V':
                getMetaDataValue(i, dataOutputBuffer.data(), videoEsDataSize);
                audioReadPointer = metaDataSize + videoEsDataSize;
                continue;
            case 'A':
                getMetaDataValue(i, dataOutputBuffer.data(), audioEsDataSize);
                continue;
            case 'p':
                if (dataOutputBuffer[++i] == 'a') {
                    getMetaDataValue(i, dataOutputBuffer.data(), audioPid);
                } else if (dataOutputBuffer[i] == 'v') {
                    getMetaDataValue(i, dataOutputBuffer.data(), videoPid);
                }
                continue;
            case 'v':
            case 'a':
                if (dataOutputBuffer[i + 1] != ',') {
                    ALOGE("[Dvr] Invalid format meta data.");
                    return false;
                }
                esMeta[frameCount] = {
                        .isAudio = dataOutputBuffer[i] == 'a' ? true : false,
                };
                i += 5;  // Move to Len
                getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].len);
                if (esMeta[frameCount].isAudio) {
                    esMeta[frameCount].startIndex = audioReadPointer;
                    audioReadPointer += esMeta[frameCount].len;
                } else {
                    esMeta[frameCount].startIndex = videoReadPointer;
                    videoReadPointer += esMeta[frameCount].len;
                }
                i += 4;  // move to PTS
                getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].pts);
                frameCount++;
                continue;
            default:
                continue;
        }
    }

    if (frameCount != totalFrames) {
        ALOGE("[Dvr] Invalid meta data, frameCount=%d, totalFrames reported=%d", frameCount,
              totalFrames);
        return false;
    }

    if (metaDataSize + audioEsDataSize + videoEsDataSize != size) {
        ALOGE("[Dvr] Invalid meta data, metaSize=%d, videoSize=%d, audioSize=%d, totolSize=%d",
              metaDataSize, videoEsDataSize, audioEsDataSize, size);
        return false;
    }

    // Read es raw data from the FMQ per meta data built previously
    vector<uint8_t> frameData;
    map<uint32_t, sp<IFilter>>::iterator it;
    int pid = 0;
    for (int i = 0; i < totalFrames; i++) {
        frameData.resize(esMeta[i].len);
        pid = esMeta[i].isAudio ? audioPid : videoPid;
        memcpy(dataOutputBuffer.data() + esMeta[i].startIndex, frameData.data(), esMeta[i].len);
        // Send to the media filter
        if (isVirtualFrontend && isRecording) {
            // TODO validate record
            mDemux->sendFrontendInputToRecord(frameData);
        } else {
            for (it = mFilters.begin(); it != mFilters.end(); it++) {
                if (pid == mDemux->getFilterTpid(it->first)) {
                    mDemux->updateMediaFilterOutput(it->first, frameData,
                                                    static_cast<uint64_t>(esMeta[i].pts));
                    startFilterDispatcher(isVirtualFrontend, isRecording);
                }
            }
        }
    }

    return true;
}

void Dvr::getMetaDataValue(int& index, uint8_t* dataOutputBuffer, int& value) {
    index += 2;  // Move the pointer across the ":" to the value
    while (dataOutputBuffer[index] != ',' && dataOutputBuffer[index] != '\n') {
        value = ((dataOutputBuffer[index++] - 48) + value * 10);
    }
}

void Dvr::startTpidFilter(vector<uint8_t> data) {
    std::map<uint32_t, sp<IFilter>>::iterator it;
    map<uint32_t, sp<IFilter>>::iterator it;
    for (it = mFilters.begin(); it != mFilters.end(); it++) {
        uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
        if (DEBUG_DVR) {
@@ -285,7 +412,7 @@ bool Dvr::startFilterDispatcher(bool isVirtualFrontend, bool isRecording) {
        }
    }

    std::map<uint32_t, sp<IFilter>>::iterator it;
    map<uint32_t, sp<IFilter>>::iterator it;
    // Handle the output data per filter type
    for (it = mFilters.begin(); it != mFilters.end(); it++) {
        if (mDemux->startFilterHandler(it->first) != Result::SUCCESS) {
@@ -296,8 +423,8 @@ bool Dvr::startFilterDispatcher(bool isVirtualFrontend, bool isRecording) {
    return true;
}

bool Dvr::writeRecordFMQ(const std::vector<uint8_t>& data) {
    std::lock_guard<std::mutex> lock(mWriteLock);
bool Dvr::writeRecordFMQ(const vector<uint8_t>& data) {
    lock_guard<mutex> lock(mWriteLock);
    if (mRecordStatus == RecordStatus::OVERFLOW) {
        ALOGW("[Dvr] stops writing and wait for the client side flushing.");
        return true;
@@ -313,7 +440,7 @@ bool Dvr::writeRecordFMQ(const std::vector<uint8_t>& data) {
}

void Dvr::maySendRecordStatusCallback() {
    std::lock_guard<std::mutex> lock(mRecordStatusLock);
    lock_guard<mutex> lock(mRecordStatusLock);
    int availableToRead = mDvrMQ->availableToRead();
    int availableToWrite = mDvrMQ->availableToWrite();

+10 −0
Original line number Diff line number Diff line
@@ -44,6 +44,13 @@ using ::android::hardware::tv::tuner::V1_0::Result;

using DvrMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;

struct MediaEsMetaData {
    bool isAudio;
    int startIndex;
    int len;
    int pts;
};

class Demux;
class Filter;
class Frontend;
@@ -84,8 +91,10 @@ class Dvr : public IDvr {
    bool addPlaybackFilter(uint32_t filterId, sp<IFilter> filter);
    bool removePlaybackFilter(uint32_t filterId);
    bool readPlaybackFMQ(bool isVirtualFrontend, bool isRecording);
    bool processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording);
    bool startFilterDispatcher(bool isVirtualFrontend, bool isRecording);
    EventFlag* getDvrEventFlag();
    DvrSettings getSettings() { return mDvrSettings; }

  private:
    // Demux service
@@ -98,6 +107,7 @@ class Dvr : public IDvr {

    void deleteEventFlag();
    bool readDataFromMQ();
    void getMetaDataValue(int& index, uint8_t* dataOutputBuffer, int& value);
    void maySendPlaybackStatusCallback();
    void maySendRecordStatusCallback();
    PlaybackStatus checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+59 −40
Original line number Diff line number Diff line
@@ -317,6 +317,11 @@ void Filter::updateFilterOutput(vector<uint8_t> data) {
    mFilterOutput.insert(mFilterOutput.end(), data.begin(), data.end());
}

void Filter::updatePts(uint64_t pts) {
    std::lock_guard<std::mutex> lock(mFilterOutputLock);
    mPts = pts;
}

void Filter::updateRecordOutput(vector<uint8_t> data) {
    std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
    mRecordFilterOutput.insert(mRecordFilterOutput.end(), data.begin(), data.end());
@@ -460,6 +465,11 @@ Result Filter::startMediaFilterHandler() {
    if (mFilterOutput.empty()) {
        return Result::SUCCESS;
    }

    if (mPts) {
        return createMediaFilterEventWithIon(mFilterOutput);
    }

    for (int i = 0; i < mFilterOutput.size(); i += 188) {
        if (mPesSizeLeft == 0) {
            uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
@@ -493,16 +503,25 @@ Result Filter::startMediaFilterHandler() {
            continue;
        }

        int av_fd = createAvIonFd(mPesOutput.size());
        createMediaFilterEventWithIon(mPesOutput);
    }

    mFilterOutput.clear();

    return Result::SUCCESS;
}

Result Filter::createMediaFilterEventWithIon(vector<uint8_t> output) {
    int av_fd = createAvIonFd(output.size());
    if (av_fd == -1) {
        return Result::UNKNOWN_ERROR;
    }
    // copy the filtered data to the buffer
        uint8_t* avBuffer = getIonBuffer(av_fd, mPesOutput.size());
    uint8_t* avBuffer = getIonBuffer(av_fd, output.size());
    if (avBuffer == NULL) {
        return Result::UNKNOWN_ERROR;
    }
        memcpy(avBuffer, mPesOutput.data(), mPesOutput.size() * sizeof(uint8_t));
    memcpy(avBuffer, output.data(), output.size() * sizeof(uint8_t));

    native_handle_t* nativeHandle = createNativeHandle(av_fd);
    if (nativeHandle == NULL) {
@@ -519,24 +538,24 @@ Result Filter::startMediaFilterHandler() {
    DemuxFilterMediaEvent mediaEvent;
    mediaEvent = {
            .avMemory = std::move(handle),
                .dataLength = static_cast<uint32_t>(mPesOutput.size()),
            .dataLength = static_cast<uint32_t>(output.size()),
            .avDataId = dataId,
    };
    if (mPts) {
        mediaEvent.pts = mPts;
        mPts = 0;
    }
    int size = mFilterEvent.events.size();
    mFilterEvent.events.resize(size + 1);
    mFilterEvent.events[size].media(mediaEvent);

    // Clear and log
        mPesOutput.clear();
    output.clear();
    mAvBufferCopyCount = 0;
    ::close(av_fd);
    if (DEBUG_FILTER) {
            ALOGD("[Filter] assembled av data length %d", mediaEvent.dataLength);
        ALOGD("[Filter] av data length %d", mediaEvent.dataLength);
    }
    }

    mFilterOutput.clear();

    return Result::SUCCESS;
}

Loading