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

Commit f80e45a6 authored by Andreas Huber's avatar Andreas Huber
Browse files

Reject streaming .mp4 files over http that are not muxed reasonably.

Audio and video samples must be reasonably close by at any given timestamp,
for now we define "reasonably" as "their offsets must not be more than 1MB apart".

related-to-bug: 3509977
Change-Id: I4c3a2a239bf911306833f3b17b73c9e2f76ab901
parent 6812b2d8
Loading
Loading
Loading
Loading
+105 −7
Original line number Diff line number Diff line
@@ -262,7 +262,7 @@ static const char *FourCC2MIME(uint32_t fourcc) {

MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
    : mDataSource(source),
      mHaveMetadata(false),
      mInitCheck(NO_INIT),
      mHasVideo(false),
      mFirstTrack(NULL),
      mLastTrack(NULL),
@@ -361,8 +361,8 @@ sp<MetaData> MPEG4Extractor::getTrackMetaData(
}

status_t MPEG4Extractor::readMetaData() {
    if (mHaveMetadata) {
        return OK;
    if (mInitCheck != NO_INIT) {
        return mInitCheck;
    }

    off64_t offset = 0;
@@ -370,17 +370,20 @@ status_t MPEG4Extractor::readMetaData() {
    while ((err = parseChunk(&offset, 0)) == OK) {
    }

    if (mHaveMetadata) {
    if (mInitCheck == OK) {
        if (mHasVideo) {
            mFileMetaData->setCString(kKeyMIMEType, "video/mp4");
        } else {
            mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
        }

        return OK;
        mInitCheck = verifyIfStreamable();
    } else {
        mInitCheck = err;
    }

    return err;
    CHECK_NE(err, (status_t)NO_INIT);
    return mInitCheck;
}

void MPEG4Extractor::setDrmFlag(bool flag) {
@@ -755,7 +758,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
                    return err;
                }
            } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
                mHaveMetadata = true;
                mInitCheck = OK;

                if (!mIsDrm) {
                    return UNKNOWN_ERROR;  // Return a dummy error.
@@ -2077,6 +2080,101 @@ status_t MPEG4Source::read(
    }
}

MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
        const char *mimePrefix) {
    for (Track *track = mFirstTrack; track != NULL; track = track->next) {
        const char *mime;
        if (track->meta != NULL
                && track->meta->findCString(kKeyMIMEType, &mime)
                && !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) {
            return track;
        }
    }

    return NULL;
}

status_t MPEG4Extractor::verifyIfStreamable() {
    if (!(mDataSource->flags() & DataSource::kIsCachingDataSource)) {
        return OK;
    }

    Track *audio = findTrackByMimePrefix("audio/");
    Track *video = findTrackByMimePrefix("video/");

    if (audio == NULL || video == NULL) {
        return OK;
    }

    sp<SampleTable> audioSamples = audio->sampleTable;
    sp<SampleTable> videoSamples = video->sampleTable;

    off64_t maxOffsetDiff = 0;
    int64_t maxOffsetTimeUs = -1;

    for (uint32_t i = 0; i < videoSamples->countSamples(); ++i) {
        off64_t videoOffset;
        uint32_t videoTime;
        bool isSync;
        CHECK_EQ((status_t)OK, videoSamples->getMetaDataForSample(
                    i, &videoOffset, NULL, &videoTime, &isSync));

        int64_t videoTimeUs = (int64_t)(videoTime * 1E6 / video->timescale);

        uint32_t reqAudioTime = (videoTimeUs * audio->timescale) / 1000000;
        uint32_t j;
        if (audioSamples->findSampleAtTime(
            reqAudioTime, &j, SampleTable::kFlagClosest) != OK) {
            continue;
        }

        off64_t audioOffset;
        uint32_t audioTime;
        CHECK_EQ((status_t)OK, audioSamples->getMetaDataForSample(
                    j, &audioOffset, NULL, &audioTime));

        int64_t audioTimeUs = (int64_t)(audioTime * 1E6 / audio->timescale);

        off64_t offsetDiff = videoOffset - audioOffset;
        if (offsetDiff < 0) {
            offsetDiff = -offsetDiff;
        }

#if 0
        printf("%s%d/%d videoTime %.2f secs audioTime %.2f secs "
               "videoOffset %lld audioOffset %lld offsetDiff %lld\n",
               isSync ? "*" : " ",
               i,
               j,
               videoTimeUs / 1E6,
               audioTimeUs / 1E6,
               videoOffset,
               audioOffset,
               offsetDiff);
#endif

        if (offsetDiff > maxOffsetDiff) {
            maxOffsetDiff = offsetDiff;
            maxOffsetTimeUs = videoTimeUs;
        }
    }

#if 0
    printf("max offset diff: %lld at video time: %.2f secs\n",
           maxOffsetDiff, maxOffsetTimeUs / 1E6);
#endif

    if (maxOffsetDiff < 1024 * 1024) {
        return OK;
    }

    LOGE("This content is not streamable, "
         "max offset diff: %lld at video time: %.2f secs",
         maxOffsetDiff, maxOffsetTimeUs / 1E6);

    return ERROR_UNSUPPORTED;
}

static bool LegacySniffMPEG4(
        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
    uint8_t header[8];
+5 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ private:
    };

    sp<DataSource> mDataSource;
    bool mHaveMetadata;
    status_t mInitCheck;
    bool mHasVideo;

    Track *mFirstTrack, *mLastTrack;
@@ -90,6 +90,10 @@ private:

    status_t parseTrackHeader(off64_t data_offset, off64_t data_size);

    Track *findTrackByMimePrefix(const char *mimePrefix);

    status_t verifyIfStreamable();

    MPEG4Extractor(const MPEG4Extractor &);
    MPEG4Extractor &operator=(const MPEG4Extractor &);
};